当前位置:嗨网首页>书籍在线阅读

02-API概述

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

51.1 API概述

三种POSIX IPC机制拥有很多共有特性。表51-1对它们的API进行了总结,在后面几节中将深入介绍它们的共有特性的细节信息。

除了在表51-1中提及外,本章剩余的部分将不会特意指出POSIX信号量存在两种形式这个事实:命名信号量和未命名信号量。命名信号量与本章介绍的其他POSIX IPC机制类似:它们通过一个名字来标识,并且所有具备在该对象上合适权限的进程都能够访问该对象。未命名信号量没有关联的标识符,而是会被放置在由一组进程或单个进程中的多个线程共享的内存区域中。在53章将会对这两种信号量的细节予以介绍。

表51-1:POSIX IPC对象编程接口总结

| 接 口 | 消 息 队 列 | 信 号 量 | 共 享 内 存 | | :----- | :----- | :----- | :----- | :----- | :----- | | 头文件 | <mqueue.h> | <semaphore.h> | <sys/mman.h> | | 对象句柄 | mqd_t | sem_t * | int(文件描述符) | | 创建/打开 | mq_open() | sem_open() | shm_open() + mmap() | | 关闭 | mq_close() | sem_close() | munmap() | | 断开链接 | mq_unlink() | sem_unlink() | shm_unlink() | | 执行IPC | mq_send(), | mq_receive() | sem_post(), sem_wait(), | sem_getvalue() | 在共享区域中的位置上操作 | | 其他操作 | mq_setattr()——设置特性 | mq_getattr()——获取特性 | mq_notify()——请求通知 | sem_init()——初始化未命名信号量 | sem_destroy()——销毁未命名信号量 | 无 |

IPC对象名字

要访问一个POSIX IPC对象就必须要通过某种方式来识别出它。在SUSv3中规定的唯一一种用来标识POSIX IPC对象的可移植的方式是使用以斜线打头后面跟着一个或多个非斜线字符的名字,如/myobject。Linux和其他一些实现(如Solaris)允许采用这种可移植的命名方式来给IPC对象命名。

在Linux上,POSIX共享内存和消息队列对象的名字的最大长度为NAME_MAX(255)个字符,而信号量的名字的最大长度要少4个字符,这是因为实现会在信号量名字前面加上字符串sem.。

SUSv3并没有禁止使用形式不为/myobject的名字,但表示这种名字的语义是由实现定义的。在一些系统上,创建IPC对象名字的规则是不同的。如在Tru64 5.1上,IPC对象名字会被创建成标准文件系统中的名字,并且名字会被解释成为一个绝对或相对路径名。如果调用者没有权限在该目录中创建文件,那么IPC open调用就会失败。这意味着在Tru64上非特权程序无法创建形如/myobject之类的名字,因为非特权用户通常无法在根目录(/)中创建文件。其他一些实现在传递给IPC open调用的名字的构建上也存在特定的规则。因此在可移植的应用程序中应该将IPC对象名的生成工作放在一个根据目标实现裁剪过的单独的函数或头文件中。

创建或打开IPC对象

每种IPC机制都有一个关联的open调用(mq_open()、sem_open()以及shm_open()),它与用于打开文件的传统的UNIX open()系统调用类似。给定一个IPC对象名,IPC open调用会完成下列两个任务中的一个。

  • 使用给定的名字创建一个新对象,打开该对象并返回该对象的一个句柄。
  • 打开一个既有对象并返回该对象的一个句柄。

IPC open调用返回的句柄与传统的open()系统调用返回的文件描述符类似——它在后续的调用中被用来引用该对象。

IPC open调用返回的句柄的类型依赖于对象的类型。对于消息队列来讲返回的是一个消息队列描述符,其类型为mqd_t。对于信号量来讲,返回的是一个类型为sem_t *的指针。对于共享内存来讲返回的是一个文件描述符。

所有IPC open调用都至少接收三个参数——name、oflag以及mode——如下面的shm_open()调用所示:

1319.png 这些参数与传统的UNIX open()系统调用接收的参数类似。name参数标识出了待创建或待打开的对象。oflag参数是一个位掩码,在这个参数中至少可以包含下列几种标记。

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对象位于标准文件系统中的系统上,SUSv3允许实现将新IPC对象的组ID设置为父目录的组ID。

关闭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()系统调用。unlink调用会立即删除对象的名字,然后在所有进程使用完对象(即当引用计数等于0时)之后销毁该对象。对于消息队列和信号量来讲,这意味着当所有进程都关闭对象之后对象会被销毁;对于共享内存来讲,当所有进程都使用munmap()解除与对象之间的映射关系之后就会销毁该对象。

当一个对象被断开链接之后,指定同一个对象名的IPC open调用将会引用一个新对象(在不指定O_CREAT时会失败)。

与System V IPC一样,POSIX IPC对象也拥有内核持久性。对象一旦被创建,就会一直存在直到被断开链接或系统被关闭。这样一个进程就能够创建一个对象、修改其状态,然后退出并将对象留给在后面某个时刻启动的一些进程访问。

通过命令行列出和删除POSIX IPC对象

System V IPC提供了两个命令ipcs和ipcrm来列出和删除IPC对象。对于POSIX 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选项来完成。