Chinaunix首页 | 论坛 | 博客
  • 博客访问: 6170
  • 博文数量: 3
  • 博客积分: 87
  • 博客等级: 民兵
  • 技术积分: 40
  • 用 户 组: 普通用户
  • 注册时间: 2011-08-28 20:44
文章分类
文章存档

2012年(3)

我的朋友
最近访客

分类: LINUX

2012-06-03 10:11:58

    可以有两种方式来实现进程间的共享内存,一种是通过XSI共享存储段,另一种是mmap方式。其中mmap需要将存储段与文件关联,而XSI共享存储段则无需这种关联。


  • 共享存储

    共享存储允许多个进程共享一个给定的存储区,由于数据不需要在多个进程之间进行复制,因此是最快的一种IPC。由于多个进程可以同时访问,因此需要多进程对存储区的访问进行同步。可以使用信号量和记录锁的方式对访问进行同步。

在使用共享存储之前首先通过shmget函数要获得共享存储标识符:

点击(此处)折叠或打开

  1. #incldue<sys/shm.h>
  2. int shmget(key_t key, size_t size, int flag);

其中key是存储段标识符,size是存储段的字节数,通常会向上取整为页长的整数倍(扩展的部分不可以使用)。如果是创建一个新段,size参数必须指定,如果引用现存的段,size参数应指定为0。其返回值为共享存储的ID,在时候的操作中我们都需要使用这个ID来进行。

    一个新段创建完成后,段内的内容会被初始化为0。此时可以调用shmat将其连接到进程的地址空间。

点击(此处)折叠或打开

  1. void *shmat(int shmid, const void *addr, int flag);

参数shmid是刚才提到的共享存储的ID。通常情况下,将addr设置为0以便由内核来选择映射的地址,该函数的返回值便是该段所连接的实际地址。

    该函数返回-1表示出错,因此要判断其返回值来确定是否连接成功。

点击(此处)折叠或打开

  1. void *p = shmat( shmid, 0, 0 );
  2. if( (int)p == -1 )
  3. {
  4.     //处理出错情况
  5. }

当我们不在需要使用共享存储的时候,我们需要使用shmdt来脱接该段。

点击(此处)折叠或打开

  1. int shmdt(void *addr)

参数addr是调用shmat的返回值。

    由于共享存储是多个进程所共享的,因此需要维护一个引用计数来表示当前一共有多少个进程连接到该共享存储,类似于引用计数。这个字段便是shmid_ds结构中的shm_nattch字段。当shmat成功执行时,nattch的数值加1。当shmdt成功执行的时候,nattach字段减1. 注意shmdt并不删除存储段及其数据结构,若要删除,需要调用shmctl函数。

点击(此处)折叠或打开

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

若要从系统中删除某共享存储段,需指定cmd参数为IPC_RMID。只有当所有进程与该共享存储脱接的时候,才会删除该存储段。不管这个段是否被使用,该段的标识符都会立即被删除,因此不能用shmat与该段进行连接。


  • 存储映射IO

存储映射IO使一个磁盘文件与存储空间中的一个缓冲区相映射,从缓冲区中读取数据,就相当与从文件的相应位置中读取数据。向缓冲区中写入数据,则相应的数据会被自动写入文件。这样的读取和写入不需要使用readwrite函数。

存储映射是通过mmap函数实现的

点击(此处)折叠或打开

  1. void *mmap(void addr, size_t len, int prot, int flag, int filedes, off_t off);

该函数的返回值为实际映射的起始内存地址。

addr参数是指定映射存储区的起始地址,通常情况下为0以便可以使内核来选择合适的地址。

filedes是将要被映射的文件,需要在调用该函数之前将文件打开

prot为存储区的访问权限,可以为PROT_READPROT_WRITEPROT_EXECPROT_NONE。但该权限不能高于filedes所对应的文件的访问权限。

flag参数的可选取值有以下几种:

MAP_FIXED:映射地址必须为addr,不建议使用

MAP_SHARED:存储操作相当与对文件调用write

MAP_PRIVATE:对映射区的操作将会创建映射区的一个私有副本,所有操作在该副本上完成,因此不会改变原文件的内容。

MAP_SHAREDMAP_PRIVATE必须指定一个,但两者不能同时使用。

    若需要改变权限,可以通过函数mprotect进行。

点击(此处)折叠或打开

  1. int mprotect(void *addr, size_t len, int prot);

    若我们对映射区进行了修改,可以使用msync函数进行同步,将映射区中的内容写回到文件中。

点击(此处)折叠或打开

  1. int msync(void *addr, size_t len, int flag);

flag参数可以有如下的取值:

MS_ASYNC:异步写

MS_SYNC:写操作完成后函数才会返回。

以上两个选项必须指定一个。注意,如果映射为MAP_PRIVATE,则不能修改映射文件。

MS_INCALIDATE:丢弃没有同步的页。

解除映射可以调用munmap函数

点击(此处)折叠或打开

  1. int munmap(caddr_t addr, size_t len);

    内存映射IO若使用不当,可能出现SIGSEGVSIGBUS信号。

SIGSEGV:进程试图访问对它不可用的内存区。例如将数据写入只读存储区。

SIGBUS:访问不存在的存储区。例如某个进程打开文件存储映射,另一个进程将文件截短,当试图访问被截掉的部分时会产生此信号。

另外,我们不能将数据添加到文件中以增加文件的长度。

    从上面可以看出,当使用存储映射的时候,我们必须指定文件名以及它的长度。若用这种方法,每次要进行进程间数据共享就必须先创建相应的文件。为了方便,我们可以将将内存映射到匿名文件上来实现多个进程间的数据共享。这样就省略了为指定映射创建相应文件的步骤。在调用mmap函数的时候,只需要设置MAP_ANONYMOUS并且指定文件描述符为-1即可.

点击(此处)折叠或打开

  1. if((area = mmap(0, size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0)) == MAP_FAILED)
  2. {
  3.          //错误处理
  4. }

阅读(637) | 评论(0) | 转发(0) |
0

上一篇:没有了

下一篇:动态共享对象(DSO, Dynamic Shared Object)

给主人留下些什么吧!~~