Linux内核深度解析之内核互斥技术——自旋锁

自旋锁

自旋锁用于处理器之间的互斥,适合保护很短的临界区,并且不允许在临界区睡眠。申请自旋锁的时候,如果自旋锁被其他处理器占有,本处理器自旋等待(也称为忙等待)。

进程、软中断和硬中断都可以使用自旋锁。

目前内核的自旋锁是排队自旋锁(queued spinlock,也称为“FIFO ticket spinlock”),算法类似于银行柜台的排队叫号。

自旋锁的定义:

include/linux/spinlock_types.h
typedef struct raw_spinlock {
	arch_spinlock_t raw_lock;
    ...
} raw_spinlock_t;

typedef struct spinlock {
	union {
		struct raw_spinlock rlock;
        ...
	};
} spinlock_t;

可以看到,数据类型spinlock对数据类型raw_spinlock做了封装,spinlock和raw_spinlock(原始自旋锁)有什么关系呢?

Linux内核有一个实时内核分支(开启配置宏CONFIG_PREEMPT_RT)来支持硬实时特性,内核主线只支持软实时。

对于没有打上实时内核补丁的内核,spinlock只是封装raw_spinlock,它们完全一样。如果打上实时内核补丁,那么spinlock使用实时互斥锁保护临界区,在临界区内可以被抢占和睡眠,但raw_spinlock还是自旋锁。

目前主线版本还没有合并实时内核补丁,说不定哪天就会合并进来,为了使代码可以兼容实时内核,最好坚持3个原则:

(1)尽可能使用spinlock;

(2)绝对不允许被抢占和睡眠的地方,使用raw_spinlock,否则使用spinlock;

(3)如果临界区足够小,使用raw_spinlock。

各种处理器架构需要自定义数据类型arch_spinlock_t,ARM64架构的定义:

arch/arm64/include/asm/spinlock_types.h
#include <asm-generic/qspinlock_types.h>

include/asm-generic/qspinlock_types.h
typedef struct qspinlock {
	union {
		atomic_t val;

		/*
		 * By using the whole 2nd least significant byte for the
		 * pending bit, we can allow better optimization of the lock
		 * acquisition for the pending bit holder.
		 */
#ifdef __LITTLE_ENDIAN		/* 小端格式 */
		struct {
			u8	locked;
			u8	pending;
		};
		struct {
			u16	locked_pending;
			u16	tail;
		};
#else						/* 大端格式 */
		struct {
			u16	tail;
			u16	locked_pending;
		};
		struct {
			u8	reserved[2];
			u8	pending;
			u8	locked;
		};
#endif
	};
} arch_spinlock_t;

定义并且初始化静态自旋锁的方法:

DEFINE_SPINLOCK(x)

在运行时动态初始化自旋锁的方法:

spin_lock_init(_lock)

申请自旋锁的函数:

(1)申请自旋锁,如果锁被其他处理器占有,当前处理器自旋等待

void spin_lock(spinlock_t *lock);

(2)申请自旋锁,并且禁止当前处理器的软中断

void spin_lock_bh(spinlock_t *lock);

(3)申请自旋锁,并且禁止当前处理器的硬中断

void spin_lock_irq(spinlock_t *lock);

(4)申请自旋锁,保存当前处理器的硬中断状态,并且禁止当前处理器的硬中断

spin_lock_irqsave(lock, flags)

(4)申请自旋锁,如果申请成功,返回1;如果锁被其他处理器占有,当前处理器不等待,立即返回0

int spin_trylock(spinlock_t *lock);

释放自旋锁的函数:

(1)释放自旋锁

void spin_unlock(spinlock_t *lock);

(2)释放自旋锁,并且开启当前处理器的软中断

void spin_unlock_bh(spinlock_t *lock);

(3)释放自旋锁,并且开启当前处理器的硬中断

void spin_unlock_irq(spinlock_t *lock);

(4)释放自旋锁,并且恢复当前处理器的硬中断状态

void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags);

定义并且初始化静态原始自旋锁的方法:

DEFINE_RAW_SPINLOCK(x)

在运行时动态初始化原始自旋锁的方法:

raw_spin_lock_init(lock)

申请原始自旋锁的函数:

(1)申请原始自旋锁,如果锁被其他处理器占有,当前处理器自旋等待

raw_spin_lock(lock)

(2)申请原始自旋锁,并且禁止当前处理器的软中断

raw_spin_lock_bh(lock)

(3)申请原始自旋锁,并且禁止当前处理器的硬中断

raw_spin_lock_irq(lock)

(4)申请原始自旋锁,保存当前处理器的硬中断状态,并且禁止当前处理器的硬中断

raw_spin_lock_irqsave(lock, flags)

(5)申请原始自旋锁,如果申请成功,返回1;如果锁被其他处理器占有,当前处理器不等待,立即返回0

raw_spin_trylock(lock)

释放原始自旋锁的函数:

(1)释放原始自旋锁

raw_spin_unlock(lock)

(2)释放原始自旋锁,并且开启当前处理器的软中断

raw_spin_unlock_bh(lock)

(3)释放原始自旋锁,并且开启当前处理器的硬中断

raw_spin_unlock_irq(lock)

(4)释放原始自旋锁,并且恢复当前处理器的硬中断状态

raw_spin_unlock_irqrestore(lock, flags)

猜你喜欢

转载自blog.csdn.net/linuxweiyh/article/details/106961972