svipc - System V interprocess communication mechanisms
linux实现的System V interprocess communication (IPC)机制包含消息队列(message queues),信号集(semaphore sets),和共享内存(shared memory segments)。
man 7 svipc
SystemV消息通信弊端
在新的应用中很少会用到System V IPC,因为它已经被POSIX IPC取代了。但编写老程序时仍可能用到。
像管道一样,IPC存在于内核(实际上是内核内存)而不是像FIFO一样存在于文件系统中。IPC的集中结构有时合起来叫做IPC对象(其实是信号灯、消息队列和共享内存)。
每个对象都通过它的标识符来引用和访问,标识符是一个正整数,它唯一地标识出对象本身和它的类型。每个标识符的类型都是唯一的,但同一标识符的值可以用于一个消息队列、一个信号灯和一个共享内存区。标识符称为该结构上所有其他操作的句柄。IPC结构标识符不想文件描述符那样使用较小的正整数。实际上,随着结构的创建和删除,标识符的值(正式的名称叫做槽使用顺序号)会不断增加直至达到一个最大值为止,然后再转回到0并重新开始。在linux系统中,标识符声明为整数,所以它的值最大可能为65535。
每个IPC结构都有get函数创建,在创建了一个IPC结构之后,使用同一个关键字(key)的get函数的后续调用不会创建新结构,但返回和现在结构相关的标识符。这可以让两个或两个以上的进程用同一关键字key调用get函数以建立一条IPC通道。
接下来的问题是怎样确保所有要使用同一IPC结构的进程都使用相同的关键字。一种方法为,实际创建结构的进程给get函数传递IPC_PRIVATE关键字,这能保证创建一个新结构。然后创建IPC结构的进程把返回的标识符保存在其他进程能够访问的文件系统中。在父进程fork或exec一个子进程的场合,父进程可以把返回的标识符作为一个参数传递给创建子进程的函数exec。
另一种传递关键字的方法是把它保存在公共的头文件中,这样一来所有包含了这个头文件的程序都能够访问到相同的关键字。这种方法引出的一个问题,没有进程直到它是正在创建一个新结构呢还是只访问已经由其他进程创建好的结构。这种方法带来的另外一个问题是关键字可能已经被另外一个无关的程序使用了。结果使用这个关键字的进程必须包含处理这种可能性的代码。
第三种方法是用ftok函数,这个函数接受一个路径名和一个称为项目标识符的单个字符作为参数,返回一个关键字可以传递给get函数。有程序员保证所有的进程实现直到路径名和项目标识符。你可以使用前面提到过的方法之一:在公共的头文件中包含路径名和项目标识符,或者把他们保存在预定义的配置文件中。
不幸的是,ftok有个严重的缺陷:它不能保证产生唯一的关键字,这一来出现了和前面讨论的第二种方法一样的问题。在下列情况下,ftok生成唯一的关键字:
>>当两个不同的符号链接到同一文件上。
>>当路径名的索引节点的前16个比特位具有相同的值。
>>当系统带有两个相同次设备号的硬盘时,在系统由多个磁盘控制器的情况下才会出现。主设备号不相同,但次设备号可以相同。
考虑到linux的ftok实现有弱点,所以强烈建议读者不使用它并且忽略它。
System V IPC有几个缺点,第一,和它提供的好处相比,其编程接口过于复杂。第二,IPC结构比其他资源,比如系统能够支持的文件数量或系统允许的活动进程数目等受到的限制大的多。第三,尽管是一种受限资源,但IPC结构却没有保留一个引用计数(它是一个记录使用结构的进程数目的计数器),因此System V IPC没有回收被丢弃的IPC结构的自动机制。最后,如前面所述,IPC结构只存在于内核中,文件系统不知道他们。因此,对他们的I/O操作需要学习另外一种编程接口。没有文件描述符,你不能通过系统调用select使用多路复用的I/O。如果进程需要等待IPC结构上的I/O操作,它必须使用某种忙等待循环。一个忙等待循环--持续检查某些变化条件的循环--几乎在任何时刻都是一种糟糕的编程习惯,因为它不必要的消耗了CPU周期。在linux下,忙等待循环特别有害,有几种方法,比如阻塞I/O、使用系统调用select和信号来实现非忙等待的循环。
推荐使用POSIX IPC