共享内存之——system V共享内存

System V 的IPC对象有共享内存、消息队列、信号灯(量)。

注意:在IPC的通信模式下,不管是共享内存、消息队列还是信号灯,每个IPC的对象都有唯一的名字,称为"键(key)"。通过"键",进程能够识别所用的对象。"键"与IPC对象的关系就如同文件名称于文件,通过文件名,进程能够读写文件内的数据,甚至多个进程能够公用一个文件。而在IPC的通信模式下,通过"键"的使用也能使得一个IPC对象能为多个进程所共用。


一、System V共享内存机制:

system V IPC 机制下的共享内存本质是一段特殊的内存区域,进程间需要共享的数据被放在该共享内存区域中,所有需要访问该共享区域的进程都要把该共享区域映射到本进程的地址空间中去。共享内存允许一个或多个进程通过同时出现在他们的虚拟地址空间的内存进行通信,而这块虚拟内存的页面被每个共享进程的页表条目所引用,同时并不需要再所有进程的虚拟内存都有相同的地址。

1.System V共享内存是一种最为搞笑的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝。

2.为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间。进程就可以直接读写这一块内存而不要进行数据的拷贝,从而大大提高效率。

3.由于多个进程共享一段内存,因此也需要依靠某种同步机制。

 二、共享内存的操作流程

1. 创建/打开共享内存

2.映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问

3.撤销共享内存的映射

4.删除共享内存对象

三、相关API

step.1----------------->获取一块共享内存

#include<sys/ipc.h>
#include<sys/shm.h> int shmget(key_t key, size_t size, int shmflg)

返回值:调用成功返回一个shmid(类似打开一个或创建一个文件获得的文件描述符一样);

   调用失败返回-1;

参数说明:

(1)key:

这两种方式分配的共享内存,一般用来亲缘关系的进程间通信:

当key取值为IPC_PRIVATE,则函数shmget()将创建一块新的共享内存;

当key取值为0,而参数shmflg中设置了IPC_CREATE这个标志,则同样创建一块新的共享内存;

我们一般是通过ftok函数获取键值key

#include<sys/types.h>
#include<sys/ipc.h> key_t ftok(const char * pathname, int proj_id) /*pathname 就是指定的文件名(该文件必须是存在而且可以访问的)
id是标示符,和pathname一起完成创建键值的参数,虽然为int,但只有8个比特被使用,一般我们写一个字符代替*/

例如:

 #include<sys/types.h>
#include<sys/ipc.h>
#include<stdio.h>
#include<stdlib.h> int main()
{
key_t key1,key2;
if((key1 = ftok(".",'a')) < )
{
perror("fail to ftok");
exit(EXIT_FAILURE);
}
if((key2 = ftok(".",'b')) < )
{
perror("fail to ftok");
exit(EXIT_FAILURE);
}
printf("key1 = %d\n",key1);
printf("key2 = %d\n",key2); return ;
}

(2)size:

是要建立共享内存的长度。所有的内存分配操作都是以页为单位的。所以如果一个进程只申请一块只有体格字节的内存,内存也会分配蒸蒸一页(在i386机器中一页的缺省大小PACE_SIZE = 4096B)

(3)shmflg:

指定创建或打开的标志和读写的权限(ipc_perm中的mode成员)。

有效的标志包括IPC_CREAT和IPC_EXCL,他们的功能与opene的O_CREAT和O_EXCL相当。

IPC_CREAT   如果共享内存不存在,则创建一个共享内存,否则直接打开已存在的。

IPC_EXCL     只有在共享内存不存在的时候,新的内存才建立,否则就产生错误。

/*假设键值为key,创建一个共享内存大小为4k,访问权限为0666
如果已经存在则返回其标示符*/
int shmid;
if((shmid = shmget(key, 4*1024, 0666 | IPC_CRREAT)) < 0)
{
perror("Fail to shmget");
exit(EXIT_FAILURE);
}
/*假设键值为key,创建一个共享内存大小为1k,访问权限为0666
如果已经存在则报错*/
int shmid;
if((shmid = shmget(key, , | IPC_CRREAT|IPC_EXCL)) < )
{
perror("Fail to shmget");
exit(EXIT_FAILURE);
}

step 2--------------------------->共享内存的映射

函数shmat将标示符为shmid共享内存映射到调用进程的地址空间中。

#include<sys/types.h>
#include<sys/shm.h> void * shmat(int shmid, const void *shmaddr, int shmflg);

参数说明:

shmid:要映射的共享内存区标示符

shmaddr:将共享内存映射到指定地址(若为NULL,则表示由系统自动完成映射)

shmflg:SHM_RDONLY 共享内存只读

默认0:共享内存可读写

返回值:调用成功返回映射后的地址,出错返回(void *)-1。

step3------------------------------>撤销共享内存与用户进程之间的映射

int shmdt(const void * shmadr);

参数shmaddr 是 shmat映射成功返回的地址。

注意:当一个进程不再需要共享内存段时,它将调用shmdt()系统调用取消这个段,但是这并不是从内核真正地删除这个段,而是把相关shmid_ds结构的shm_nattch域的值减1,当这个值为0时,内核才从物理上删除这个共享段。

step4-------------------------------->控制共享内存(&删除)

#inlcude<sys/ipc.h>
#include<sys/shm.h> int shmctl(int shmid, int cmd, struct shmid_ds *buf);

参数说明:

shmid 共享内存标示符ID

cmd  IPC_STAT得到共享内存的状态

   IPC_SET改变共享内存的状态

     IPC_RMID删除共享内存

buf   是一个结构体指针。IPC_STAT的时候,取得的状态放在这个结构体中。如果要改变共享内存的状态,用这个结构体 struct shmid_ds 指定:

共享内存之——system V共享内存

注意:

1.IPC_RMID命令实际上不从内核删除一个段,而是仅仅把这个段标记为删除,实际的删除发生最后一个进程离开这个共享段时。

2.当cmd为IPC_RMID时,第三个参数应为NULL。用这个函数“删除”共享内存

3.如果在代码中没有手动删除,共享内存并不会随着程序的终止而自动清理!

上一篇:Repositories.EntityFramework 实现方式


下一篇:Codeforces Round #Pi (Div. 2) E. President and Roads 最短路+桥