操作系统的同步机制是老话题了,不过技术的东西放久不用了就容易忘记,在这里就信号量为大家整理一下思绪。
在linux系统中,信号量是一种可以睡眠的锁。如果进程A希望占有一个正在被进程B使用的信号量时,进程A会被推入等待队列,然后睡眠。直到进程B将持有的信号量释放,处于等待队列中的进程A才会被唤醒,并获得信号量。
从信号量的特点所得出的一些结论,如下:
-
- 由于等待信号量的进程会睡眠,因此信号量适用于需要长时间占用临界资源的情况,即临界区较长。
-
- 相反,如果临界区很短,就不适合用信号量,因为进入睡眠,唤醒,维护等待队列这一系列动作,可能比等待临界区资源的时间还要长。
-
- 等待信号量的进程会睡眠,因此不可以在中断处理中使用信号量,因为在中断上下文中是不能进行调度的。
-
- 占用信号量的时候,不能再次占用自旋锁,因为等待信号量的进程会睡眠,而等待自旋锁的进程不可以睡眠。
如何在自旋锁和信号量之间做个选择呢,即临界区短就用自旋锁,临界区长就用信号量,当然禁止睡眠的地方是不可以使用信号量的。
信号量的另一个特性是,同一时刻允许任意数量的持有者,相比自旋锁同一时刻只能允许一个持有者,信号量在某些场合是非常有优势的。
因此在linux中,信号量又分为两种:
1. 同一时刻可被多个持有者占有的信号量称为计数信号量 sem
2. 同一时刻只允许一个持有者占用的信号量称为互斥信号量 mutex
互斥信号量用的比较多一些,信号量到底可以允许几个持有者,是在声明信号量的时候用参数控制的。
struct semaphore ,这便是linux中信号量的数据类型,可以通过一下方式静态的声明信号量:
static DECLARE_SEMAPHORE_GENERIC(name, count);
其中参数,name自然是指信号量的名字,count就是刚才提到过的持有者的数量,如果希望创建一个互斥量,可以简单的使用下面的宏:
static DECLARE_MUTEX(name);
当你需要用kmalloc动态创建一个信号量时,可以用sem_init(sem, count)来初始化动态创建的信号量,当然用下面的简单调用可以初始化动态创建的互斥信号量:
init_MUTEX(sem);
说了这么多,下面贴上一段代码,和大家说说从声名,到使用一个信号量的完整过程:
/* 在全局位置上,定义并声名一个互斥信号量, 名字为test_sem */
static DECLARE_MUTEX(test_sem);
/* 在某个函数中, 希望获取信号量 */
if (down_interruptible(&test_sem)) { /* 在这里,down_interruptible会让我们睡眠 */
return; /* 信号量被占用,无法获取*/
}
/* 临界区 */
......
/* 释放信号量 */
up(&test_sem);
我们说说down_interruptible,它会试图获取信号量,但如果发现其已经被持有,down_interruptible会让当前的进程进入等待队列并睡眠。当然,这种睡眠是可以被打断的,可以响应信号,如果使用另一个获取信号量的函数 - down(),那就不能被信号打断,会一直睡在那里,直到希望获取的信号量被释放。
来源:http://lion3875.blog.sohu.com/102725871.html