一、自旋锁用来干什么
If you have understood Mutex then Spinlock is also similar. Both are used to protect a shared resource from being modified by two or more processes simultaneously.
二、Linux kernel中spinlock的需求场景
If the kernel is running on a uniprocessor and CONFIG_SMP, CONFIG_PREEMPT aren’t enabled while compiling the kernel then spinlock will not be available. Because there is no reason to have a lock when no one else can run at the same time.
But if you have disabled CONFIG_SMP and enabled CONFIG_PREEMPT then spinlock will simply disable preemption, which is sufficient to prevent any races.
也就是说,spinlock只适用于开多核和开调度器抢占的情况。这里就引出自旋锁互斥的三个要点:
1. 自旋锁互斥包括:多核并发互斥 + 单核中断互斥 + 单核抢占互斥;
2. 获取自旋锁之前关中断,目的就是为了避免单核上中断导致的竞争条件;
3. 获取自旋锁之前关抢占,目的就是为了避免单核上抢占导致的竞争条件;
三、自旋锁本尊以及自旋锁的多种变形
(1)、本尊
spin_lock(lock); /* 死等一个原子变量值有效,其本身是可以被中断打断以及被进程调度抢占的 */
(2)、多种变形
#define spin_lock_irqsave(lock, flags) do { local_irq_save(flags); spin_lock(lock); } while (0)
#define spin_lock_irq(lock) do { local_irq_disable(); spin_lock(lock); } while (0)
#define spin_lock_bh(lock) do { local_bh_disable(); spin_lock(lock); } while (0)
#define read_lock_irqsave(lock, flags) do { local_irq_save(flags); read_lock(lock); } while (0)
#define read_lock_irq(lock) do { local_irq_disable(); read_lock(lock); } while (0)
#define read_lock_bh(lock) do { local_bh_disable(); read_lock(lock); } while (0)
#define write_lock_irqsave(lock, flags) do { local_irq_save(flags); write_lock(lock); } while (0)
#define write_lock_irq(lock) do { local_irq_disable(); write_lock(lock); } while (0)
#define write_lock_bh(lock) do { local_bh_disable(); write_lock(lock); } while (0)
#define spin_unlock_irqrestore(lock, flags) do { spin_unlock(lock); local_irq_restore(flags); } while (0)
#define spin_unlock_irq(lock) do { spin_unlock(lock); local_irq_enable(); } while (0)
#define spin_unlock_bh(lock) do { spin_unlock(lock); local_bh_enable(); } while (0)
#define read_unlock_irqrestore(lock, flags) do { read_unlock(lock); local_irq_restore(flags); } while (0)
#define read_unlock_irq(lock) do { read_unlock(lock); local_irq_enable(); } while (0)
#define read_unlock_bh(lock) do { read_unlock(lock); local_bh_enable(); } while (0)
#define write_unlock_irqrestore(lock, flags) do { write_unlock(lock); local_irq_restore(flags); } while (0)
#define write_unlock_irq(lock) do { write_unlock(lock); local_irq_enable(); } while (0)
#define write_unlock_bh(lock) do { write_unlock(lock); local_bh_enable(); } while (0)
(3)、local_irq_save与local_irq_disable的区别?
local_irq_save是既关闭中断,也保留CPU状态寄存器,以用于恢复之前状态
local_irq_disable只关闭状态,不保留CPU状态寄存器,之后不会恢复之前的CPU状态
四、问题:spin_lock_irqsave关中断后,为什么要再禁止抢占呢,不多余吗?
static inline unsigned long __raw_spin_lock_irqsave(raw_spinlock_t *lock)
{
unsigned long flags;
local_irq_save(flags); /* 第一步:关硬中断 */
preempt_disable(); /* 第二步:关抢占 */
spin_acquire(&lock->dep_map, 0, 0, _RET_IP_); /* 第三步:获取lock */
......
}
如果不禁止内核抢占,可能会有以下的情况发生(假设进程B比进程A具有更高的优先级):
a. 进程A获得spinlock lock
b. 进程B运行(抢占进程A)
c. 进程B获取spinlock lock
由于进程B比进程A优先级高,所以进程B在进程A之前运行,而进程B需要进程A释放lock之后才能运行,于是,死锁。
所以,禁止中断并不能完全禁止抢占。
五、spinlock应用场景和案例
1.Approach 1 (Locking between User context) ——If you share data with user context (between Kernel Threads), then you can use this approach.
加锁: spin_lock(spinlock_t *lock) ——
This will take the lock if it is free, otherwise, it’ll spin until that lock is free (Keep trying).
尝试加锁:spin_trylock(spinlock_t *lock) ——
Locks the spinlock if it is not already loc