Linux内核互斥锁的实现方式
自旋锁
自旋锁是一个忙等锁,如果获取不到锁就会一直等待。
如下为忙等锁的实现方式,使用了一个原子变量(原子操作的实现和硬件有关)来实现:
#include <atomic>
class SpinLock {
private:
std::atomic_flag flag;
public:
SpinLock() : flag(ATOMIC_FLAG_INIT) {
}
void lock() {
while (flag.test_and_set(std::memory_order_acquire)) {
// 自旋等待锁释放
}
}
void unlock() {
flag.clear(std::memory_order_release);
}
};
互斥锁
互斥锁是一个睡眠锁,获取不到则线程就会睡眠引起任务调度,互斥锁可以使用信号量实现也可以用自旋锁来实现,下面将会对用自旋锁实现互斥量的方式展开讲。
1.互斥锁的数据结构
struct mutex {
atomic_t count; //引用计数器,1: 所可以利用,小于等于0:该锁已被获取,需要等待
spinlock_t wait_lock;//自旋锁类型,保证多cpu下,对等待队列访问是安全的。
struct list_head wait_list; //等待队列,如果该锁被获取,任务将挂在此队列上,等待调度。
};
如上所示,互斥锁的数据结构主要有三个变量构成:
- count:用来记录目前锁的状态 1: 所可以利用,小于等于0:该锁已被获取,需要等待。
- wait_lock:自旋锁,操作等待队列的时候会使用自旋锁将等待队列保护起来。
- wait_list:因为获取不到这个锁而阻塞的进程。
2.加锁函数接口
void inline __sched mutex_lock(struct mutex *lock)
实际上是调用了一个宏:
__mutex_fastpath_lock(&lock->count, __mutex_lock_slowpath);
宏的定义:将mutex数据结构中,引用计数器减1,如果不为负数就返回,如果为负数,需要调用函数:__mutex_lock_slowpath,接下来我们再来分析这个函数,我们先来分析一下这个宏。
#define __mutex_fastpath_lock(count, fail_fn)
do {
unsigned int dummy;/
; 检查参数类型的有效性
typecheck(atomic_t *, count); /
typecheck_fn(void (*)(atomic_t *), fail_fn