Release log:
2021-05-07 五: 完成初版
2021-05-10 一: 添加自旋锁和信号量的对比
原文地址
Linux 内核中自旋锁的使用
在 Linux 内核中,要使用自旋锁需要包含的文件
<linux/spinlock.h>
,锁的数据类型为
spinlock_t
。
自旋锁的声明以及初始化
void spin_lock_init(spinlock_t *lock);
自旋锁的获取以及释放
void spin_lock(spinlock_t *lock);
void spin_unlock(spinlock_t *lock);
注意: 所有自旋锁等待在本质上是不可中断的
使用自旋锁的核心规则
- 任何拥有自旋锁的代码都必须是原子的。它不能休眠,事实上,它不能因为任何原因放弃处理器,除了服务中断以外(某些情况下此时也不能放弃处理器)
- 内核抢占的情况由自旋锁代码本身处理。任何时候,只要内核代码拥有自旋锁,在相关处理器上的抢占就会被禁止
- 在拥有锁的时候避免休眠有时候很难做到,当我们编写需要自旋锁下执行的代码时,必须注意每一个所调用的函数
- 拥有锁的时间越短越好
自旋锁函数
锁定一个自旋锁的函数
void spin_lock(spinlock_t *lock);
/* 这是一个宏,获得自旋锁之前禁止中断(只在本地处理器上),而先前的中断状态保存在 flags 中 */
void spin_lock_irqsave(spinlock_t *lock, unsigned long flags);
void spin_lock_irq(spinlock_t *lock);
/* 在获得锁之前禁止软件中断,但是会让硬件中断保持打开 */
void spin_lock_bh(spinlock_t *lock);
如果我们有一个自旋锁,它可以被运行在(硬件或软件)中断上下文中的代码获得,则必须使用某个禁止中断的 spin_lock 形式
释放自旋锁的方法需要严格对应
void spin_unlock(spinlock_t *lock);
void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags);
void spin_unlock_irq(spinlock_t *lock);
void spin_unlock_bh(spinlock_t *lock);
另外,还有如下非阻塞的自旋锁操作
int spin_trylock(spinlock_t *lock);
int spin_trylock_bh(spinlock_t *lock);
读取者/写入者自旋锁
读取者/写入者锁的类型为 rwlock_t,在
<linux/spinlock.h>
中定义
自旋锁的初始化
void rwlock_init(rwlock_t *lock);
读取者对应的函数(这里没有 read_trylock 可用)
void read_lock(rwlock_t *lock);
void read_lock_irqsave(rwlock_t *lock, unsigned long flags);
void read_lock_irq(rwlock_t *lock);
void read_lock_bh(rwlock_t *lock);
void read_unlock(rwlock_t *lock);
void read_unlock_irqrestore(rwlock_t *lock, unsigned long flags);
void read_unlock_irq(rwlock_t *lock);
void read_unlock_bh(rwlock_t *lock);
写入者对应的函数
void write_lock(rwlock_t *lock);
void write_lock_irqsave(rwlock_t *lock, unsigned long flags);
void write_lock_irq(rwlock_t *lock);
void write_lock_bh(rwlock_t *lock);
void write_trylock(rwlock_t *lock);
void write_unlock(rwlock_t *lock);
void write_unlock_irqrestore(rwlock_t *lock, unsigned long flags);
void write_unlock_irq(rwlock_t *lock);
void write_unlock_bh(rwlock_t *lock);
和 rwsem 类似,读取者/写入者锁可能造成读取者饥饿
对比信号量与自旋锁
自旋锁可以在不能休眠的代码中使用,比如中断处理例程。自旋锁如果被其他人获得,则代码进入忙循环并重复检查这个锁,直到该锁可用为止。信号量是一种睡眠锁,如果一个任务试图获得一个已被持有的信号量时,信号量会将其推入等待队列,然后让其睡眠。信号量不能在中断上下文中被调度
自旋锁会关闭抢占,而信号量不会
在正确使用的情况下,自旋锁通常可以提供比信号量更高的性能
需求 建议的加锁方式
低开销加锁 优先使用自旋锁
短期锁定 优先使用自旋锁
中断上下文中加锁 使用自旋锁
长期加锁 优先使用信号量
持有锁是需要睡眠、调度 使用信号量