POSIX.1b实时扩展定义了一组IPC机制,他们呢与第45章到第48章中介绍的System V IPC机制类似。(POSIX.1b的开发者的其中一个目标是设计出语族能弥补System V IPC工具的不足指出的IPC机制。)这些IPC机制被统称为POSIX IPC。这三种POSIX IPC 机制具体如下。
- 消息队列可用来在进程间传递消息。与System V 消息队列一样,消息边界被保留了下来,这样读者和写者就以消息为单位(与管道提供的无分隔符的字节流是不同的)进行通信了。POSIX消息队列允许给每个消息赋一个优先级,这样在队列中优先级较高的消息会排在优先级较低的消息前面。这种功能从某种程度上来讲与System V消息中的类型字段提供的功能是一样的。
- 信号量允许多个进程同步各自的动作。与System V 信号量一样,POSIX信号量也是一个由内核维护的整数,其值永远都不会小于0.与System V 信号量相比,POSIX信号量在用法上要简单一些:他们时逐个分配的(与System V信号量集相比),并且在单个信号量上只能使用两个操作来将信号量的值加1或减1(与semop()系统调用能原子地在一个System V 信号量集中的多个信号量上加上或减去一个任意值相比)。
- 共享内存使得多个进程能够共享同一块内存区域。与System V 共享内存一样, POSIX共享内存提供了一种快速IPC。一个进程一旦更新了共享内存之后,所发生地变更立即对共享同于i区域地其他进程可见。
51.1 API概述
三种POSIX IPC 机制拥有很多共有特性。表51-1对他们地API进行了总结。
除了在表51-1中提及外,本章剩余地部分将不会特意之处POSIX信号量存在两种形式这个事实:命名信号量和未命名信号量。命名信号量与本章介绍地其他POSIX IPC 机制类似:他们通过一个名字来标识,并且所有具备在该对象上合适权限地进程都能够访问该对象。未命名信号量没有关联地标识符,而是会被放置在由一组进程或单个进程中地多个线程共享地内存区域中。
IPC 对象名字
要访问一个POSIX IPC对象就必须要通过某种方式来识别出它。在SUSv3中规定地唯一一种用来表示POSIX IPC对象地可移植的方式是使用以斜线大头后面跟着一个或多个飞鞋先字符地名字,如/myobject。Linux 和其他一些实现允许采用这种可以指地命名方式来给IPC对象命名法。
在Linux 上,POSIX共享内存和消息队列对象名字地最大长度为NAME_MAX(255)字符,而信号量地名字地最大长度要少4个字符,这是因为实现会在信号量i那个字前面加上字符串sem.。
SUSv3并没有进制使用形式不为/myobject地名字。如在Tru64.5.1上,IPC对象名字会被创建成标准文件系统中地名字,并且名字会被解释成为一个绝对或相对地路径名。如果调用者没有权限在该目录中创建文件,那么IPC open 调用就会失败。这意味着在Tru 64上非特权程序无法创建/myproject之类地名字,因为非特权用户通常无法在根目录(/)创建文件。其他一些实现在传递给IPC open调用地名字地构建上也存在特定的规则。因此在可一致的应用程序中应该将IPC对象名的生成工作放在一个根据目标实现才见过的单独的函数或头文件中。
创建或打开IPC对象
每种IPC机制都有一个关联的open调用(mq_open()、sem_open()以及shem_open()),它与用于打开文件的传统的UNIX open()系统调用类似。给定一个IPC对象名,IPC open调用会完成下列两个任务中的一个。
- 使用给定的名字创建一个新对象,打开该对象并返回该对象的一个句柄。
- 打开一个既有对象并返回该对象的一个句柄。
IPC open调用返回的句柄与传统的open()系统调用返回的文件描述符类似---他在后续的调用中被用来引用该对象。
IPC open调用返回的句柄的类型依赖于对象的类型。对于消息队列来将是一个消息队列描述符,,其类型为mqd_t。对于信号量来将,返回的是一个类型为sem_t *类型的指针。对于共享内存来将返回的是一个文件描述符。
所有IPC open 调用都至少接收三个参数--name、oflag以及mode--如下面的shm_open()调用所示:
fd = shm_open("/mymem",O_CREAT | O_RDWR,S_IRUSR | S_IWUSR);
这些参数与传统的UNIX open()系统调用接收的参数类似。name参数标识除了带创建或待打开的对象。oflsg参数是一个位掩码,在这个参数中至少可以包含下列几个标记。
O_CREAT
如果对象不存在,那么就创建一个对象。如果没有指定这个标记并且对象不存在,那么就会返回一个错误(ENOENT)。
O_EXCL
如果同时也指定了O_CREAT并且对象已经存在,那么就返回一个错误(EEXIST)。这两步--检查是否存在和创建---是原子操作(5.1节)。这个标记在不指定O_CREAT时是不起作用的。
根据对象的类型,oflag还可能会包含O_RDONLY、O_WRONLY以及O_RDWR这三个值中的一个,其含义与他们在open()含义相同。一些IPC机制还支持额外的标记。
剩下的参数mode是一个位掩码,它指定了在对象被创建时(即指定了O_CREAT并且对象不存在)施加于新对象之上的权限。mode参数能取的值与其在文件上的取值一样(表15-4).与open()系统调用一样,mode中权限掩码会根据进程的umask(15.4.6节)取掩码。新IPC对象的所有权和组所有权将根据发起这个IPC open调用的进程的有效用户ID和组ID来确定。(严格来将,在Linux上,新POSIX IPC 的所有权是由进程的文件系统ID来确定的,而进程的文件徐通ID通常与相应的有效ID的值是一样的。参考9.5节)
关闭IPC 对象
对于POSIX消息队列和信号量来将,存在一个IPC close调用来表明进程已经使用完该对象,系统可以释放与该对象关联的所偶资源了。POSIX共享内存对象的关闭则是通过使用munmap()接触映射来完成的。
IPC对象在进程终止或执行exec()时会自动被关闭。
IPC对象权限
IPC对象上的权限掩码与文件上的权限严么是一样的。访问一个IPC对象的权限与昂问文件的权限(15.4.3节)是类似的,但对于POSIX IPC 对象来将,执行权限是没有意义的。
从内核2.6.19起,Linux 支持使用访问控制列表(ACL)来设置POSIX共享内存对象和命名信号量上的权限。目前,在POSIX消息队列上不支持ACL.
IPC对象删除和对象持久性
与打开文件一样,POSIX IPC 对象也有引用计数--内核会维护对象上的打开和引用计数。与System V IPC对象相比,这种方式使得应用程序能够更加容易地确定合适可以安全地删除一个对象。
每个IPC 对象都有一个对应的unlink调用,其操作类似于应用文件的传统的unlink()系统调用会立即删除对象的名字,然后在所有进程中使用完对象(即当引用就hi书等于0时)之后销毁该对象。对于消息队列和信号量来将,这意味着当所有进程都关闭对象之后对象会销毁;对于共享内存来将,当所有进程都是用munmap()解除与对象之间的映射关系之后会销毁该对象。
当一个对象被断开连接之后,指定同一个对象名的IPC open调用将会引用一个新对象(在不指定O_CREAT时会失败) 。
与System V IPC 一样,POSIX IPC 对象也拥有内核持久性。对象一旦被创建,就会一直存在直到被断开连接或系统被关闭。这样一个进程就能够创建一个对象、修改其状态,然后退出并将对象留给后面某个时刻启动的一些进程访问。
通过命令行列出和删除POSIX IPC 对象
System V IPC提供了两个命令ipcs和ipcrm来列出和删除IPC 对象。对于POIX IPC 对象来讲,不存在标准的命令来执行类似的服务。然而在很多系统上,包括Linux IPC对象是一个挂载在根目录(/)下某处的真实或虚拟文件系统中实现的,因此可以使用标准的ls和rm命令来列出和删除IPC 对象。(SUSv3并没有规定使用ls和rm来完成这些任务。)使用这些命令存在的主要问题是POSIX IPC 对象名以及他们在文件系统中所处的位置是不标准的。
在Linux上,POSIX IPC 对象位于挂载在设置了粘滞位的目录下的虚拟文件系统。这个位是一个受限的删除标记(15.4.5节)设置该位标识得特权进程只能够断开它自己哦那个有的POSIX IPC对象的链接。
在Linux 上编译使用 POSIX IPC 的程序
在Linux 上,使用 POSIX IPC机制的程序必须要与实时库librt连接起来,这可以通过在cc命令中指定-lrt选项来完成。
51.2 System V IPC与POSIX IPC 比较
与System V IPC 相比,POSIX IPC 拥有下列常规优势。
- POSIX IPC 的接口比System V IPC接口简单。
- POSIX IPC 模型--使用名字代替键、使用open、close以及unlink函数--与传统的UNIX模型更加一致
- POSIX IPC 对象是引用计数的。这就简化了对象删除,因为可以带你开一个POSIX IPC 对象的链接,并且直到当所有进曾都关闭该对象之后就会被销毁。
然而System V IPC具备一个显著的优势:可移植性。POSIX IPC在下列方面的移植性不如System V IPC.
- System V IPC在SUSv3中进行了规定,并且几乎所有的UNINX 实现都支持System V IPC.而与之相反的是,POSIX IPC 机制在SUSv3中则是一个可选的组件。一些UNIX 实现并不支持(所有)POSIX IPC机制。这种情况可以通过Linux 上的微观世界反映出来:POSIX 狗好像内存从内核2.4开始得到支持;完整的POSIX信号量从内核2.6开始得到支持POSIX消息队列从内核2.6.6开始得到支持。
- 尽管SUSv3对POSIX IPC名字进行了规定,但各种实现任然采用不同的规则来命名IPC 对象。这些差异使得程序在编写可移植应用程序时需要做一些(很少)额外的工作。
- POSIX IPC 的各种细节并没有在SUSv3中进行规定。特别是没有规定使用哪些命令来显示和删除系统上的IPC 对象。(在很多实现上使用的时标准的文件系统命令,但用来标识IPC对象的里精明的细节信息则因实现而异。)
51.3 总结
POSIX IPC 是一个一般名称,它指由POSIX.1b涉及来取代与之类似的System V IPC 机制的三种IPC机制---消息队列、信号量以及共享内存。
POSIX IPC 接口与传统的UNIX 文件模型更加一致。IPC 对象是通过名字来标识的,并使用open()、close以及unlink等操作方式与相应的文件相关的系统调用类似的调用来管理。
POSIX IPC提供的几口在很多方面都优于System V IPC接口,但POSIX IPC可移植性要比System 稍差。