Linux IPC之POSIX共享内存

来源:互联网 发布:30岁程序员失业怎么办 编辑:程序博客网 时间:2024/05/02 23:21

导言:System V共享内存和共享文件映射,允许无关进程共享内存区域以便执行IPC通信。但这两种技术都存在一些不足:1. System V共享内存模型使用的是键和标识符,这与标准的UNIX I/O模型使用文件名和描述符的做法是不一致的,这种差异意味着使用System V共享内存段需要一整套全新的系统调用和命令。2. 使用一个共享文件映射来进行IPC要求创建一个磁盘文件,即使无需对共享区域进行持久存储也需要这样做,除了因需要创建文件所带来的不便之外,这种技术还会带来一些文件I/O开销。由于存在这些不足,所以POSIX.1b定义了一组新的共享内存API —— POSIX共享内存。

概述

POSIX共享内存能够让无关进程共享一个映射区域,而无需创建一个相应的文件映射。Linux从内核2.4起开始支持POSIX共享内存。SUSv3并没有对POSIX共享内存的实现细节进行规定,特别是没有要求使用一个(真实或虚拟)文件系统来标识共享内存对象,但是很多UNIX实现都采用了文件系统来标识共享内存对象。一些UNIX实现将共享对象名创建为标准文件系统上一个特殊位置处的文件,Linux使用挂载于/dev/shm目录下的专用tmpfs文件系统,这个文件系统具有内核持久性,即它所包含的共享内存对象会一直持久,即使当前不存在任何进程打开它,但这些对象会在系统关闭之后丢失。

注意:系统上POSIX共享内存区域占据的内存总量受限于底层的tmpfs文件系统的大小,这个文件系统通常会在启动时使用默认大小(如,256MB)进行挂载,如果有必要的话,root用户能够使用命令mount -o remount,size=<num-bytes>重新挂载这个文件系统来修改它的大小。

要使用POSIX共享内存对象,需要完成下列工作:

  1. 使用shm_open()函数打开一个与指定的名字对应的对象,shm_open()函数与open()系统调用类似,它会创建一个新共享对象,或打开一个既有对象。shm_open()会返回一个引用该对象的文件描述符。
  2. 将上一步中获得的文件描述符传入mmap()调用,并在其flags参数中指定MAP_SHARED,这会将共享内存对象映射到进程的虚拟地址空间。与mmap()的其他用法一样,一旦映射了对象之后就能够关闭该文件描述符,而不会影响到这个映射。然后,有可能需要将这个文件描述符保持在打开状态,以便后续的fstat()ftruncate()调用使用这个文件描述符。

说明:
1. POSIX共享内存上shm_open()mmap()的关系类似于System V共享内存上shmget()shmat()的关系。使用POSIX共享内存对象需要两步式过程(shm_open()+mmap()),而没有使用单个函数来执行两项任务是因为历史原因。在POSIX委员会增加这个特性时,mmap()调用已经存在了,实际上,这里所需要做的事情是使用shm_open()调用替换open()调用,其中差别是使用shm_open()无需在一个基于磁盘文件系统上创建一个文件。
2. 由于共享内存对象的引用是通过文件描述符来完成的,因此可以直接使用UNIX系统中已经定义好的各种文件描述符系统调用,而无需增加新的系统调用。

使用共享内存对象

#include <fcntl.h>      // Defines 0_* constants#include <sys/stat.h>   // Defines mode constants#include <sys/mman.h>   // returns file descriptor on success, or -1 on errorint shm_open(const char *name, int oflag, mode_t mode);
  • name参数标识出了待创建或待打开的共享内存对象。
  • oflag参数是一个改变调用行为的位掩码。如果oflag中不包含O_CREAT,那么就打开一个既有对象,如果指定了O_CREAT,那么在对象不存在时就创建对象。同时指定O_EXCLO_CREAT能够确保调用者是对象的创建者,如果对象已经存在,就返回一个EEXIST错误。oflag参数还表明了调用进程在共享内存对象上的访问模式,其取值为O_RDONLYO_RDWR。而O_TRUNC会导致在成功打开一个既有共享内存对象之后将对象的长度截断为0。
oflag 作用 O_CREAT 对象不存在的时候创建 O_EXCL 与O_CREATE互斥地创建对象 O_RDONLY 打开只读访问 O_RDWR 打开读写访问 O_TRUNC 将对象长度截断为0
  • mode参数能取的位值与文件上的权限位值是一样的,与open()系统调用一样,mode中的权限掩码将会根据进程umask来取值,与open()不同的是,在调用shm_open()时总是需要mode参数,在不创建新对象时需要将这个参数指定为0。

一个新共享内存对象被创建时,其初始长度会被设置为0,这意味着在创建完一个新共享内存对象之后,通常在调用mmap()之前需要调用ftruncate()来设置对象的大小。在调用完mmap()之后可能还需要使用ftruncate()来根据需求扩大或收缩共享内存对象。在扩展一个共享内存对象时,新增加的字节会自动被初始化为0。

SUSv3要求POSIX共享内存对象至少具备内核持久性,即它们会持续存在直到被显示删除或系统重启。当不再需要一个共享内存对象时,就应该使用shm_unlink()删除它。

#include <sys/mman.h>// returns 0 on success, or -1 on errorint shm_unlink(const char *name);

shm_unlink()函数会删除通过name指定的共享内存对象。删除一个共享内存对象不会影响对象的既有映射,它会保持有效直到相应的进程调用munmap()或终止,但会阻止后续的shm_open()调用打开这个对象。一旦所有进程都解除映射这个对象,对象就会被删除,其中的内容会丢失。

例子

https://github.com/gerryyang/TLPI/tree/master/src/pshm

共享内存API比较

TODO

0 0
原创粉丝点击