进程间通信方式——共享内存
来源:互联网 发布:黑莓q5软件下载 编辑:程序博客网 时间:2024/06/08 12:55
作者:华清远见
一、进程间通信的方式
在程序的运行中,我们必定会有关于进程与进程间通信的问题。而我们的先辈早已为我们准备了关于解决这些问题的方法。这些方法主要有几种:无名管道,有名管道,信号,消息队列,共享内存以及信号灯(信号量)等几种方法。其中共享内存又是其中的翘楚。这篇文章讲解一下共享内存。
二、共享内存介绍
1、共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝。
2、为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间。进程就可以直接读写这一块内存而不需要进行数据的拷贝,从而大大提高效率。
3、由于多个进程共享一段内存,因此也需要依靠某种同步机制(信号灯)。
三、共享内存的同步
共享内存为在多个进程之间共享和传递数据提供了一种有效的方式。但是它并未提供同步机制,所以我们通常需要用其他的机制来同步对共享内存的访问。我们通常是用共享内存来提供对大块内存区域的有效访问,同时通过传递小消息来同步对该内存的访问。在第一个进程结束对共享内存的写操作之前,并无自动的机制可以阻止第二个进程开始对它进行读取。对共享内存访问的同步控制必须由程序员来负责。
四、共享内存的实现
共享内存的使用包括如下步骤:
1、创建/打开共享内存
2、映射共享内存,即把指定的共享内存映射到进程的地址空间用 于访问。
3、撤销共享内存的映射。
4、删除共享内存对象
五、共享内存使用的函数
#include
int shmget(key_t key, size_t size, int shmflg);
void *shmat(int shm_id, const void *shm_addr, int shmflg);
int shmdt(const void *shm_addr);
int shmctl(int shm_id, int cmd, struct shmid_ds *buf);
1. shmget函数
int shmget(key_t key, size_t size, int shmflg);
该函数用来创建共享内存:
参数:key : 和信号量一样,程序需要提供一个参数key,它有效地为共享内存段命名。有一个特殊的键值IPC_PRIVATE,它用于创建一个只属于创建进程的共享内存,通常不会用到。
size: 以字节为单位指定需要共享的内存容量。
shmflag: 包含9个比特的权限标志,它们的作用与创建文件时使用的mode标志是一样由IPC_CREAT定义的一个特殊比特必须和权限标志按位或才能创建一个新的共享内存段。
NOTE:权限标志对共享内存非常有用,因为它允许一个进程创建的共享内存可以被共享内存的创建者所拥有的进程写入,同时其它用户创建的进程只能读取共享内存。我们可以利用这个功能来提供一种有效的对数据进行只读访问的方法,通过将数据放共享内存并设置它的权限,就可以避免数据被其他用户修改。
返回值:创建成功,则返回一个非负整数,即共享内存标识;如果失败,则返回-1。
2. shmat函数
第一次创建共享内存段时,它不能被任何进程访问。
要想启动对该内存的访问,
必须将其连接到一个进程的地址空间。
这个工作由shmat函数完成:
void *shmat(int shm_id, const void *shm_addr, int shmflg);
参数:shm_id : 由shmget返回的共享内存标识。shm_add: 指定共享内存连接到当前进程中的地址位置。它通常是一个空指针,表示让系统来选择共享内存出现的地址。
shmflg : 是一组标志。它的两个可能取值是:
SHM_RND, 和shm_add联合使用,用来控制共享内存连接的地址。
SHM_RDONLY, 它使连接的内存只读。
返回值:如果调用成功, 返回一个指向共享内存第一个字节的指针;
如果失败,返回-1。
共享内存的读写权限由它的属主(共享内存的创建者),它的访问权限和当前进程的属主决定。共享内存的访问权限类似于文件的访问权限。
3. shmdt
将共享内存从当前进程中分离。
int shmdt(const void *shm_addr);
shm_addr: shmat返回的地址指针。
成功时,返回0,
失败时,返回-1.
NOTE:共享内存分离并未删除它,只是使得该共享内存对当前进程不再可用。
注意:当一个进程不再需要共享内存段时,它将调用shmdt()系统调用取消这个段,但是,这并不是从内核真正地删除这个段,而是把相关shmid_ds结构的shm_nattch域的值减1,当这个值为0时,内核才从物理上删除这个共享段。(每有一个进程映射到这段共享内存,这个值就会加1,每次调用shmctl删除,仅仅只是删掉这个标记)
用完共享存储段后,将进程和该共享存储段脱离。这并不是从系统中删除其标识符以及其数据结构,直到某个进程(一般是创建者server)调用shmctl(IPC_RMID)特地删除它。
4. shmctl
共享内存的控制函数
int shmctl(int shm_id, int cmd, struct shmid_ds *buf);
shmid_ds结构至少包含以下成员:
struct shmid_ds {
uid_t shm_perm.uid;
uid_t shm_perm.gid;
mode_t shm_perm.mode;
}
参数:
shm_id : 是shmget返回的共享内存标识符。
command: 是要采取的动作,
它可以取3个值:
IPC_STAT 把shmid_ds结构中的数据设置为共享内存的当前关联值。
IPC_SET 如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值。
IPC_RMID 删除共享内存段。
buf : 是一个指针,包含共享内存模式和访问权限的结构。
返回值:
成功时,返回0,
失败时,返回-1。
六、代码示例
说了这么多,又到了实战的时候了。下面我们就用代码来进一步了解。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define BUFF_SIZE 1024
typedef struct {
sem_t rsem; /* sem for read */
sem_t wsem; /* sem for write */
char buf[BUFF_SIZE];
}SHM;
int do_read (SHM *pSHM);
int do_write(SHM *pSHM);
int main(int argc, char *argv[])
{
int shmid = -1;
int pid = -1;
SHM *pSHM;
int retval = 0;
/* create share memory
* IPC_PRIVATE for communication betweeen parent-child processes
* IPC_CREAT to indicate a new creation of IPC object
*/
if ((shmid = shmget(IPC_PRIVATE, sizeof(SHM), 0666)) < 0) {
perror("shmget error");
return -1;
}
printf("pid[%d] create new shmid = %d\n", getpid(), shmid);
/* attach share memory */
if ((pSHM = (SHM *)shmat(shmid, NULL, 0)) == (void *)-1) {
perror("shmat error");
retval = -1;
goto _exit_point;
}
printf("parent: shmaddr = %p\n", pSHM);
/* set 3rd param of sem_init - pshared - as nonzero,
* then the semaphore is shared between processes
*/
if (sem_init(&(pSHM->rsem), 1, 0) < 0) { /* read sem is init as busy */
perror("sem_init read error");
retval = -1;
goto _exit_point;
}
if (sem_init(&(pSHM->wsem), 1, 1) < 0) { /* write sem is init as free */
perror("sem_init write error");
retval = -1;
goto _exit_point;
}
if ((pid = fork()) < 0) {
perror("fork error");
retval = -1;
goto _exit_point;
}else if (pid == 0) { /* child */
/* After a fork the child inherits the attached
* shared memory segments.
*/
retval = do_read(pSHM);
}else{ /* parent */
usleep(5000); /* just to make output looks better */
retval = do_write(pSHM);
/* detach the share memory */
if (shmdt(pSHM) < 0) {
perror("shmdt error");
retval = -1;
goto _exit_point;
}
/* wait child process */
wait(NULL);
}
_exit_point:
if (pid != 0) { /* child will not handle this */
if (shmid > 0) {
/* IPC_RMID just mark the share memory presented by
* the shmid should be deleted, but the deletion
* happens only when no proces is attached
*/
if (shmctl(shmid, IPC_RMID, NULL) < 0) {
perror("shmctl error");
}
}
}
return retval;
}
int do_write(SHM *pSHM)
{
while (1) {
/* try to lock for write */
if (sem_wait(&(pSHM->wsem)) < 0) {
perror("sem_wait error");
break;
}
printf(">");
/* access the share memory and write data which are got
* from stdin
*/
fgets(pSHM->buf, BUFF_SIZE, stdin);
/* eat the LF and complete the string */
pSHM->buf[strlen(pSHM->buf) - 1] = '\0';
/* allow read */
if (sem_post(&(pSHM->rsem)) < 0) {
perror("sem_post error");
break;
}
if (strncmp(pSHM->buf, "quit", 4) == 0) {
break;
}
usleep(500);
}
return 0;
}
int do_read(SHM *pSHM)
{
while(1) {
/* try to lock for read */
if (sem_wait(&(pSHM->rsem)) < 0) {
perror("sem_wait error");
break;
}
/* read the share memory buffer and print the content out */
printf("read buf: %s\n", pSHM->buf);
/* test the buf inside read lock */
if (strncmp(pSHM->buf, "quit", 4) == 0) {
/* detach the share memory */
if(shmdt(pSHM) < 0) {
perror("shmdt error");
return -1;
}
break;
}
/* allow write */
if (sem_post(&(pSHM->wsem)) < 0) {
perror("sem_post error");
break;
}
}
return 0;
}
大家可以看到,在这个程序中加入了信号灯的代码。主要是因为两个进程在调用同一个内存空间所以需要加入信号灯以达到同步或互斥的目的。
七、共享内存的优缺点
1、优点:我们可以看到使用共享内存进行进程间的通信真的是非常方便,而且函数的接口也简单,数据的共享还使进程间的数据不用传送,而是直接访问内存,也加快了程序的效率。同时,它也不像匿名管道那样要求通信的进程有一定的父子关系。
2、缺点:共享内存没有提供同步的机制,这使得我们在使用共享内存进行进程间通信时,往往要借助其他的手段来进行进程间的同步工作。
文章选自华清远见嵌入式培训
>>>更多优秀技术博文每日更新
- 进程间通信方式——共享内存
- 进程间通信方式总结——共享内存
- 进程间通信方式——共享内存
- linux进程间通信方式 -- 共享内存
- 进程间通信 共享内存shmget方式
- 进程间通信——共享内存
- 进程间通信——共享内存
- 共享内存——进程间通信
- 进程间通信——共享内存
- 进程间通信——共享内存
- 进程间通信——共享内存
- 进程间通信——共享内存
- 进程间通信——共享内存
- 进程间通信——共享内存
- 进程间通信——共享内存
- 进程间通信——共享内存
- 进程间通信——共享内存
- 进程间通信——共享内存
- Oracle--表中插入数据优化
- Metron学习1_安全大数据分析框架 OpenSOC
- [Ubuntu] 安装 Nvidia 双显卡驱动
- 无中生有之突击NOIP(8)——更多算法(一)
- Code First Migrations更新数据库结构的具体步骤
- 进程间通信方式——共享内存
- 暨南大学Oracle OCM培训正在火热招生
- C#委托Delegate
- Android 内存泄露分析--MAT分析hprof文件
- 关于rxjava与retrofit结合详情
- asp.net母版页中实现点击菜单切换对应的样式
- linux上安装最新git
- 自定义splash导航器,选中的点变大
- OpenVPN for Android