Linux中的信号量是一种睡眠锁。如果有一个任务试图获得一个已经被占用的信号量时,信号量会将其推进一个等待队列,然后让其睡眠。这时处理器能重获自由,从而去执行其他代码。当持有信号量的进程将信号量释放后,处于等待队列中的那个任务将被唤醒,并获得该信号量。
由信号量的睡眠特性得出的结论:
由于争用信号量的进程在等待锁重新变为可用时会睡眠,所以信号量适用于锁会被长时间持有的情况。
锁被短时间持有时,使用信号量就不适宜。因为睡眠、维护等待队列以及唤醒所花费的开销可能比锁被占用的全部时间还要长。
由于执行线程在锁被争用时会睡眠,所以只能在进程上下文中才能获取信号量锁,因为在中断上下文中是不能进行调度的。
你可以在持有信号量时去睡眠,因为当其他进程试图获得同一信号量时不会因此而死锁。
在你占用信号量的同时不能占有自旋锁。因为在你等待信号量时可能会睡眠,而在持有自旋锁时是不允许睡眠的。
在需要和用户空间同步时,你的代码会需要睡眠,此时使用信号量是唯一的选择。
信号量不同于自旋锁,它不会禁止内核抢占,所有持有信号量的代码可以被抢占。这意味着信号量不会对调度的等待时间带来负面影响。
信号量同时运行任意数量的锁持有者,而自旋锁在一个时刻最多允许一个任务持有它。信号量同时允许的持有者数量可以在声明信号量时指定,这个值称为使用者数或简单称为数量count。
当信号量在一个时刻只允许有一个锁持有者,这时计数等于1,这样的信号量被称为互斥信号量。
当信号量的数量大于1时,称为计数信号量,它允许在一个时刻至多有count个锁持有者。计数信号量不能用来进行强制互斥,因为它允许多个执行线程同时访问临界区。
- 在<semaphore.h(include/asm-i386)>中
- struct semaphore {
- atomic_t count;
- int sleepers;
- wait_queue_head_t wait;
- };
信号量支持两个原子操作p()和V()。前者叫做测试操作,后者叫做增加操作。后来的操作系统都分别叫做down()和up()。down()操作通过对信号量计数减1来请求获得一个信号量,如果结果是0或大于0,获得信号量锁,任务就可以进入临界区。如果结果是负数,任务会被放入等待队列。当临界区中的操作完成后,up()操作用来释放信号量,又称为提升(upping)信号量。如果在该信号量上的等待队列不空,则处于该队列伤的任务被唤醒的同时会获得该信号量。
-
- static inline void down(struct semaphore * sem)
- {
- might_sleep();
- __asm__ __volatile__(
- "# atomic down operation/n/t"
- LOCK_PREFIX "decl %0/n/t"
- "jns 2f/n"
- "/tlea %0,%%eax/n/t"
- "call __down_failed/n"
- "2:"
- :"+m" (sem->count)
- :
- :"memory","ax");
- }
- static inline void up(struct semaphore * sem)
- {
- __asm__ __volatile__(
- "# atomic up operation/n/t"
- LOCK_PREFIX "incl %0/n/t"
- "jg 1f/n/t"
- "lea %0,%%eax/n/t"
- "call __up_wakeup/n"
- "1:"
- :"+m" (sem->count)
- :
- :"memory","ax");
- }
I can't find arch/i386/kernel/semaphore.c.
静态声明一个信号量:
- #define __SEMAPHORE_INITIALIZER(name, n) /
- { /
- .count = ATOMIC_INIT(n), /
- .sleepers = 0, /
- .wait = __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) /
- }
- #define __DECLARE_SEMAPHORE_GENERIC(name,count) /
- struct semaphore name = __SEMAPHORE_INITIALIZER(name,count)
- //静态创建互斥信号量
- #define DECLARE_MUTEX(name) __DECLARE_SEMAPHORE_GENERIC(name,1)
- #define DECLARE_MUTEX_LOCKED(name) __DECLARE_SEMAPHORE_GENERIC(name,0)
也可以动态的创建信号量(假设信号量作为一个大的数据结构被动态创建,此时你已经有了一个指向信号量的指针):
- static inline void sema_init (struct semaphore *sem, int val)
- {
- atomic_set(&sem->count, val);
- sem->sleepers = 0;
- init_waitqueue_head(&sem->wait);
- }
- //动态创建互斥信号量
- static inline void init_MUTEX (struct semaphore *sem)
- {
- sema_init(sem, 1);
- }
- static inline void init_MUTEX_LOCKED (struct semaphore *sem)
- {
- sema_init(sem, 0);
- }
函数down_interrruptible()试图获取指定的信号量,如果获取失败,它将以TASK_INTERRUPTIBLE状态进入睡眠。如果接收到了信号,那么该进程就会被唤醒,而该函数返回-EINTR。而down()函数会让进程在TASK_UNINTERRUPTIBLE状态下睡眠。使用down_trylock()函数以堵塞的费方式来获得指定的信号量。要释放指定的信号量使用up()函数。
- static inline int down_interruptible(struct semaphore * sem)
- {
- int result;
- might_sleep();
- __asm__ __volatile__(
- "# atomic interruptible down operation/n/t"
- "xorl %0,%0/n/t"
- LOCK_PREFIX "decl %1/n/t"
- "jns 2f/n/t"
- "lea %1,%%eax/n/t"
- "call __down_failed_interruptible/n"
- "2:"
- :"=&a" (result), "+m" (sem->count)
- :
- :"memory");
- return result;
- }
- static inline int down_trylock(struct semaphore * sem)
- {
- int result;
- __asm__ __volatile__(
- "# atomic interruptible down operation/n/t"
- "xorl %0,%0/n/t"
- LOCK_PREFIX "decl %1/n/t"
- "jns 2f/n/t"
- "lea %1,%%eax/n/t"
- "call __down_failed_trylock/n/t"
- "2:/n"
- :"=&a" (result), "+m" (sem->count)
- :
- :"memory");
- return result;
- }
希望发表的时候不要出错(一_一)