kernel version: 2.6.26
linux kernel代码中有很多自旋锁的使用场景,同时存在很多spin_lock的接口定义,那么怎么去区分这些接口的使用场景呢?
首先来看spin_lock的实现:
#define spin_lock(lock) _spin_lock(lock)
void __lockfunc _spin_lock(spinlock_t *lock)
{
preempt_disable();
spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
LOCK_CONTENDED(lock, _raw_spin_trylock, _raw_spin_lock);
}
spin_lock_irq的实现,我们按照smp来看:
#define spin_lock_irq(lock) _spin_lock_irq(lock)
void __lockfunc _spin_lock_irq(spinlock_t *lock)
{
local_irq_disable();
preempt_disable();
spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
LOCK_CONTENDED(lock, _raw_spin_trylock, _raw_spin_lock);
}
spin_lock_irqsave的实现
#define spin_lock_irqsave(lock, flags) flags = _spin_lock_irqsave(lock)
unsigned long __lockfunc _spin_lock_irqsave(spinlock_t *lock)
{
unsigned long flags;
local_irq_save(flags);
preempt_disable();
spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
/*
* On lockdep we dont want the hand-coded irq-enable of
* _raw_spin_lock_flags() code, because lockdep assumes
* that interrupts are not re-enabled during lock-acquire:
*/
#ifdef CONFIG_LOCKDEP
LOCK_CONTENDED(lock, _raw_spin_trylock, _raw_spin_lock);
#else
_raw_spin_lock_flags(lock, &flags);
#endif
return flags;
}
显而易见,spin_lock_irq还调用了local_irq_disable函数,禁用本地中断;
假设当进程A调用spin_lock访问临界区时,如果中断来临,刚好调度到这个cpu核上,并且也去获取spin_lock,此时进程A已经被设置为TASK_INTERRUPT,同时中断处理程序因为拿不到锁,一直在忙等,进程调度无法调度进程A,导致死锁。而如果这个中断是在另一个cpu核上,就不会出现这种情况,因为另一个cpu核的中断并不会导致进程A运行状态被设置为TASK_INTERRUPT,只是被调度换出。
所以在使用spin_lock接口时,需要注意是否会在中断处理中获取锁。
而spin_lock_irqsave在进入临界区前,会保存当前中断状态flag,关闭本地中断,然后进入临界区,在退出临界区时,把保存的flag写回到中断寄存器。
spin_lock_irq在进入临界区前不保存中断状态,关中断,进入临界区,在退出临界区时,开中断。
spin_lock_irqsave锁返回时,中断状态不会被改变,调用spin_lock_irqsave前是开中断返回就开中断。
spin_lock_irq锁返回时,永远都是开中断,即使spin_lock_irq前是关中断