一、Linux 锁的介绍
Linux中按照大类分为2种锁,睡眠锁和自旋锁。其中睡眠锁就是无法获得锁的时候,当前线程进入休眠状态,包括信号量semaphore、互斥锁mutex。自旋锁就是当无法获得锁时,不会休眠,一直循环等待,只有一种spinlock。
自旋锁 spinlock
spinlock 函数在内核文件 include\linux\spinlock.h 中声明,如下表
信号量 semaphore
semaphore 函数在内核文件 include\linux\semaphore.h 中声明,如下表:
函数名 | 作用 |
---|---|
DEFINE_SEMAPHORE(name) | 定义一个 struct semaphore name 结构体, count 值设置为 1 |
void sema_init(struct semaphore *sem, int val) | 初始化 semaphore |
void down(struct semaphore *sem) | 获得信号量,如果暂时无法获得就会休眠返回之后就表示肯定获得了信号量在休眠过程中无法被唤醒,即使有信号发给这个进程也不处理 |
int down_trylock(struct semaphore *sem) | 尝试获得信号量,不会休眠,返回值:0:获得了信号量 1:没能获得信号量 |
void up(struct semaphore *sem) | 释放信号量,唤醒其他等待信号量的进程 |
互斥锁 mutex
mutex 函数在内核文件 include\linux\mutex.h 中声明,如下表:
函数名 | 作用 |
---|---|
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_trylock(struct mutex *lock) | 尝试获取 mutex,如果无法获得,不会休眠,返回值:1:获得了 mutex,0:没有获得注意,这个返回值含义跟一般的 mutex 函数相反 |
void mutex_unlock(struct mutex *lock) | 释放 mutex,会唤醒其他等待同一个 mutex 的线程 |
semaphore和mutex的区别
强调一下: mutex 只能在进程上下文中使用,谁给 mutex 加锁,就只能由谁来解锁。semaphore 的锁定与释放,并不限定为同一个进程。
二、各种锁的使用场景
如下图是各个场景下锁的使用:
以上图红色圈为例,介绍下怎么看这张表,红圈表示如果“ IRQ Handler A”和“ Softirq A”要竞争临界资源,那么需要使用“spin_lock_irq()”函数。为什么不能用 spin_lock 而要用 spin_lock_irq?也就是为什么要把中断给关掉?假设在 Softirq A 中获得了临界资源,这时发生了 IRQ A 中断, IRQ Handler A 去尝试获得自旋锁,这就会导致死锁,所以需要关中断。
针对各个场景如上图,我们总结一下:
1、假设只有程序 A、程序 B 会抢占资源,这 2 个程序都是可以休眠的,所以可以使用信号量。
2、在用户上下文与 Softirqs 之间加锁,可以使用 spin_lock_bh 函数,它会先禁止本地 CPU 的中断下半部即 Softirq。
3、在用户上下文与 Tasklet 之间加锁,同“在用户上下文与 Softirqs 之间加锁”完全一样。
4、在用户上下文与 Timer 之间加锁,同“在用户上下文与 Softirqs 之间加锁”完全一样。
5、在 Tasklet 与 Timer 之间加锁,可以使用 spin_lock()、spin_unlock()来保护临界资源。不需要用 spin_lock_bh(),因为一旦当前 CPU 已经处于 Tasklet 或 Timer中,同一个 CPU 不会同时再执行其他 Tasklet 或 Timer。
6、在 Softirq 之间加锁,可以使用 spin_lock()、 spin_unlock()来访问临界区,原因同上。
7、硬件中断服务例程与一个 Softirq共享数据,在 Softirq 获得锁之前,禁止当前 CPU 的中断;在硬件中断服务例程中不需要使用 spin_lock_irq(),因为当它在执行的时间 Softirq 是不可能执行的,它可以使用 spin_lock()用来防止别的 CPU 抢占。
8、硬件中断 A、硬件中断 B 都要访问临界资源,使用 spin_lock()实现互斥访问临界资源就可以了。因为 Linux 不支持中断嵌套,即当前 CPU 正在处理中断 A 时,中断 B 不可能在当前 CPU 上被处理,不需要再次去禁止中断。
9、spin_lock用于阻止在不同CPU上的执行单元对共享资源的同时访问以及不同进程上下文互相抢占导致的对共享资源的非同步访问,而中断失效和软中断失效却是为了阻止在同一CPU上软中断或中断对共享资源的非同步访问。
本文主要摘录自韦东山韦老师的《嵌入式Linux应用开发完全手册_韦东山全系列视频文档全集V2.8》百问网嵌入式资料下载