自旋锁的核心思想:
设置一个在多处理器之间共享的全局变量锁L,并定义当L=1时为上锁状态,L=0位解锁状态。如果处理器A上的代码要进入临界区,它要先读取L的值,判断其是否为0,如果L!=0表明有其他处理器上的代码正在对共享数据进行访问,此时处理器A进入忙等待即自旋状态,如果L=0表明当前没有其他处理器上的代码进入临界区,此时处理器A可以访问该资源,它先把L置1,然后进入临界区,访问完毕离开临界区时将L置0.
static inline void spin_lock(spinlock_t *lock)
{
raw_spin_lock(&lock->rlock);
}
static inline void raw_spin_lock(raw_spinlock_t *lock)
{
preempt_disable(); //关闭调度器
do_raw_spin_lock(lock);//加锁操作
}
do_raw_spin_lock()函数可以保证原子的去判断和更改自旋锁的值,具体可参看源码。
以下场景会遇到问题:
一个进程正在占用自旋锁L,此时产生了中断,并且中断中对自旋锁L进行了获取操作,这样将导致死锁。
遇到上述情况需要用到spin_lock_irq锁
static inline void spin_lock_irq(spinlock_t *lock)
{
raw_spin_lock_irq(&lock->rlock);
}
static inline void raw_spin_lock_irq(raw_spinlock_t *lock)
{
local_irq_disable();//关闭中断
preempt_disable();//关闭调度器
do_raw_spin_lock(lock);//加锁操作
}
spin_lock_irq与spin_lock的区别就是关闭中了中断,从而解决了上面的问题。
spin_lock_irq存在的问题,就是当遇到smp系统的时候,此时关闭的中断只是关闭当前进程所在的cpu上面的中断,其他cpu上面依然会产生中断。如果其他cpu上面产生了中断并且试图去获取自旋锁,这样中断函数中依然会自旋,但是进程所在的cpu上面依然可以运行,所以不会导致死锁的发生。这个问题就需要我们在自旋锁的临界区内的代码必须是原子的,不能休眠。
spin_lock_irqsave()与spin_lock_irq的区别,在关闭中断前会将处理器当前的FLAGS寄存器的值保存在一个变量中,spin_unlock_irqrestore()来释放锁。
void spin_lock_bh(spinlock_t *lock);相对于spin_lock_irq,该函数用来处理软中断。void spin_unlock_bh用来释放锁。
非阻塞版本:
static inline int spin_trylock(spinlock_t *lock);
static inline int spin_trylock_irq(spinlock_t *lock);
spin_trylock_irqsave(lock, flags);
spin_trylock_bh(spinlock_t *lock);
这些非阻塞版本的自旋锁函数在试图获得一个锁时,如果发现该锁处于上锁状态,会直接返回0而不是自旋,如果成功则返回1.
读写自旋锁rwlock
与之前的spin_lock类比起来,这种锁比较有意思的地方在于:它允许任意数量的读取者同时进入临界区,但写入者必须进行互斥访问。一个进程想进去读的话,必须检查是否有进程在写,有的话必须自旋,否则可以获得锁。一个进程想进去写的话,必须先检查是否有进程正在读或写,有的话必须自旋。
rwlock版本:
对于读取者:
void read_lock(rwlock *lock);
void read_lock_irq(rwlock_t *lock);
void read_lock_irqsave(rwlock_t *lock, unsigned long flags);
void read_unlock(rwlock_t *lock);
void read_unlock_irq(rwlock_t *lock);
void read_unlock_irqrestore(rwlock_t *lock, unsigned long flags);
对于写入者:
void write_lock(rwlock_t *lock);
void write_lock_irq(rwlock_t *lock);
void write_lock_irqsave(rwlock_t *lock, unsigned long flags);
void write_unlock(rwlock_t *lock);
void write_unlock_irq(rwlock *lock);
void write_unlock_irqrestore(rwlock_t *lock, unsigned long flags);
try版本:
int read_trylock(rwlock *lock);
int write_trylock(rwlock_t *lock);