6、XSIIPC
三种类型的XSI IPC:消息队列,信号量,和共享内存,有许多共同点。这里将讨论这些共同的特性,后面会依次讨论它们的各自的特性。
XSI的IPC函数基于System V的IPC函数。这三种类型的IPC起源于1970s的一个被称作"Columbus UNIX"的内部AT&T的UNIX版本。后来这三种类型的IPC被添加到System V中。它们不使用文件系统的命名空间而是使用自己的标准的命名空间。
我们应当知道(前面的表格中也说了):消息队列,信号量和共享内存被定义为Single UNIX Specification的XSI扩展。
标识和关键字
内核中的每个IPC的结构(消息队列,信号量或者共享内存段),通过一个非负的整数来引用。例如当发送或者从一个消息队列中接收消息的时候,我们所知道的就只是这个队列的标识。和文件描述符号不同,IPC的标识不是一个小的整数。实际上,当创建并且移走一个IPC结构的时候,相应的标识符号会连续地增加,知道它到达一个最大的正整数值,然后又回归到0。
标识只是一个IPC对象的内部名字。协作进程需要一个外部名字策略以便能够使用IPC对象进行交互。为了实现这个目的,IPC对象和一个关键字进行关联,这个关键字的作用就像是一个外部的名字一样。
当创建一个IPC结构的时候(通过调用msgget, semget, 或 shmget),必须指定一个关键字。关键字的类型是key_t,它是在头文件<sys/types.h>中被定义的一个长整数类型。这个关键字在内核里面被转换成一个标识。
对于一个客户和服务,有许多种不同的方法来使用同一个IPC结构。
-
服务可以通过指定一个IPC_PRIVATE的关键字来创建一个IPC结构,然后将返回的标识存储到一个地方(例如文件系统中)让客户能够获取到。关键字IPC_PRIVATE可以确保服务能够创建一个新的IPC结构。使用这个技术的缺点就是,服务进程需要通过文件系统操作来将整数标识写入到文件中,然后客户就可以获取到这个标识了。
IPC_PRIVATE关键字也可以使用在父子关系的进程中,父进程通过指定IPC_PRIVATE创建一个新的IPC结构,然后返回的标识对于后来fork出来的子进程就是可用的了。子进程可以通过exec函数的参数,将这个标识符号传递给一个新启动的程序中。
-
客户和服务可以通过一个共同的头文件来商定好一个定义的关键字。例如,服务之后通过指定这个关键字创建一个新的IPC结构。这个方法的一个问题就是,有可能这个关键字已经和某个其他的IPC结构关联了,在这个情况下,get函数(msgget,semget,或者shmget)返回一个错误。服务进程必须处理这个错误,删除已经存在的IPC结构,然后再重新创建一个。
-
客户和服务进程可以商定好一个路径和project ID(这个project ID是一个介于0到255之间的字符),然后调用函数ftok将这两个值转换成一个关键字。这个关键字然后在第2步(应该就是前面的头文件的方法)中被使用。ftok所提供的功能就只是从一个给定的路径名和project ID生成一个关键字。
include <sys/ipc.h>
key_t ftok(const char *path, int id);
返回:如果成功返回关键字,如果失败返回(key_t)-1。
这里的path参数必须引用一个已经存在的文件名称,而参数id则只使用它的低8位来生成关键字。
ftok创建的关键字,一般是通过path对应的文件的stat结构的st_dev和st_ino成员结合project ID,来进行生成的。如果指定两个不同的path,那么ftok通常会返回两个不同的关键字。然而由于i-node成员和关键字都是通过长整型来存储的,所以在创建关键字的时候必然会丢失一些信息(关于索引节点的唯一性的信息),这意味着如果两个不同的路径名,如果指定的project id是相同的话,也是有可能会产生同样值的关键字的。
三个get函数(msgget,semget和shmget)都有两个类似的参数:一个关键字和一个整数标记。
当一个 新的IPC结构被创建 (通常是服务进程创建)的时候,通常是由如下情况导致:
- 指定的关键字应当是IPC_PRIVATE
- 或者是一个没有与任何其他特定IPC结构关联的关键字并且指定了IPC_CREAT标记。
如果想要引用一个已经存在的队列(通常是客户进程引用),关键字必须要和创建该队列时候的关键字一样并且没有指定IPC_CREAT标记。
需要注意的是,我们不可能通过IPC_PRIVATE关键字来引用一个已经存在的queue,因为这个特殊的关键字值会创建一个新的queue。为引用一个已经存在的使用关键字IPC_PRIVATE创建的queue,我们必须知道相应的表示,然后在其他的IPC调用中(例如msgsnd和msgrcv)使用那个标识,而不是通过get函数来获得标识。
如果我们想要创建一个新的IPC结构变量,那么我们需要确保我们不会引用一个使用同样表示的已经存在的IPC结构变量,我们必须同时设定IPC_CREAT和IPC_EXCL位的标记。使用这个方法,当已经存在了那个IPC结构的时候,会导致返回一个EEXIST错误(这个情况就类似使用指定了O_CREAT和O_EXCL标记的open函数)。
权限结构
XSI的IPC结构和一个ipc_perm结构相关联,这个结构定义了权限和属主,并且至少包含了如下的成员:
struct ipc_perm {
uid_t uid; /* owner's effective user id */
gid_t gid; /* owner's effective group id */
uid_t cuid; /* creator's effective user id */
gid_t cgid; /* creator's effective group id */
mode_t mode; /* access modes */
.
};
每种实现都包含了额外的成员,可以通过查看你系统上面的<sys/ipc.h>文件来了解完整的定义。
当建立IPC的时候,所有的成员都会被初始化。之后,我们可以通过调用msgctl,semctl,或者shmctl来修改uid,gid,和mode成员。为了能够改变这些值,调用进程必须是IPC结构的创建者或者是超级用户。修改这些成员和给一个文件调用chown或者chmod类似。
mode成员的值和<sys/stat.h>中定义的文件访问权限很类似,但是对于任何的IPC结构没有相应的执行权限。同时,消息队列和共享内存使用read和write但是信号量使用read和alter。下表展示的就是每种IPC形式的六个权限:
XSI的IPC权限
+-----------------------------+
| Permission | Bit |
|----------------------+------|
| user-read | 0400 |
|----------------------+------|
| user-write (alter) | 0200 |
|----------------------+------|
| group-read | 0040 |
|----------------------+------|
| group-write (alter) | 0020 |
|----------------------+------|
| other-read | 0004 |
|----------------------+------|
| other-write (alter) | 0002 |
+-----------------------------+
有一些实现定义了一些常量来表示这些权限,但是这些都没有被Single UNIX Specification标准化。
配置限制
我们可以看到,所有三种形式的XSI IPC都有内部的限制。所有这些限制可以通过内核来进行配置,我们后面讲到每一种IPC的时候,将会对这些限制进行描述。
每个平台提供了它自己的显示和修改特定限制的方法。FreeBSD 5.2.1,Linux 2.4.22,和Mac OS X 10.3提供了sysctl命令来查看和修改内核配置参数。Solaris 9可以通过修改文件/etc/system然后重新启动来修改内核的配置参数。
在Linux上面,呢可以通过运行"ipcs -l"来查看IPC相关的限制。在FreeBSD上,相应的命令是"ipcs -T"。在Solaris上,你可以运行sysdef -i来查看可以调节的参数。
优点和缺点
(内容不会被及时释放)XSI IPC的一个基本问题就是IPC结构是系统范围内的,没有一个引用计数。例如,如果我们创建了一个消息队列,将一些消息放到这个队列上面,然后终止进程,那么消息队列和它的内容并不会被删除。它们会保留在系统中,直到其它进程调用msgrcv或者msgctl进行特定的读取或删除,或者通过执行ipcrm命令,或者通过系统的重新启动。与pipe相比,pipe会在最后一个引用它的进程结束的时候完全地被移除。对于FIFO,尽管管道的名字在文件系统中保留着(以管道文件的形式),但是FIFO中保留的任何数据会在最后一个引用该FIFO的进程结束的时候被移走。
(无法像文件那样直观)另外一个XSI IPC的问题就是,这些IPC结构不是通过在文件系统中的名字被知道的。我们无法通过前面的函数来访问和修改它们。有许多系统调用(msgget,semop,shmat,等等)被添加到内核中以便支持这些IPC对象。我们无法通过ls命令来查看IPC对象,我们无法通过rm命令来删除它们,我们也无法通过chmod命令来修改它们的权限。然而,有两个命令"ipcs"和"ipcrm"被加入了进来,可以实现类似的功能。
(无法进行高级的文件操作)因为这些IPC不能使用文件描述符号,所以我们不能构对它们使用多I/O函数(select和poll函数)进行操作。这使得我们同时使用多个IPC结构或者通过文件或者设备I/O使用任何这些IPC结构变得非常的困难。例如,我们无法有一个服务进程不经过忙等待的循环方式等待消息被放到两个消息队列中的一个上面。
还有更多关于有点和缺点的讨论或者争辩,这里就不一一列举了。同时有一个描述各种IPC特性的表格,这里也不列出了。具体参见参考资料。
后面将会对三种IPC分别进行详细的讲解。
译者注
-
标识
IPC有类似文件系统的方式,来使用一个整数标识来标识一个IPC对象。
-
创建获取
调用msgget, semget, 或 shmget来创建以及获取一个IPC对象。
创建:使用一个关键字+CREATE标记
获取:使用之前CREATE的关键字(无CREATE标记)
-
关键字
IPC_PRIVATE方式,直接创建IPC对象,返回标识,然后直接使用其他的IPC调用(例如msgsnd和msgrcv)使用那个标识。适用于父子进程,或者将标识存放在文件中供其它进程引用到该IPC对象。
使用关键字方式:生成关键字供msgget, semget, 或 shmget来创建以及获取一个IPC对象。可以事先通过头文件商定好那个关键字,或者使用系统路径结合ftok创建关键字。
网友评论