当前位置:嗨网首页>书籍在线阅读

08-自旋锁的使用

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

7.4.1 自旋锁的使用

自旋锁(spin lock)是一种典型的对临界资源进行互斥访问的手段,其名称来源于它的工作方式。为了获得一个自旋锁,在某CPU上运行的代码需先执行一个原子操作,该操作测试并设置(test-and-set)某个内存变量,由于它是原子操作,所以在该操作完成之前其他执行单元不可能访问这个内存变量。如果测试结果表明锁已经空闲,则程序获得这个自旋锁并继续执行;如果测试结果表明锁仍被占用,程序将在一个小的循环内重复这个“测试并设置”操作,即进行所谓的“自旋”,通俗地说就是“在原地打转”。当自旋锁的持有者通过重置该变量释放这个自旋锁后,某个等待的“测试并设置”操作向其调用者报告锁已释放。

理解自旋锁最简单的方法是把它作为一个变量看待,该变量把一个临界区或者标记为“我当前在运行,请稍等一会”或者标记为“我当前不在运行,可以被使用”。如果A执行单元首先进入例程,它将持有自旋锁;当B执行单元试图进入同一个例程时,将获知自旋锁已被持有,需等到A执行单元释放后才能进入。

Linux中与自旋锁相关的操作主要有以下4种。

1.定义自旋锁

spinlock_t lock;

2.初始化自旋锁

spin_lock_init(lock)

该宏用于动态初始化自旋锁lock。

3.获得自旋锁

spin_lock(lock)

该宏用于获得自旋锁lock,如果能够立即获得锁,它就马上返回,否则,它将自旋在那里,直到该自旋锁的保持者释放。

spin_trylock(lock)

该宏尝试获得自旋锁lock,如果能立即获得锁,它获得锁并返回真,否则立即返回假,实际上不再“在原地打转”。

4.释放自旋锁

spin_unlock(lock)

该宏释放自旋锁lock,它与spin_trylock或spin_lock配对使用。

自旋锁一般这样被使用:

/ 定义一个自旋锁/

spinlock_t lock;

spin_lock_init(&lock);

spin_lock (&lock) ; / 获取自旋锁,保护临界区 /

. . ./ 临界区/

spin_unlock (&lock) ; / 解锁/

自旋锁主要针对SMP或单CPU但内核可抢占的情况,对于单CPU和内核不支持抢占的系统,自旋锁退化为空操作。在单CPU和内核可抢占的系统中,自旋锁持有期间内核的抢占将被禁止。由于内核可抢占的单CPU系统的行为实际很类似于SMP系统,因此,在这样的单CPU系统中使用自旋锁仍十分必要。

尽管用了自旋锁可以保证临界区不受别的CPU和本CPU内的抢占进程打扰,但是得到锁的代码路径在执行临界区的时候,还可能受到中断和底半部(BH,稍后的章节会介绍)的影响。为了防止这种影响,就需要用到自旋锁的衍生。spin_lock()/spin_unlock()是自旋锁机制的基础,它们和关中断local_irq_disable()/开中断local_irq_enable()、关底半部local_bh_disable()/开底半部local_bh_enable()、关中断并保存状态字local_irq_save()/开中断并恢复状态local_irq_restore()结合就形成了整套自旋锁机制,关系如下:

spin_lock_irq() = spin_lock() + local_irq_disable()

spin_unlock_irq() = spin_unlock() + local_irq_enable()

spin_lock_irqsave() = spin_lock() + local_irq_save()

spin_unlock_irqrestore() = spin_unlock() + local_irq_restore()

spin_lock_bh() = spin_lock() + local_bh_disable()

spin_unlock_bh() = spin_unlock() + local_bh_enable()

spin_lock_irq()、spin_lock_irqsave()、spin_lock_bh()类似函数会为自旋锁的使用系好“安全带”以避免突入其来的中断驶入对系统造成的伤害。

驱动工程师应谨慎使用自旋锁,而且在使用中还要特别注意如下几个问题。

(1)自旋锁实际上是忙等锁,当锁不可用时,CPU一直循环执行“测试并设置”该锁直到可用而取得该锁,CPU在等待自旋锁时不做任何有用的工作,仅仅是等待。因此,只有在占用锁的时间极短的情况下,使用自旋锁才是合理的。当临界区很大,或有共享设备的时候,需要较长时间占用锁,使用自旋锁会降低系统的性能。

(2)自旋锁可能导致系统死锁。引发这个问题最常见的情况是递归使用一个自旋锁,即如果一个已经拥有某个自旋锁的CPU想第二次获得这个自旋锁,则该CPU将死锁。

(3)自旋锁锁定期间不能调用可能引起进程调度的函数。如果进程获得自旋锁之后再阻塞,如调用copy_from_user()、copy_to_user()、kmalloc()和msleep()等函数,则可能导致内核的崩溃。

代码清单7.2给出了自旋锁的使用例子,它被用于实现使得设备只能被最多1个进程打开,等同于代码清单7.1。

代码清单7.2 使用自旋锁实现设备只能被一个进程打开

1 int xxx_count = 0;/定义文件打开次数计数/

2

3 static int xxx_open(struct inode inode, struct file filp)

4 {

5 ...

6 spinlock(&xxx_lock);

7 if (xxx_count) { /已经打开/

8 spin_unlock(&xxx_lock);

9 return - EBUSY;

10 }

11 xxx_count++;/增加使用计数/

12 spin_unlock(&xxx_lock);

13 ...

14 return 0; / 成功 /

15 }

16

17 static int xxx_release(struct inode inode, struct file filp)

18 {

19 ...

20 spinlock(&xxx_lock);

21 xxx_count--; /减少使用计数/

22 spin_unlock(&xxx_lock);

23

24 return 0;

25 }