自旋锁用于保护短的代码段,其中只包含少量C语句,因此会很快执行完毕。自旋锁是为实现保护共享资源而提出一种锁机制。但是自旋锁是一种比较低级的保护数据结构或代码片段的原始方式,这种锁可能存在两个问题:死锁和过多占用cpu资源。
自旋锁通过spinlock_t数据结构实现,基本上可使用spin_lock和spin_unlock操纵。
1 typedef struct spinlock { 2 union { 3 struct raw_spinlock rlock; 4 5 #ifdef CONFIG_DEBUG_LOCK_ALLOC 6 # define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map)) 7 struct { 8 u8 __padding[LOCK_PADSIZE]; 9 struct lockdep_map dep_map; 10 }; 11 #endif 12 }; 13 } spinlock_t;
spin_lock_irqsave不仅获得自旋锁,还停用本地CPU的中断,用该接口获得的自旋锁必须用spin_unlock_irqsave释放;
spin_lock_bh不仅获得自旋锁,还可停用softIRQ,用该接口获得的自旋锁必须用spin_unlock_bh释放;
自旋锁的基本用法:
spinlock_t lock = SPIN_LOCK_UNLOCKED;
...
spin_lock(&lock);
.../*临界区*/
spin_unlock(&lock);
初始化自旋锁时,必须使用SPIN_LOCK_UNLOCKED将其设置为未锁定状态。spin_lock会考虑下面两种情况:
1.如果内核中其他地方尚未获得lock,则由当前处理器获取。其他处理器不能再进入lock保护的代码范围;
2.如果lock已经由另一个处理器获得,spin_lock进入一个无限循环,重复地检查lock是否已经由spin_unlock释放(自旋锁因此得名)。如果已经释放,则获得lock并进入临界区;
使用自旋锁时必须注意:
1.如果获得锁之后不释放,系统将变得不可用。所有的处理器(包括获得锁的处理器在内),迟早需要进入所对应的临界区。它们会进入无限循环等待锁释放,但是等不到。这样就造成了死锁。k
2.自旋锁决不能长期持有,因为所有等待锁释放的处理器都处于不可用状态,无法用于其他工作。
3.由自旋锁保护的代码不能进入睡眠状态 。避免直接进入睡眠状态并不复杂,但还必须保证在自旋锁保护的代码所调用的函数也不会进入睡眠状态!一个特定的例子是kmalloc函数,通常将立刻返回请求的内存,但在内存短缺时,该函数就会进入睡眠状态。
4.自旋锁当前的持有者无法也不能多次获得同一自旋锁!在函数调用了其它函数,而这些函数每次都操作同一个锁时,这种约束特别重要。如果已经获得一个锁,而调用的某个函数试图再次获得该锁,尽管当前的代码路径已经持有该锁,也同样会发生死锁,因为处理器等待自身释放持有的锁,这尼玛等啥时候去啊!!!
在单处理器系统上,自旋锁定义为空操作,因为不存在几个CPU同时进入临界区的情况,但如果启用了内核抢占(kernel preemption),自旋锁就不能定义为空操作啦。如果内核在临界区中被中断,而此时另一个进程进入临界区,这与SMP系统上两个处理器同时在临界区执行的情况是等效的。通过一个简单的技巧可以防止这种情况的发生:内核在进入到自旋锁保护的临界区时,就停用内核抢占。在启用了内核抢占的单处理器内核中,spin_lock基本上等价于preempt_disable,而spin_unlock则等价于preempt_enable。
参考资料:
http://www.cnitblog.com/zouzheng/archive/2013/09/11/40164.html
http://www.cnblogs.com/biyeymyhjob/archive/2012/07/21/2602015.html