参考资料:
https://www.kernel.org/doc/html/latest/locking/index.html
https://mirrors.edge.kernel.org/pub/linux/kernel/people/rusty/kernel-locking/
锁的类型
Linux内核提供了很多类型的锁,它们可以分为两类
- 自旋锁(Spinning locks)
- 睡眠锁(Sleeping locks)
自旋锁类型
简单地说就是无法获得锁时,不会休眠,会一直循环等待,有这些自旋锁:
自旋锁 | 描述 |
---|---|
raw_spinlock_t | 原始自旋锁 |
bit spinlocks | 位自旋锁 |
自旋锁加锁、解锁函数是:spin_lock、spin_unlock,还可以加上各种后缀,后缀表示加锁和解锁会做额外地事:
后缀 | 描述 |
---|---|
_bh() | 加锁时禁止下半部(软中断),解锁时使能下半部(软中断) |
_irq() | 加锁时禁止中断,解锁时使能中断 |
_irqsave/restore() | 加锁时禁止并记录状态,解锁时恢复中断位所记录的状态 |
睡眠锁类型
睡眠锁无法获得锁时,会将当前线程休眠。
休眠锁 | 描述 |
---|---|
mutex | mutual exclusion,彼此排斥,即互斥锁(后面讲解) |
rt_mutex | |
semaphore | 信号量、旗语 |
rw_semaphore | 读写信号量,读写互斥,但是可以多人同时读 |
ww_mutex | |
precpu_rw_semaphore | 对rw_semaphore的改进,性能更优 |
自旋锁具体实现
spinlock函数在内核文件include\linux\spinlock.h中声明,如下表:
休眠锁 | 描述 |
---|---|
spin_lock_init(_lock) | 初始化自旋锁为unlock状态 |
void spin_lock(spinlock_t *lock) | 获取自旋锁(加锁),返回后肯定获得了锁 |
int spin_trylock(spinlock_t *lock) | 尝试获得自旋锁,成功获得锁则返回1,否则返回0 |
void spin_unlock(spinlock_t *lock) | 释放自旋锁,或称解锁 |
int spin_is_locked(spinlock_t *lock) | 返回自旋锁的状态,已加锁返回1,否则返回0 |
自旋锁的加锁、解锁函数是:spin_lock、spin_unlock,还可以加上各种后缀,这表示在加锁或加锁的同时,还会做额外的事情。
信号量semaphore
semaphore函数在内核文件include\linux\semaphore.h中声明,如下表:
函数名 | 作用 |
---|---|
DEFINE_SEMAPHORE(name) | 定义一个struct semaphore name结构体,count值设置为1 |
void sema_init(struct semaphore *sem, int val) | 初始化semaphore |
int down_interruptible(struct semaphore *sem) | 获得信号量,如果暂时无法获得就会休眠, 休眠过程有可能受到信号而被唤醒,要判断返回值: 0:获得了信号量 -EINTER:被信号量打断 |
int down_killable(struct semaphore *sem) | 跟down_interruptible类似,down_interruptible 可以被任意信号唤醒, 但down_killable只能被"final signal"唤醒,返回值: 0:获得了信号量 -EINTR:被信号打断 |
int down_trylock(struct semaphore *sam) | 尝试获得信号量,不会休眠,返回值: 0:获得了信号量 1:没能获得信号量 |
int down_timeout(struct semaphore *sem, long jiffies) | 获得信号量,如果不成功,休眠一段时间返回值: 0:获得了信号量 -ETIME:这段时间内没能获取信号量,超时返回down_timeout休眠过程中,他不会被信号唤醒 |
void up(struct semaphore *sem) | 释放信号量,唤醒其他等待信号量的进程 |
互斥量mutext
mutex函数在内核文件include\linux\mutext.h中声明,如下表:
函数名 | 作用 |
---|---|
mutex_init(mutex) | 初始化一个struct mutext指针 |
DEFINE_MUTEX(mutexname) | 初始化struct mutex mutexname |
int mutex_is_locked(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和mutex区别
semaphore和mutex是不是只是资源个数的区别呢,如果semaphore设置为1,那是不是和mutex一样了呢?
struct mutex {
/* 1: unlocked, 0: locked, negative: locked, possible waiters */
atomic_t count;
spinlock_t wait_lock;
struct list_head wait_list;
#if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_MUTEX_SPIN_ON_OWNER)
struct task_struct *owner; //指向某个线程,只能由加锁的此线程来解锁
#endif
#ifdef CONFIG_MUTEX_SPIN_ON_OWNER
struct optimistic_spin_queue osq; /* Spinner MCS lock */
#endif
#ifdef CONFIG_DEBUG_MUTEXES
void *magic;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
struct lockdep_map dep_map;
#endif
};
- 一个mutex只能在进程上下文中使用,谁给mutex加锁,就只能由谁来解锁。
- sempahore没有这个限制,可以用来解决“读写”问题,A在等待数据获取锁,B产生数据释放锁,释放的同时唤醒A读取数据。(没有进程限定)
何时用何种锁
本节参考:https://wenku.baidu.com/view/26adb3f5f61fb7360b4c656e.html
英文原文:https://mirrors.edge.kernel.org/pub/linux/kernel/people/rusty/kernel-locking/
/ | IRQ Handler A | IRQ Handler B | Softirq A | Softirq B | Tasklet A | Tasklet B | Timer A | Timer B | User Context A | User Context B |
---|---|---|---|---|---|---|---|---|---|---|
IRQ Handler A | None | |||||||||
IRQ Handler B | spin_lock_irqsave() | None | ||||||||
Softirq A | spin_lock_irq() | spin_lock_irq() | spin_lock() | |||||||
Softirq B | spin_lock_irq() | spin_lock_irq() | spin_lock() | spin_lock() | ||||||
Tasklet A | spin_lock_irq() | spin_lock_irq() | spin_lock() | spin_lock() | None | |||||
Tasklet B | spin_lock_irq() | spin_lock_irq() | spin_lock() | spin_lock() | spin_lock() | None | ||||
Timer A | spin_lock_irq() | spin_lock_irq() | spin_lock() | spin_lock() | spin_lock() | spin_lock() | None | |||
Timer B | spin_lock_irq() | spin_lock_irq() | spin_lock() | spin_lock() | spin_lock() | spin_lock() | spin_lock() | None | ||
User Context A | spin_lock_irq() | spin_lock_irq() | spin_lock_bh() | spin_lock_bh() | spin_lock_bh() | spin_lock_bh() | spin_lock_bh() | spin_lock_bh() | None | |
User Context B | spin_lock_irq() | spin_lock_irq() | spin_lock_bh() | spin_lock_bh() | spin_lock_bh() | spin_lock_bh() | spin_lock_bh() | spin_lock_bh() | down_interruptible() | None |
使用场景
本节参考:https://wenku.baidu.com/view/26adb3f5f61fb7360b4c656e.html
英文原文:https://mirrors.edge.kernel.org/pub/linux/kernel/people/rusty/kernel-locking/