天天看点

LINUX驱动-自旋锁

  • 当一个线程要访问某个共享资源的时候首先要获得相应的锁,锁只能被一个线程持有,只要此线程不释放持有的锁,那么其他的线程就不能获取此锁。这个锁就叫做自旋锁。
  • 如果自旋锁正在被线程A所持有,线程B要想获得自旋锁,那么线程B就会处于忙循环-旋转-等待状态。不会处于休眠状态
  • Linux内核使用spinlock_t表示自旋锁。结构定义如下
typedef struct spinlock {
 union {
 struct raw_spinlock rlock;

 #ifdef CONFIG_DEBUG_LOCK_ALLOC
 # define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map))
struct {
 u8 __padding[LOCK_PADSIZE];
struct lockdep_map dep_map;
 };
 #endif
 };
 } spinlock_t;
           

   在使用自旋锁之前,首先要定义一个自旋锁变量,定义方法如下所示

spinlock_t lock; //定义自旋锁
           

 定义好自旋锁变量以后就可以使用相应的API函数来操作自旋锁

  • 注意:被自旋锁保护的临界区不能调用任何引起睡眠和阻塞的API函数,否则的话会导致死锁的发生。自旋锁会自动禁止抢占的发生。

      如果在线程A在拥有自旋锁的过程中进入了睡眠或者阻塞状态。那么线程B会开始运行,线程B就要获取锁,但是锁被A持有,而且被禁止了抢占。所以会发生死锁。

  • 获取锁之前一定要先关闭本地中断,避免死锁发生。(linux提供了相应的API)

       建议使用spin_lock_irqsave/spin_unlock_irqrestore(线程中使用).这组函数会保存中断状态,在释放锁的时候会恢复中断状态。在中断中使用spin_lock/spin_unlock,示例代码如下

DEFINE_SPINLOCK(lock) /* 定义并初始化一个锁 */

/* 线程 A */
4 void functionA (){
5 unsigned long flags; /* 中断状态 */
6 spin_lock_irqsave(&lock, flags) /* 获取锁 */
7 /* 临界区 */
8 spin_unlock_irqrestore(&lock, flags) /* 释放锁 */
9 }
10
11 /* 中断服务函数 */
12 void irq() {
13 spin_lock(&lock) /* 获取锁 */
14 /* 临界区 */
15 spin_unlock(&lock) /* 释放锁 */
16 }
           

用自旋锁的注意事项

1)、锁的持有时间不能太长。

2)、自旋锁保护的临界区内不能调用任何可能导致线程休眠的API函数

3)、不能递归申请自旋锁,因为一旦通过递归申请一个正在持有的锁,就会进入自旋状态等待锁的释放。然后就会死锁。

4)、编写驱动时,要将SOC当作多核来编写驱动程序。

继续阅读