|
平台:x86 32位
内核:2.6.24
信号量的定义在 include/asm-x86/Semaphore_32.h:
struct semaphore {
atomic_t count;
int sleepers;
wait_queue_head_t wait;
};
count : 指定了可以同时处于信号量保护的临界区中的进程数。一般情况下会设 count == 1,表示同一时刻允许进入临界区只有 1 个进程,这时的信号量也称为互斥信号量。
sleepers : 指定了允许进入等待进入临界区的进程数。和自旋锁不同,等待的进程不会原地打转,而是进入睡眠,直到信号量被释放时才会被唤醒。
wait : 实现一个等待队列,在该队列中保存了所有因等待该信号量而睡眠的进程的 task_struct 。
信号量和自旋锁的比较:
信号量更适合用于保护较长的临界区(代码比较多,等待进入临界区的进程可能要等待较长的时间),而不用它来保护较短的临界区(代码较少,等待进入临界区的进程无需等待较长的时间便可以进入临界区)。其原因是,等待信号量是需要睡眠的,在要进入临界区时又要唤醒睡眠,如果等待的时间不长,这样一睡一醒反而代价更高。所以,等待较短的临界区适合用自旋锁。
大多数情况下,信号量只用作互斥量。
直接创建信号量可以使用 sema_init() 函数来完成,其定义在 include/asm-x86/Semaphore_32.h 中:
static inline void sema_init (struct semaphore *sem, int val)
{
/*
* *sem = (struct semaphore)__SEMAPHORE_INITIALIZER((*sem),val);
*
* i'd rather use the more flexible initialization above, but sadly
* GCC 2.7.2.3 emits a bogus warning. EGCS doesn't. Oh well.
*/
atomic_set(&sem->count, val);
sem->sleepers = 0;
init_waitqueue_head(&sem->wait);
}
如果用于互斥模式,内核还提供了 DECLARE_MUTEX 宏来初始化,这样会让情况显得更简单些,该宏也定义在 include/asm-x86/Semaphore_32.h 中:
[Plain Text] 纯文本查看 复制代码 #define DECLARE_MUTEX(name) __DECLARE_SEMAPHORE_GENERIC(name,1)
__DECLARE_SEMAPHORE_GENERIC 宏的定义为:
[Plain Text] 纯文本查看 复制代码 #define __DECLARE_SEMAPHORE_GENERIC(name,count) struct semaphore name = __SEMAPHORE_INITIALIZER(name,count)
__SEMAPHORE_INITIALIZER(name,count) 的定义为:
#define __SEMAPHORE_INITIALIZER(name, n)
{
.count = ATOMIC_INIT(n),
.sleepers = 0,
.wait = __WAIT_QUEUE_HEAD_INITIALIZER((name).wait)
}
另外,在早期的内核中还提供了 DECLARE_MUTEX_LOCKED 宏,它用于初始化一个已经锁定了的信号量。该宏在 2.6.24 后的内核中已经取消,因为有了一个称为“完成量”(completion) 的更好实现方法。 |
|