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


sunbox$ ./mq_client
My key is 704654099
Message identifier is 2
Hello, world! (104)
客户端和服务器的输出表明,它们得出了相同的 IPC 密钥,因为它们都引用同一个文件和标识符 。服务器创建了 IPC 实例,内核为该实例分配了值 2,并且客户端应用程序知道这一点 。这样,客户端从消息队列提取回“Hello, world!就没什么奇怪的了 。
此示例显示了最简单的情况 。消息队列对于短期进程是有用的,例如将工作提交给重负荷后端应用程序(例如某个批处理应用程序)的 Web 事务 。客户端也可以是服务器,并且多个应用程序可以向队列提交消息 。消息类型字段允许应用程序将消息发送给特定的读取器 。
使用信号量锁定资源
进程之间的通信不需要涉及到发送大量的数据 。实际上,单个位可能就足以指示某个进程在使用某个资源 。请考虑两个需要访问某个硬件部分的进程,但是一次只有一个进程能够使用该硬件 。每个进程就包含引用计数器的点达成一致 。如果一个进程读取该计数器并看到其值为 1,则它就知道另一个进程正在使用该硬件 。如果该计数器的值为 0,则该进程就可以自由使用该硬件资源,前提是在该硬件操作期间将计数器设置为 1 并在结束操作时将其重置为 0 。
此场景存在两个问题:第一个问题不过就是设置共享计数器并就其位置达成一致,再没有比这麻烦的了 。第二个问题是锁定硬件资源所需要的获取和设置操作不是原子的 。如果一个进程在读取计数器时,其值为 0,但是在它还没有机会将计数器设置为 1 之前,另一个进程已抢先读取了该计数器,则第二个进程就能够读取和设置计数器 。两个进程都会认为它们可以使用该硬件 。没有办法知道另一个或其他进程是否会设置该计数器 。这称为争用条件 。信号量通过提供一个公共应用程序接口,以及通过实现原子测试和设置操作,从而同时解决了这两个问题 。
信号量的 SysV 实现比上述解决方案更通用 。首先,信号量的值不需要是 0 或 1;它可以是 0 或任何正数 。其次,可以执行一系列信号量操作,与用于 msgrcv 的 type 参数非常类似 。这些操作作为一个指令集提供给内核,并且这些指令要么全部运行,要么一个也不会运行 。内核要求将这些指令放在一个名为 sembuf 的结构中,该结构具有以下成员(按顺序):
sem_num:描述正在操作该集合中的哪一个信号量 。
sem_op:一个有符号整数,其中包含要执行的指令或测试 。
sem_flg:熟悉的 IPC_NOWAIT 标志(它指示测试应该立即返回还是阻塞直至通过)和 SEM_UNDO(它在进程提前退出时导致撤销该信号量操作)的组合 。
sem_op 是放置许多配置的地方:
如果 sem_op 为 0,则测试 sem_num 以确定它是否为 0 。如果 sem_num 为 0,则运行下一个测试 。如果 sem_num 不为 0,则在未设置 IPC_NOWAIT 时,操作将阻塞直至信号量变为 0,而在设置了 IPC_NOWAIT 时,则跳过其他测试 。
如果 sem_op 是某个正数,则将信号量的值加上 sem_op 的值 。
如果 sem_op 是一个负整数,并且信号量的值大于或等于 sem_op 的绝对值,则从信号量的值减去该绝对值 。
如果 sem_op 是一个负整数,并且信号量的值小于 sem_op 的绝对值,则在 IPC_NOWAIT 为 true 时立即停止测试的执行,而在该值为 false 时则阻塞,直至信号量的值变得大于 sem_op 的绝对值 。
清单 5 中的示例阐明了信号量的使用,其中研究了一个可同时运行多次的程序,但是该程序确保一次只有一个进程处于关键部分 。其中使用了简单情况下的信号量;当信号量的值为 0 时释放资源 。

推荐阅读