进程之间的通讯之共享内存

来源:互联网 发布:js require的用法 编辑:程序博客网 时间:2024/06/01 07:25

作者:华清远见讲师

一. 简介

刚刚我们了解了我们的IPC对象,我们知道我们的System V进程间的通信,在系统建立IPC通信的时候,必须指定一个ID值。而该ID的值,我们就可以通过ftok()函数来间接的得到。共享内存就是我们的进程间的一种通信方式。

顾名思义,共享内存就是就是允许两个不相关的进程访问同一个物理内存。可以理解为多个进程共享同一块物理内存。共享内存是进程间共享数据的一种最快的方法,进程可以将相同的物理内存,映射到不同的虚拟地址空间中。所有的进程都可以访问共享内存中的数据。可以理解为C语言的malloc分配了一个空间,定义两个指针变量保存了堆区的空间一样。如果一个进行向共享内存中写入了数据,那么它的举动会影响到可以访问同一段内存的其他进程。如下图所示。


二. 共享内存的实现步骤

我们的共享内存的操作步骤分为以下四步:

<1>创建共享内存

<2>映射共享内存,即把指定的共享内存映射到多个进程的地址空间,方便进程的访问。

<3>撤销共享内存的映射

<4>删除共享内存

1)创建共享内存 ,既然叫做共享内存,顾名思义,肯定是share memory 的缩写。函数如下。

#include

#include

int shmget(key_t key, size_t size, int shmflg);

功能:申请一块指定大小共享内存

参数:

@ key IPC_PRIVATE : 用于亲缘间进程的通信

ftok()函数获得: 用于非亲缘关系的进程

@size 申请共享内存的大小

(注:所有的内存分配时以也4K的倍数为大小进行分配的。即如果一个进程申 请了1块只有1byte的内存,操作系统也会给该内存分配4096bytes。但是真正能够使用的只有1byte。 )

@shmflg 权限标志 (常用如下)。

IPC_CREAT | 0666 如果共享内存不存在,则创建一个共享内存,否则直接打开已存在的,返回其ID。

IPC_CREAT | IPC_EXCL |0666 只有在共享内存不存在的时候,新的共享内存才建立,否则若是存在,shmges调用失败,并设置EEXITST错误码。

返回值:

成功返回共享内存的id号,失败返回-1

查看IPC对象

ipcs –m 显示共享内存段的信息

ipcs -q 显示消息队列段的信息

ipcs -s 显示信号灯集段的信息。

删除IPC对象

ipcrm -m/-q/-s ID

理解方法:可以想象成我们使用open函数打开一个文件的时候,若是使用O_CREAT | O_EXECL,0666,若是文件存在,则显示打开失败。

练习:

自己利用ftok()函数创建一个key值,然后利用shmget()创建共享内存。

如果共享内存存在则报错,不存在则创建。自己利用ipcs命令查看共享内存信息。

2)映射共享内存,把共享内存和进程的地址空间联系起来。(share memeor attach)

手册阅读:

翻译:

<1>函数功能及参数

shmat() 函数映射一个共享内存段,把它和由当前进程调用的由shmid参数指定的地址空间联系起来。

这个指定的地址空间,由shmaddr下列选择指定:

如果 shmaddr 是NULL,操作系统选择一个合适的(未使用的)共享内存段。

如果SHM_RDONLY 被shmflag标志指定,这个被映射的进程的地址空间必须拥有读权限。另一方面,段连接的地址空间若是想要读写的话,必须要有读和写的权限。没有一个只写概念的共享内存段。

<2>返回值

Shamt()函数成功返回共享内存映射的地址空间,失败返回(void *)-1并设置error

<3>映射共享内存,把共享内存和进程的地址空间联系起来。

void * shmat(int shmid, const void *shmaddr, int shmflg);

功能:把shmid创建共享内存块附加到进程的私有地址。[进程的虚拟地址空间]

参数:

@ shmid 共享内存段的标识 [由shmget()函数得到]

@ shmaddr[将共享内存映射到指定的地址空间]

NULL 让系统自动完成映射

@ shmflg[映射的标志] 0 映射可以读写;

SHM_RDONLY 映射后只能读

返回值:成功返回映射后的进程的地址空间

失败返回(void *)-1,并且置errno

注意:进程结束之后,共享内存的映射自动撤销。

3)撤销共享内存。

int shmdt (const void * shmaddr);

功能:撤销共享内存到进程地址空间的映射

参数:

@smaddr 共享内存映射到进程指定的地址空间

返回值:

成功返回 0

失败返回 -1, 并且置errno

注:给shmdt传递的地址必须是shmat()函数获得的。

4)删除共享内存。[shmat control]

int shmctl(int shmid, int cmd,struct shmid_ds *buf);

功能:对共享内存进行控制

参数:

@shmid 共享内存段的标识 [由shmget()函数得到]

@cmd 共享内存的控制命令

IPC_RMID 删除共享内存。

@buf shmid 的一些信息。

NULL 表示不需要使用它。

返回值:

成功返回0,失败返回-1

//结构体简介 [了解即可]

struct shmid_ds

{

struct ipc_perm shm_perm;/* 操作权限*/

int shm_segsz; /*段的大小(以字节为单位)*/

time_t shm_atime; /*最后一个进程附加到该段的时间*/

time_t shm_dtime; /*最后一个进程离开该段的时间*/

time_t shm_ctime; /*最后一个进程修改该段的时间*/

unsigned short shm_cpid; /*创建该段进程的pid*/

unsigned short shm_lpid; /*在该段上操作的最后1个进程的pid*/

short shm_nattch; /*当前附加到该段的进程的个数*/

/*下面是私有的*/

unsigned short shm_npages; /*段的大小(以页为单位)*/

unsigned long *shm_pages; /*指向frames->SHMMAX的指针数组*/

struct vm_area_struct *attaches; /*对共享段的描述*/

};

IPC_RMID 破化共享内存段。

代码演示:





运行结果:

练习:利用共享内存实现两个进程间的shm_read.C和shm_write.C之间的通信。

[注意:两个进程只要打开的是同一个文件路径,则获得相同的共享内存。]

思路:

shm_read.c

//接收写进程的信号

Signal(SIGUSR1,signal_handler)

{

}

//创建共享内存

Shget

//内存映射

Shmat

通过getpid()得到自己的pid号写到共享内存中,让write进程获取。

*(int *)paddr = getpid();

//循环把数据写到共享内存中去

While(1)

{

Pause();//修改等待写进程书写完毕,接收信号。

Read();

}

//撤销进程

//删除共享内存

shm_write.c

//创建共享内存

Shget

//内存映射

Shmat

//获得read进程的pid号,以便于向read进程发送信号

Pid = *(int *)paddr;

While(1)

{

putchar(‘>’);

fgets();

//书写完毕发送信号。

kill(pid,SIGUSR1);

}

//解除映射

//删除共享内存

注意:由于write进程要获得read进程的pid号,故要求read进程先运行,write进程后运行。

文章源自华清远见嵌入式学院:http://www.embedu.org/

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

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 散粉盖子扭不开怎么办 玻璃瓶饮料盖子打不开怎么办 玻璃瓶玻璃盖子打不开怎么办 美甲没有胶水怎么办 按压式瓶盖打不开怎么办 睫毛胶水瓶盖打不开怎么办 玻璃杯盖子滑丝怎么办 瓶盖滑扣了怎么办 胶水瓶口被塞住怎么办 美林盖子打不开怎么办 美林瓶盖打不开怎么办 泰诺瓶盖打不开怎么办 玻璃罐头瓶盖打不开怎么办 塑料罐头瓶盖打不开怎么办 喷笔壶盖打不开怎么办 陶瓷壶盖卡住了怎么办 贝德玛瓶盖摔坏怎么办 塑料盖子错位拧不开怎么办 安全瓶盖坏了怎么办 刚买面霜打不开怎么办 可乐瓶盖鼓起来怎么办 暖壶塞子吸住了怎么办 茶兀瓶盖打不开怎么办 水杯盖太紧了拧不开怎么办 矿泉水瓶盖拧不开了怎么办 弩弦用手拉不上怎么办 茅台酒瓶口漏酒怎么办 化妆品盖子丢了怎么办 化妆品盖子碎了怎么办 自制水泵压力小怎么办 大学数学不会做怎么办 下雪了怎么办教案幼儿园小班 下水道被混凝土堵塞怎么办 日本足贴丢了胶布怎么办 牙齿被可乐腐蚀怎么办 三十岁满嘴无牙怎么办 水乳盖子打不开怎么办 蜂蜜罐子打不开了怎么办 蜂蜜盖子第二次拧不开怎么办 玻璃杯子拧不开盖子怎么办 玻璃杯水杯盖子拧不开怎么办