概念
一个信号量本质是一个整数值,它和一堆函数联合使用,这一对函数通常称为P和V;希望进入临界区的进程将在相关信号量上调用P;如果信号量的值大于零,则该值会减少1,进程可以继续执行;相反,如果信号量的值为0或者更小,则进程必须等待知道其他人释放该信号量;对信号量的解锁通过调用V完成;该函数增加信号量的值,并在必要时唤醒等待的进程;
当信号量用于互斥时(即避免多个进程同时在一个临界区中运行),信号量 的值应该初始化为1;这种信号量在任何给定时刻只能由单个进程或者线程拥有;这种使用模式下,一个信号量有时也成为一个互斥体(mutex),它是互斥的简称;
Linux内核汇总几乎所有的信号量均用于互斥体;
信号量的使用
Linux内核遵守上述语义提供了信号量的实现,要使用信号量,内核代码必须包含<linux/semaphore.h>,相关的类型是struct semaphore;
实际的信号量可以通过集中途径来生命和初始化;
直接创建信号量,其中val参数是赋予一个信号量的初始值;
1 static inline void sema_init(struct semaphore *sem, int val)
Linux中P函数被称为down–或者这个名字的变种;该类函数减小了信号量的值,它也许会将调用者置于休眠状态,然后等待信号量变为可用,之后授予调用者对保护资源的访问;作为通常规则,我们不应该使用非中断的操作;down的几个变种函数都有返回值,需要始终进行检查;
Linux中V函数被称为up,该函数增加了信号量的值,使用该函数后,调用者不再拥有该信号量;任何down操作都需要对应进行up操作,特别注意在错误分支中对已持有信号量的释放;
1 void down(struct semaphore *sem); 2 int __must_check down_interruptible(struct semaphore *sem); 3 int __must_check down_killable(struct semaphore *sem); 4 int __must_check down_trylock(struct semaphore *sem); 5 int __must_check down_timeout(struct semaphore *sem, long jiffies); 6 void up(struct semaphore *sem);
读写信号量的使用
许多任务可以划分成两种不同的工作类型:一些任务只需要读取受保护的数据结构,而其他的则必须做出修改;允许多个并发读取是可能的,只要它们中没有哪个要做修改;这样做可以大大的提高性能,以内容只读任务可以并行的完成它们的工作,而不需要等待其他读取者退出临界区;
内核中为这种情形提供了一种特殊的信号量类型,rwsem,虽然使用比较少,但偶尔也比较有用;
使用rwsem必须包含头文件<linux/rwsem.h>,对应的数据类型是struct rw_semaphore;
在使用之前需要使用init_rwsem宏进行初始化;
1 #define init_rwsem(sem) \ 2 do { \ 3 static struct lock_class_key __key; \ 4 \ 5 __init_rwsem((sem), #sem, &__key); \ 6 } while (0)
down_read系列函数提供了对保护资源的只读访问,可以和其他读取者并发的访问;down_write则提供了对保护资源的写访问,与其他读写着互斥;一个rwsem允许一个写入者和多个读取者拥有该信号量;up_xxx操作则用于释放已经持有的信号量;
1 /* 2 * lock for reading 3 */ 4 extern void down_read(struct rw_semaphore *sem); 5 6 /* 7 * trylock for reading -- returns 1 if successful, 0 if contention 8 */ 9 extern int down_read_trylock(struct rw_semaphore *sem); 10 11 /* 12 * lock for writing 13 */ 14 extern void down_write(struct rw_semaphore *sem); 15 extern int __must_check down_write_killable(struct rw_semaphore *sem); 16 17 /* 18 * trylock for writing -- returns 1 if successful, 0 if contention 19 */ 20 extern int down_write_trylock(struct rw_semaphore *sem); 21 22 /* 23 * release a read lock 24 */ 25 extern void up_read(struct rw_semaphore *sem); 26 27 /* 28 * release a write lock 29 */ 30 extern void up_write(struct rw_semaphore *sem); 31 32 /* 33 * downgrade write lock to read lock 34 */ 35 extern void downgrade_write(struct rw_semaphore *sem);
互斥体的使用
Linux内核互斥体之前是以val为1的信号量存在的,现在已经单独实现;使用互斥体需要包含<linux/mutex.h>头文件;
mutex_init宏完成对互斥量的初始化;
1 #define mutex_init(mutex) \ 2 do { \ 3 static struct lock_class_key __key; \ 4 \ 5 __mutex_init((mutex), #mutex, &__key); \ 6 } while (0)
其中包含了一些列的lock与unlock操作,如下所示,其中mutex_lock_xxx表示在进入临界区之前加锁的操作,mutex_unlock操作表示退出临界区的解锁的操作;
1 extern void mutex_lock(struct mutex *lock); 2 extern int __must_check mutex_lock_interruptible(struct mutex *lock); 3 extern int __must_check mutex_lock_killable(struct mutex *lock); 4 extern void mutex_lock_io(struct mutex *lock); 5 6 # define mutex_lock_nested(lock, subclass) mutex_lock(lock) 7 # define mutex_lock_interruptible_nested(lock, subclass) mutex_lock_interruptible(lock) 8 # define mutex_lock_killable_nested(lock, subclass) mutex_lock_killable(lock) 9 # define mutex_lock_nest_lock(lock, nest_lock) mutex_lock(lock) 10 # define mutex_lock_io_nested(lock, subclass) mutex_lock(lock) 11 #endif 12 13 /* 14 * NOTE: mutex_trylock() follows the spin_trylock() convention, 15 * not the down_trylock() convention! 16 * 17 * Returns 1 if the mutex has been acquired successfully, and 0 on contention. 18 */ 19 extern int mutex_trylock(struct mutex *lock); 20 extern void mutex_unlock(struct mutex *lock); 21 22 extern int atomic_dec_and_mutex_lock(atomic_t *cnt, struct mutex *lock);