此场景存在两个问题:第一个问题不过就是设置共享计数器并就其位置达成一致,再没有比这麻烦的了。第二个问题是锁定硬件资源所需要的获取和设置操作不是原子的。如果一个进程在读取计数器时,其值为 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 的绝对值,则从信号量的值减去该绝对值。
标签: