一、什么是自旋锁
自旋锁最初就是为了SMP系统设计的,实现在多处理器情况下保护临界区。自旋锁实际上是忙等待锁,它有几个重要的特性:被自旋锁保护的临界区代码执行时不能进入睡眠状态;被自旋锁保护的临界区代码执行时,内核不能被抢占。
在单cpu并且不支持内核抢占的系统上,自旋锁实际上是空操作;在单cpu并且支持内核抢占的系统上,自旋锁实际上是对内核抢占功能的打开和关闭;在SMP系统上,导致可能有多个进程物理上的并发,因此除了对内核抢占的禁止,还需要在内存加一个标志,每个需要进入临界区的代码都必须检查这个标志,看是否有进程已经在这个临界区中。
二、自旋锁的使用
1、初始化
spin_lock_init(x)
该宏用于初始化自旋锁x。自旋锁在真正使用前必须先初始化。该宏用于动态初始化。
2、加锁
void spin_lock(spinlock_t *lock);
最基本得自旋锁函数,它不失效本地中断。
void spin_lock_irqsave(spinlock_t *lock, unsigned long flags);
在获得自旋锁之前禁用硬中断(只在本地处理器上),而先前的中断状态保存在flags中
void spin_lock_irq(spinlock_t *lock);
在获得自旋锁之前禁用硬中断(只在本地处理器上),不保存中断状态
void spin_lock_bh(spinlock_t *lock);
在获得锁前禁用软中断,保持硬中断打开状态
3、解锁
spin_unlock(lock);
该宏释放自旋锁lock,它与spin_trylock或者spin_lock配对使用。如果spin_trylock返回假,表明没有获得自旋锁,因此不必使用spin_unlock(lock)释放。
spin_unlock_bh(lock)
该宏释放自旋锁lock的同一时候,也使能本地的软中断。它与spin_lock_bh配对使用。
spin_unlock_irqrestore(lock, flags)
该宏释放自旋锁lock的同一时候,使能本地硬件中断并且恢复标志寄存器的值为变量flags保存的值。它与spin_lock_irqsave配对使用。
spin_unlock_irq(lock)
该宏释放自旋锁lock的同一时候,也使能本地中断。它与spin_lock_irq配对使用。
三、什么时候使用自旋锁
1、自旋锁和互斥锁比较
a、低开销加锁优先使用自旋锁。
b、如果要在中断里面加锁,那么只能使用自旋锁。
c、自旋锁是忙等待锁,等待时不会进入睡眠状态,因此如果临界区的时间很短的话,使用自旋锁会有更高的效率。
2、使用自旋锁的一些重要规则
a、如果被保护的共享资源只在一个tasklet或timer上下文访问,那么不需要任何自旋锁保护,因为同一个tasklet或timer只能在一个CPU上运行,即使是在SMP环境下也是如此。实际上tasklet在调用tasklet_schedule标记其需要被调度时已经把该tasklet绑定到当前CPU,因此同一个tasklet决不可能同时在其他CPU上运行。timer也是在其被使用add_timer添加到timer队列中时已经被帮定到当前CPU,所以同一个timer绝不可能运行在其他CPU上。
b、如果被保护的共享资源只在一个软中断(tasklet和timer除外)上下文访问,那么这个共享资源需要用spin_lock和spin_unlock来保护,因为同样的软中断可以同时在不同的CPU上运行。如果被保护的共享资源在两个或多个软中断上下文访问,那么这个共享资源当然更需要用spin_lock和spin_unlock来保护,不同的软中断能够同时在不同的CPU上运行。
c、如果被保护的共享资源只在两个或多个tasklet或timer上下文访问,那么对共享资源的访问仅需要用spin_lock和spin_unlock来保护,不必使用_bh版本,因为当tasklet或timer运行时,不可能有其他tasklet或timer在当前CPU上运行。
自旋锁最初就是为了SMP系统设计的,实现在多处理器情况下保护临界区。自旋锁实际上是忙等待锁,它有几个重要的特性:被自旋锁保护的临界区代码执行时不能进入睡眠状态;被自旋锁保护的临界区代码执行时,内核不能被抢占。
在单cpu并且不支持内核抢占的系统上,自旋锁实际上是空操作;在单cpu并且支持内核抢占的系统上,自旋锁实际上是对内核抢占功能的打开和关闭;在SMP系统上,导致可能有多个进程物理上的并发,因此除了对内核抢占的禁止,还需要在内存加一个标志,每个需要进入临界区的代码都必须检查这个标志,看是否有进程已经在这个临界区中。
二、自旋锁的使用
1、初始化
spin_lock_init(x)
该宏用于初始化自旋锁x。自旋锁在真正使用前必须先初始化。该宏用于动态初始化。
2、加锁
void spin_lock(spinlock_t *lock);
最基本得自旋锁函数,它不失效本地中断。
void spin_lock_irqsave(spinlock_t *lock, unsigned long flags);
在获得自旋锁之前禁用硬中断(只在本地处理器上),而先前的中断状态保存在flags中
void spin_lock_irq(spinlock_t *lock);
在获得自旋锁之前禁用硬中断(只在本地处理器上),不保存中断状态
void spin_lock_bh(spinlock_t *lock);
在获得锁前禁用软中断,保持硬中断打开状态
3、解锁
spin_unlock(lock);
该宏释放自旋锁lock,它与spin_trylock或者spin_lock配对使用。如果spin_trylock返回假,表明没有获得自旋锁,因此不必使用spin_unlock(lock)释放。
spin_unlock_bh(lock)
该宏释放自旋锁lock的同一时候,也使能本地的软中断。它与spin_lock_bh配对使用。
spin_unlock_irqrestore(lock, flags)
该宏释放自旋锁lock的同一时候,使能本地硬件中断并且恢复标志寄存器的值为变量flags保存的值。它与spin_lock_irqsave配对使用。
spin_unlock_irq(lock)
该宏释放自旋锁lock的同一时候,也使能本地中断。它与spin_lock_irq配对使用。
三、什么时候使用自旋锁
1、自旋锁和互斥锁比较
a、低开销加锁优先使用自旋锁。
b、如果要在中断里面加锁,那么只能使用自旋锁。
c、自旋锁是忙等待锁,等待时不会进入睡眠状态,因此如果临界区的时间很短的话,使用自旋锁会有更高的效率。
2、使用自旋锁的一些重要规则
a、如果被保护的共享资源只在一个tasklet或timer上下文访问,那么不需要任何自旋锁保护,因为同一个tasklet或timer只能在一个CPU上运行,即使是在SMP环境下也是如此。实际上tasklet在调用tasklet_schedule标记其需要被调度时已经把该tasklet绑定到当前CPU,因此同一个tasklet决不可能同时在其他CPU上运行。timer也是在其被使用add_timer添加到timer队列中时已经被帮定到当前CPU,所以同一个timer绝不可能运行在其他CPU上。
b、如果被保护的共享资源只在一个软中断(tasklet和timer除外)上下文访问,那么这个共享资源需要用spin_lock和spin_unlock来保护,因为同样的软中断可以同时在不同的CPU上运行。如果被保护的共享资源在两个或多个软中断上下文访问,那么这个共享资源当然更需要用spin_lock和spin_unlock来保护,不同的软中断能够同时在不同的CPU上运行。
c、如果被保护的共享资源只在两个或多个tasklet或timer上下文访问,那么对共享资源的访问仅需要用spin_lock和spin_unlock来保护,不必使用_bh版本,因为当tasklet或timer运行时,不可能有其他tasklet或timer在当前CPU上运行。