linux中的信号量是sleeping lock。当尝试获得的锁当前被占用时,它把task放入一个等待队列,然后把task置于睡眠状态。当信号量变成可用时,唤醒task,并获得信号量。
信号量通常用在锁占用很长时间的地方。
相反,一个短时间的锁,用信号量开销很大。因为要睡眠、维护等待队列、唤醒等。
由于要睡眠,因此信号量只能用在进程上下文。
可以在获取信号量之后睡眠(尽管可能不是你想要的),而不会发生死锁。
信号量不会禁用抢占。
信号量同时允许的持有者数量在声明信号量时指定,这个值被称为使用者数量或者简称为数量。
通常情况下,只允许一个锁持有者,count等于1,这种信号量叫做binary semaphore,或者叫做mutex。
大于1,叫做counting semaphores。但是这种在内核中不常用。
信号量支持两个原子操作:P() 和 V(),后面的系统叫做 down() 和 up()。down() 用来获得信号量,通过给计数减1。up() 用来释放信号量。
信号量的实现和架构相关,头文件 <asm/semaphore.h>,类型:struct semaphore。
定义并初始化
1 2 | struct semaphore name; sema_init(&name, count); |
定义并初始化一个binary semaphore
1 | static DECLARE_MUTEX(name); |
初始化一个动态创建的mutex,可以使用
1 | init_MUTEX(sem); |
down_interruptible() 尝试获得信号量,如果信号量不可用,它将进程睡眠到TASK_INTERRUPTIBLE状态。当信号发生时,函数返回-EINTR。
down() 则睡眠成不可中断状态。
典型的用法
1 2 3 4 5 6 7 8 9 10 11 | /* define and declare a semaphore, named mr_sem, with a count of one */ static DECLARE_MUTEX(mr_sem); /* attempt to acquire the semaphore ... */ if (down_interruptible(&mr_sem)) { /* signal received, semaphore not acquired ... */ } /* critical region ... */ /* release the given semaphore */ up(&mr_sem); |
Semaphore Methods
Method | Desc |
sema_init(struct semaphore *, int) | |
init_MUTEX(struct semaphore *) | |
init_MUTEX_LOCKED(struct semaphore *) | |
down_interruptible (struct semaphore *) | |
down(struct semaphore *) | |
down_trylock(struct semaphore *) | |
up(struct semaphore *) |
类型 struct rw_semaphore,头文件<linux/rwsem.h>
定义并初始化
1 | static DECLARE_RWSEM(name); |
动态初始化
1 | init_rwsem( struct rw_semaphore *sem) |
所有的读写信号量都是mutex。count为1。使用示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | static DECLARE_RWSEM(mr_rwsem); /* attempt to acquire the semaphore for reading ... */ down_read(&mr_rwsem); /* critical region (read only) ... */ /* release the semaphore */ up_read(&mr_rwsem); /* attempt to acquire the semaphore for writing ... */ down_write(&mr_rwsem); /* critical region (read and write) ... */ /* release the semaphore */ up_write(&mr_sem); |
还有down_read_trylock()和down_write_try_lock()。读写信号量还支持将写锁变成一个读锁:downgrade_write()
结构struct mutex,它和计数为1的信号量类似,但是接口更简单,效率更高,但有一些额外的约束。
静态声明
1 | DEFINE_MUTEX(name); |
动态初始化
1 | mutex_init(&mutex); |
上锁和解锁
1 2 3 | mutex_lock(&mutex); /* critical region ... */ mutex_unlock(&mutex); |
Mutex Methods
mutex_lock(struct mutex *) | |
mutex_unlock(struct mutex *) | |
mutex_trylock(struct mutex *) | |
mutex_is_locked (struct mutex *) |
互斥体有一些约束
谁加锁,谁就必须解锁。不能在一个context加锁,在另一个context解锁。
不允许迭代加锁和解锁
持有互斥体时,进程不能退出
中断和下半部不能使用mutex
mutex只能通过官方API管理,不能拷贝、手动初始化、重初始化等。