进程间通信方式——共享内存

来源:互联网 发布:黑莓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、缺点:共享内存没有提供同步的机制,这使得我们在使用共享内存进行进程间通信时,往往要借助其他的手段来进行进程间的同步工作。

文章选自华清远见嵌入式培训

>>>更多优秀技术博文每日更新

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 wps文档加密后忘记密码怎么办 wps表格文件密码忘记了怎么办 wps表格密码忘记了怎么办 发布的文案缩成一行怎么办 创维电视都是VIP怎么办 挂电视距离太远怎么办 电视离沙发太近怎么办 苹果6更新11.1卡怎么办 苹果升级变卡了怎么办 迅雷被资源举报无法加速怎么办 迅雷的敏感资源无法加速怎么办 迅雷下载敏感资源无法加速怎么办 换手机微信账户怎么办 拉链拉不合没有蜡怎么办 长杆烟斗头松动怎么办? 大王卡没流量了怎么办 晚上电视想看野外生存怎么办 假离婚后真离婚怎么办 苹果手机在车上不能充电怎么办 遥控钥匙打不开车门怎么办 苹果访问限制密码忘记了怎么办 苹果手机忘记访问限制密码怎么办 苹果忘记访问限制密码怎么办 苹果限制访问密码忘了怎么办 东奥会计书盗版怎么办 合同封印少盖一页怎么办 玩单机游戏屏幕输入不支持怎么办 汽车不小心陷进泥潭怎么办 招商银行信用卡激活电话换了怎么办 冲鼻子是耳朵进水了怎么办 不小心点了赞怎么办 小车间太热了怎么办 在车间上班好热怎么办 英雄杀四星李逵不拉仇恨怎么办 手表玻璃里面有雾水怎么办 dw手表玻璃碎了怎么办 手表玻璃面花了怎么办 有个窝囊的父母怎么办 苹果7p玩游戏卡怎么办 三星玩游戏很卡怎么办 三星打游戏很卡怎么办