简介
内核中的锁大致分为两类.
| mutex | mutual exclusion,彼此排斥,即互斥锁 |
|---|---|
| rt_mutex | |
| semaphore | 信号量、旗语 |
| rw_semaphore | 读写信号量,读写互斥,但是可以多人同时读 |
| ww_mutex | |
| percpu_rw_semaphore | 对rw_semaphore的改进,性能更优 |
锁的函数讲解
自旋锁
# 加锁void spin_lock(spinlock_t *lock);void spin_lock_bh(spinlock_t *lock); # 加锁时禁止下半部(软中断),解锁时使能下半部(软中断)void spin_lock_irq(spinlock_t *lock); # 加锁时禁止中断,解锁时使能中断spin_lock_irqsave(lock, flags); # 加锁时禁止并中断并记录状态,解锁时恢复中断为所记录的状态# 解锁void spin_unlock(spinlock_t *lock);void spin_unlock_bh(spinlock_t *lock);void spin_unlock_irq(spinlock_t *lock);void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags)# 初始化操作spin_lock_init(_lock)# 尝试获取锁int spin_trylock(spinlock_t *lock)# 获取锁的状态int spin_is_locked(spinlock_t *lock)
信号量
| 函数名 | 作用 |
|---|---|
| DEFINE_SEMAPHORE(name) | 定义一个struct semaphore name结构体, count值设置为1 |
| void sema_init(struct semaphore *sem, int val) | 初始化semaphore |
| void down(struct semaphore *sem) | 获得信号量,如果暂时无法获得就会休眠 返回之后就表示肯定获得了信号量 在休眠过程中无法被唤醒, 即使有信号发给这个进程也不处理 |
| int down_interruptible(struct semaphore *sem) | 获得信号量,如果暂时无法获得就会休眠, 休眠过程有可能收到信号而被唤醒, 要判断返回值: 0:获得了信号量 -EINTR:被信号打断 |
| int down_killable(struct semaphore *sem) | 跟down_interruptible类似, down_interruptible可以被任意信号唤醒, 但down_killable只能被“fatal signal”唤醒, 返回值: 0:获得了信号量 -EINTR:被信号打断 |
| int down_trylock(struct semaphore *sem) | 尝试获得信号量,不会休眠, 返回值: 0:获得了信号量 1:没能获得信号量 |
| int down_timeout(struct semaphore *sem, long jiffies) | 获得信号量,如果不成功,休眠一段时间 返回值: 0:获得了信号量 -ETIME:这段时间内没能获取信号量,超时返回 down_timeout休眠过程中,它不会被信号唤醒 |
| void up(struct semaphore *sem) | 释放信号量,唤醒其他等待信号量的进程 |
互斥量mutex
| 函数名 | 作用 |
|---|---|
| mutex_init(mutex) | 初始化一个struct mutex指针 |
| DEFINE_MUTEX(mutexname) | 初始化struct mutex mutexname |
| int mutex_is_locked(struct mutex *lock) | 判断mutex的状态 1:被锁了(locked) 0:没有被锁 |
| void mutex_lock(struct mutex *lock) | 获得mutex,如果暂时无法获得,休眠 返回之时必定是已经获得了mutex |
| int mutex_lock_interruptible(struct mutex *lock) | 获得mutex,如果暂时无法获得,休眠; 休眠过程中可以被信号唤醒, 返回值: 0:成功获得了mutex -EINTR:被信号唤醒了 |
| int mutex_lock_killable(struct mutex *lock) | 跟mutex_lock_interruptible类似, mutex_lock_interruptible可以被任意信号唤醒, 但mutex_lock_killable只能被“fatal signal”唤醒, 返回值: 0:获得了mutex -EINTR:被信号打断 |
| int mutex_trylock(struct mutex *lock) | 尝试获取mutex,如果无法获得,不会休眠, 返回值: 1:获得了mutex, 0:没有获得 注意,这个返回值含义跟一般的mutex函数相反, |
| void mutex_unlock(struct mutex *lock) | 释放mutex,会唤醒其他等待同一个mutex的线程 |
| int atomic_dec_and_mutex_lock(atomic_t cnt, struct mutex lock) | 让原子变量的值减1, 如果减1后等于0,则获取mutex, 返回值: 1:原子变量等于0并且获得了mutex 0:原子变量减1后并不等于0,没有获得mutex |
互斥量和信号量之间的区别
semaphore中可以指定count为任意值,比如有10个厕所,所以10个人都可以使用厕所。而mutex的值只能设置为1或0,只有一个厕所。
换句话说,信号量可以表示有多少资源.
| semaphore | mutex | |
|---|---|---|
| 几把锁 | 任意,可设置 | 1 |
| 谁能解锁 | 别的程序、中断等都可以 | 谁加锁,就得由谁解锁 |
| 多次解锁 | 可以 | 不可以,因为只有1把锁 |
| 循环加锁 | 可以 | 不可以,因为只有1把锁 |
| 任务在持有锁的期间可否退出 | 可以 | 不建议,容易导致死锁 |
| 硬件中断、软件中断上下文中使用 | 可以 | 不可以 |
何时使用何种锁
参考 Unreliable Guide To Locking 内核锁指南
举例简单介绍一下,上表中第一行“IRQ Handler A”和第一列“Softirq A”的交叉点是“spin_lock_irq()”,意思就是说如果“IRQ Handler A”和“Softirq A”要竞争临界资源,那么需要使用“spin_lock_irq()”函数。为什么不能用spin_lock而要用spin_lock_irq?也就是为什么要把中断给关掉?假设在Softirq A中获得了临界资源,这时发生了IRQ A中断,IRQ Handler A去尝试获得自旋锁,这就会导致死锁:所以需要关中断。
按照上面的表格小结一下。
Softirq 之间的竞争使用 spin_lock。
Softirq 和 IRQ Handler 之间的竞争使用 spin_lock_irq。
IRQ Handler 之间的竞争使用 spin_lock_irqsave。
IRQ Handler 和 Tasklet 之间的竞争使用 spin_lock_irq。
最复杂的是, 内核普通函数和中断下半段处理函数之间的竞争。
