简介
内核中的锁大致分为两类.
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。
最复杂的是, 内核普通函数和中断下半段处理函数之间的竞争。