使用 UNIX System V IPC 机制共享应用程序数据

引言
Unix 内核管理的进程自主地操作,从而产生更稳定的系统 。然而,每个开发人员最终都会遇到这样的情况,即其中一组进程需要与另一组进程通信,也许是为了交换数据或发送命令 。这种通信称为进程间通信(Inter-Process Communication,IPC) 。System V (SysV) UNIX 规范描述了以下三种 IPC 机制,它们统称为 SysV IPC:
消息队列
信号量
共享内存
此外,进程还可以通过其他机制通信,例如:
读、写和锁定文件
信号
套接字
管道
FIFO(先进先出)
这后一组机制一般也称为 IPC 。由于其简单性和有效性,本文将集中于 SysV IPC 方法 。
了解 SysV 模型
三种 SysV IPC 方法具有类似的语法,尽管它们具有不同的用途 。一般情况下,它们执行以下操作:
确定要用于 ftok(3) 的正确 IPC 密钥 。
分别使用 msgget(2)、semget(2) 或 shmget(2) 获得用于消息队列、信号量或共享内存的特定于 IPC 的标识符,这些标识符与 IPC 密钥相关联 。
使用 msgctl(2)、semctl(2) 或 shmctl(2) 修改 IPC 实例的属性 。
利用特定的 IPC 实例 。
最后,使用 msgctl(2)、semctl(2) 或 shmctl(2) 和 IPC_RMID 标志销毁 IPC 实例 。
每个 IPC 实例都被赋予一个标识符,以将它与系统上存在的其他 IPC 实例区分开来 。例如,两个不同的应用程序可能分别决定使用共享内存段,因此系统范围的 IPC ID 将区分这两个实例 。虽然可能不是那么明显,但是第一个挑战就是弄清如何分发这样的信息:即如何在没有准备某种 IPC 机制的情况下附加到一个公共 IPC 实例 。
ftok 库调用使用某个给定文件中的索引节点信息和一个唯一标识符来得出一个密钥,只要该文件存在并且该标识符为常量,此密钥就保持相同 。因此,两个进程可以使用它们的配置文件和编译时常量来得出相同的 IPC 密钥 。常量的存在允许同一个应用程序通过改变常量来创建 IPC 机制的多个实例 。
在一组进程独立得出各自的 IPC 密钥之后,它们必须使用某个 get 系统调用来获得与该特定 IPC 实例关联的特定标识符 。各个 get 调用全都需要 IPC 密钥和一组标志,以及一些信号量和共享内存大小信息 。由于 Unix 是多用户系统,标志将包括熟悉的八进制形式的文件权限(因此 666 意味着任何人都可以执行读和写) 。如果还设置了 IPC_CREAT 标志,则会在 IPC 实例不存在时创建该实例 。如果没有设置 IPC_CREAT 标志并且还未创建 IPC 实例,则 get 调用将返回错误 。
对于能够自己分发 IPC 实例标识符的应用程序,存在一种用于执行该任务的更简单方法 。如果您在调用 get 以创建 IPC 时使用密钥 IPC_PRIVATE,则实例标识符将是唯一的 。需要附加到该 IPC 的其他进程不需要调用 get,因为它们已经拥有该标识符 。
一旦拥有了标识符,应用程序就可以任意使用 IPC 实例 。每种 IPC 方法都是不同的,并在它们各自的部分中进行处理 。
通过队列传递消息
消息队列提供了一种机制,使得一个进程可以发送另一个进程能够获得的消息 。在获得该消息之后,将从队列中删除该消息 。消息队列非常独特,因为两个进程不必同时存在——一个进程可以发送一个消息并退出,而该消息可以在数天后才被另一个进程获得 。
消息必须由一个长整数后面跟着消息数据组成 。清单 1 显示了 C 语言中的这样一个结构,其中使用了一个 100 字节的消息 。
清单 1. 示例消息的 C 语言定义
struct mq_message {
long type; /* The type or destination */
char text[100]; /* Data */
};
消息接收者使用消息类型 。当从队列轮询消息时,您可以选择第一个可用的消息,或者可以查找某种特定的消息类型 。将要使用的消息类型特定于应用程序,从而使得队列独特于其他形式的 IPC,因为内核通过读取 type 字段,从而在一定程度上了解所传递的应用程序数据 。

推荐阅读