谈谈对信号量的理解

信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)所拥有。

信号量的值为正的时候,说明它空闲。所测试的线程可以锁定而使用它。若为0,说明它被占用,测试的线程要进入睡眠队列中,等待被唤醒。

为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,我们需要一种方法,它可以通过生成并使用令牌来授权,在任一时刻只能有一个执行线程访问代码的临界区域。

临界区域是指执行数据更新的代码需要独占式地执行。而信号量就可以提供这样的一种访问机制,让一个临界区同一时间只有一个线程在访问它,也就是说信号量是用来调协进程对共享资源的访问的。

信号量是一个特殊的变量,程序对其访问都是原子操作,且只允许对它进行等待(即P(信号变量))和发送(即V(信号变量))信息操作。

最简单的信号量是只能取0和1的变量,这也是信号量最常见的一种形式,叫做二进制信号量。而可以取多个正整数的信号量被称为通用信号量。这里主要讨论二进制信号量。

由于信号量只能进行两种操作等待和发送信号,即P(sv)和V(sv),他们的行为是这样的:

1.P(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行
2.V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1.

例:A和B进程共享信号量sv,一旦A进程执行了P(sv)操作,它将得到信号量,并可以进入临界区,使sv减1。而B将被阻止进入临界区,因为当它试图执行P(sv)时,sv为0,它会被挂起以等待A离开临界区域并执行V(sv)释放信号量,这时B就可以恢复执行。

Linux提供两种信号量:

1.内核信号量,由内核控制路径使用
2.用户态进程使用的信号量,这种信号量又分为POSIX信号量和SYSTEM V信号量。

POSIX信号量又分为有名信号量和无名信号量

1.有名信号量,其值保存在文件中, 所以它可以用于线程也可以用于进程间的同步。
2.无名信号量,其值保存在内存中。

POSIX 信号量与SYSTEM V信号量的比较

1.POSIX信号量较为简单,是个非负整数。常用于线程间同步,POSIX信号量的引用头文件是<semaphore.h>
2.SYSTEM V信号量较为复杂,是一个或多个信号量的集合,它对应的是一个信号量结构体,这个结构体是为SYSTEM V IPC服务的,信号量只不过是它的一部分。常用于进程间同步,SYSTEM V信号量的引用头文件是<sys/sem.h>

内核信号量
Linux内核的信号量在概念和原理上与用户态的System V的IPC机制信号量是一样的,但是它绝不可能在内核之外使用,它是一种睡眠锁。如果有一个任务想要获得已经被占用的信号量时,信号量会将其放入一个等待队列然后让其睡眠。当持有信号量的进程将信号释放后,处于等待队列中的一个任务将被唤醒(因为队列中可能不止一个任务),并让其获得信号量。

与自旋锁的差异
由于争用信号量的进程在等待锁重新变为可用时会睡眠,所以信号量适用于锁会被长时间持有的情况;相反,锁被短时间持有时,使用信号量就不太适宜了,因为睡眠、维护等待队列以及唤醒所花费的开销可能比锁占用的全部时间表还要长。
信号量还有一个特征,就是它允许多个持有者,而自旋锁在任何时候只能允许一个持有者。我们遇到只有一个持有者,这种信号量叫二值信号量或者叫互斥信号量。允许有多个持有者的信号量叫计数信号量,在初始化时要说明最多允许有多少个持有者(Count值)。信号量在创建时需要设一个初始值,表示同时可以有多个任务访问该信号量保护的共享资源,初始值为1就变成互斥锁(Mutex),即同时只能有一个任务可以访问信号量保护的共享资源。当任务访问完被信号量保护的共享资源后,必须释放信号量,释放信号量通过把信号量的值加1实现,如果信号量的值为非正数,表明有任务等待当前信号量,因此它也唤醒所有等待该信号量的任务。
内核信号量的构成
内核信号量类似于自旋锁,因为当锁关闭着时,它不允许内核控制路径继续进行。然而,当内核控制路径试图获取内核信号量锁保护的忙资源时,相应的进程就被挂起。只有在资源被释放时,进程才再次变为可运行。
只有可以睡眠的函数才能获取内核信号量;中断处理程序和可延迟函数都不能使用内核信号量。

上一篇:路由器的System Verilog验证平台


下一篇:SV中的接口