信号量

一个信号是一个变量或抽象数据类型是用于控制访问,由多个进程,以便在一个共同的资源并发系统如一个多道程序操作系统。

想在现实世界中的系统中使用的信号的一个有用的方式是提供一个特定资源的多少个单位,再加上操作的记录,安全(即防止竞态条件)调整了纪录所需单位或变得自由,并且,如果需要的话,等到资源的单元变为可用。信号量是在防止竞争条件的有用工具; 然而,它们的使用是绝不是保证一个节目是从上述问题的分类。信号灯允许任意的资源数量被称为计数信号,而这被限制在值0和1(或锁定/解锁,不可用/可用)信号灯称为二进制信号。

信号量的概念是由发明荷兰 计算机科学家 Edsger Dijkstra算法在1962年或1963年,已经发现在各种广泛使用的操作系统。它也被用作I / O控制器的控制机构,例如在Electrologica X8计算机。


当用于控制访问池资源,信号量只跟踪有多少资源是免费的; 它并不跟踪其中的资源都是免费的。可能需要一些其他机制(可能涉及更多的信号量)来选择特定的免费资源。
范例是特别强大,因为信号计数可以用作许多不同的操作的有用的触发。上述馆长可关灯在自习时,有没有剩余的学生,也可以放置一个牌子,上面写着房间都非常忙的时候大部分的客房都被占用。
该协议的成功需要应用正确遵循它。公平性和安全性有可能受到影响(这实际上意味着一个程序可能会表现得慢,不稳定的行为,挂起或崩溃),如果连一个单一的过程中的行为不正确。这包括:
请求资源和忘记释放;
释放这是从来没有请求的资源;
拿着很长一段时间,而不需要它的资源;
使用资源,而不首先请求它(或释放它之后)。

即使所有的过程遵循这些规则,多资源死锁时,有不同的信号和管理不同的资源,仍然可能发生,当流程需要同时使用多个资源,由所示哲学家就餐问题。

计数信号量都配有两种操作,在历史上记为P和V(见§操作名称替代名称)。V作战​​递增信号量小号和运行时的P 减它。
该信号量的值小号是当前可用的资源的单位数。P操作浪费时间或休眠直到由信号量保护的资源变得可用时,在该时间资源被立即权利。在V操作是相反:它使一个资源可用后再次进程已使用完毕。信号中的一个重要属性小号是不能除通过用V和P的操作而改变其值。
一个简单的方法来了解等待(P)和信号(V)的操作是:
等待:如果信号量变量的值不为负,减1它如果信号量的变量是现在否定的,则处理执行等待被阻塞(即,加到信号量的队列),直到值大于或等于1。否则,过程继续执行,具有所使用的资源的单位。
信号:递增1信号量变量的值的增加后,如果预先递增值为负(意味着有等待资源的进程),其传送从所述信号量的等待队列到就绪队列中的阻塞的进程。
许多操作系统都提供了一个解除阻止等待的过程时,信号量递增有效信号元。这意味着,过程不浪费不必要的时间检查信号量的值。
计数信号量的概念可与权利要求或信号量,在实现技术返回多个“单位”的能力进行扩展的Unix。

为了避免饥饿,信号量有一个相关的队列中的进程(通常与FIFO语义)。如果一个过程在具有零值信号量进行P动作,则处理被添加到信号灯的队列和它的执行被挂起。当其它处理而执行为V操作递增信号,和有在队列过程,它们中的一个从队列中删除,并恢复执行。当方法具有不同的优先级队列可以通过优先排序,使得最高优先级的处理被从队列中取出第一。
如果实现不保证递增,递减和比较操作的原子,再有就是被遗忘递增或递减的风险,或信号量的值成为负数。原子可以通过使用机器指令,能够实现读取,修改和写入在单个操作中的信号量。在没有这种硬件的指令,一个原子操作可通过使用一个来合成软件互斥算法。在单处理器系统中,原子操作可以暂时中止确保抢占或禁用硬件中断。其中,它有可能为两个程序共用信号在同一时间在不同的处理器上运行该方法不多处理器系统上工作。为了解决这个问题,在一个多处理器系统中的锁定变量可以用来控制访问信号量。锁定变量是用操纵的测试和设置锁命令。


信号量与互斥
一个互斥基本上是同样的事情当作二元信号灯,有时使用相同的基本实现。它们之间的差异是在如何使用它们。而二进制信号可被用作一个互斥,互斥是一个更具体的用例,在只有该锁定互斥的处理是应该解锁。这种约束使得有可能实现互斥一些额外的功能:
1.因为只有锁定互斥体的过程中应该打开它,一个互斥体可以存储过程中的锁定它的ID,并确认相同的过程解锁。
2.互斥体可以提供优先级反转的安全性。如果互斥知道谁锁住它,应该打开它,就可以当一个高优先级任务开始等待互斥体,以促进这一进程的优先级。
3.互斥锁还可以提供安全的缺失,其中过程持有互斥不会被意外删除。
4.或者,如果该过程保持互斥被删除(可能是由于不可恢复的错误),则互斥可以自动释放。

5.互斥可以是递归的:一个进程被允许多次锁定,而不会造成死锁。


在 Linux 下,PV 操作通过调用semop函数来实现。该函数定义在头文件 sys/sem.h中,原型如下:
int semop(int semid,struct sembuf *sops,size_t nsops);


在 Linux 下,PV 操作通过调用semop函数来实现。该函数定义在头文件 sys/sem.h中,原型如下:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, unsigned nsops);
int semtimedop(int semid, struct sembuf *sops, unsigned nsops, struct timespec *timeout);
参数
semid:信号集的识别码,可通过semget获取。
sops:指向存储信号操作结构的数组指针,信号操作结构的原型如下

struct sembuf
{
unsigned short sem_num; /* semaphore number */
short sem_op; /* semaphore operation */
short sem_flg; /* operation flags */
};


这三个字段的意义分别为:
sem_num:操作信号在信号集中的编号,第一个信号的编号是0。
sem_op:如果其值为正数,该值会加到现有的信号内含值中。通常用于释放所控资源的使用权;如果sem_op的值为负数,而其绝对值又大于信号的现值,操作将会阻塞,直到信号值大于或等于sem_op的绝对值。通常用于获取资源的使用权;如果sem_op的值为0,则操作将暂时阻塞,直到信号的值变为0。
sem_flg:信号操作标志,可能的选择有两种
IPC_NOWAIT //对信号的操作不能满足时,semop()不会阻塞,并立即返回,同时设定错误信息。

SEM_UNDO //程序结束时(不论正常或不正常),保证信号值会被重设为semop()调用前的值。这样做的目的在于避免程序在异常情况下结束时未将锁定的资源解锁,造成该资源永远锁定。

nsops:信号操作结构的数量,恒大于或等于1。
timeout:当semtimedop()调用致使进程进入睡眠时,睡眠时间不能超过本参数指定的值。如果睡眠超时,semtimedop()将失败返回,并设定错误值为EAGAIN。如果本参数的值为NULL,semtimedop()将永远睡眠等待。
返回说明
成功执行时,两个系统调用都返回0。失败返回-1,errno被设为以下的某个值
E2BIG:一次对信号的操作数超出系统的限制
EACCES:调用进程没有权能执行请求的操作,并且不具有CAP_IPC_OWNER权能
EAGAIN:信号操作暂时不能满足,需要重试
EFAULT:sops或timeout指针指向的空间不可访问
EFBIG:sem_num指定的值无效
EIDRM:信号集已被移除
EINTR:系统调用阻塞时,被信号中断
EINVAL:参数无效
ENOMEM:内存不足
ERANGE:信号所允许的值越界

你可能感兴趣的:(信号量)