进程之间的通讯之共享内存
2016-09-21 15:14
387 查看
作者:华清远见讲师
一. 简介
刚刚我们了解了我们的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/
>>>更多优秀技术博文每日更新
一. 简介
刚刚我们了解了我们的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/
>>>更多优秀技术博文每日更新
相关文章推荐
- 利用共享内存实现进程之间的通讯
- 进程之间使用共享内存通信....
- windows笔记-使用内存映射文件在进程之间共享数据
- 进程间通讯 —— 共享内存
- 进程间通讯共享内存的具体步骤
- windows笔记-使用内存映射文件在进程之间共享数据
- linux进程通讯-共享内存
- 进程间通讯 —— 共享内存
- [Linux学习]Linux下进程通讯之共享内存
- 两个或多个进程之间通过Win32 API实现内存共享的方法(转)
- 使用内存映射文件在进程之间共享数据
- 进程间通讯-共享内存2(代码)
- Linux进程间通讯之共享内存
- VC中进程与进程之间共享内存
- Linux进程间通讯之共享内存
- linux进程通讯-共享内存
- 在VC++ 6.0下利用共享内存、消息实现内部进程通讯
- linux进程通讯-共享内存1
- VC中进程与进程之间共享内存
- 共享内存进程之间