概述

Spinlock的目的是用来同步SMP中会被多个CPU同时存取的变量。在Linux中,普通的spinlock由于不带额外的语义,用起来反而要非常小心。
在Linux kernel中执行的代码大体分normal和interrupt context两种。tasklet/softirq可以归为normal因为他们可以进入等待;nested interrupt是interrupt context的一种特殊情况,当然也是interrupt context。Normal级别可以被interrupt抢断,interrupt会被另一个interrupt抢断,但不会被normal中断。各个 interrupt之间没有优先级关系,只要有可能,每个interrupt都会被其他interrupt中断。
我们先考虑单CPU的情况。在这样情况下,不管在什么执行级别,我们只要简单地把CPU的中断关掉就可以达到独占处理的目的。从这个角度来说,spinlock的实现简单地令人乍舌:cli/sti。只要这样,我们就关闭了preemption带来的复杂之门。
单CPU的情况很简单,多CPU就不那么简单了。单纯地关掉当前CPU的中断并不会给我们带来好运。当我们的代码存取一个共享数据时,另一颗CPU随时会把该数据改得面目全非。我们需要有手段通知它们——spinlock正为此设。

使用例子

extern spinlock_t lock;
// ...
spin_lock(&lock);
// do something
spin_unlock(&lock);

他能正常工作吗?答案是有可能。在某些情况下,这段代码可以正常工作,但想一想会不会发生这样的事:

// in normal run level
extern spinlock_t lock;
// ...
spin_lock(&lock);
// do something// interrupted by IRQ ...// in IRQextern spinlock_t lock;spin_lock(&lock);

我们在normal级别下获得了一个spinlock,正当我们想做什么的时候,我们被interrupt打断了,CPU转而执行interrupt level的代码,它也想获得这个spinlock,于是“死锁”发生了!解决方法如下:

extern spinlock_t lock;
// ...
cli; // disable interrupt on current CPU
spin_lock(&lock);
// do something
spin_unlock(&lock);
sti; // enable interrupt on current CPU

在获得spinlock之前,我们先把当前CPU的中断禁止掉,然后获得一个lock;在释放lock之后再把中断打开。这样,我们就防止了死锁。事实上,Linux提供了一个更为快捷的方式来实现这个功能:

extern spinlock_t lock;
// ...
spin_lock_irq(&lock);
// do something
spin_unlock_irq(&lock);

如果没有nested interrupt,这一切都很好。加上nested interrupt,我们再来看看这个例子:

// code 1
extern spinlock_t lock1;
// ...
spin_lock_irq(&lock1);
// do something
spin_unlock_irq(&lock1);// code 2
extern spinlock_t lock2;
// ...
spin_lock_irq(&lock2);
// do something
spin_unlock_irq(&lock2);

Code 1和code 2都可运行在interrupt下,我们很容易就可以想到这样的运行次序:

Code 1                                               Code 2extern spinlock_t lock1;
// ...
spin_lock_irq(&lock1); extern spinlock_t lock2;// ...spin_lock_irq(&lock2);// do something spin_unlock_irq(&lock2);
// do something
spin_unlock_irq(&lock1);

问题是在运行spin_unlock_irq(&lock2)后这个CPU的中断已经被打开,“死锁”的问题又会回到我们身边!
解决方法是我们在每次关闭中断前纪录当前中断的状态,然后恢复它而不是直接把中断打开。

unsigned long flags;
local_irq_save(flags);
cli; // disable interrupt on current CPU
spin_lock(&lock);
// do something
spin_unlock(&lock);
local_irq_restore(flags);

Linux同样提供了更为简便的方式:

unsigned long flags;
spin_lock_irqsave(&lock, flags);
// do something
spin_unlock_irqrestore(&lock, flags);

总结

1)被保护的共享资源只在进程上下文访问和软中断上下文访问
  如果被保护的共享资源只在进程上下文访问和软中断上下文访问,那么当在进程上下文访问共享资源时,可能被软中断打断,从而可能进入软中断上下文来对被保护的共享资源访问,因此对于这种情况,对共享资源的访问必须使用spin_lock_bh和spin_unlock_bh来保护。(由于历史原因,后缀‘bh’成为对各种下半部的通称。其实spin_lock_bh本应叫作spin_lock_softirq才贴切)。
  当然使用spin_lock_irq和spin_unlock_irq以及spin_lock_irqsave和spin_unlock_irqrestore也可以,它们失效了本地硬中断,失效硬中断隐式地也失效了软中断。但是使用spin_lock_bh和spin_unlock_bh是最恰当的,它比其他两个快。

2)被保护的共享资源只在进程上下文和tasklet或timer上下文访问
  如果被保护的共享资源只在进程上下文和tasklet或timer上下文访问,那么同样应该使用spin_lock_bh和spin_unlock_bh来获得和释放锁的宏,因为tasklet和timer是用软中断实现的。

3)被保护的共享资源只在一个tasklet或timer上下文访问
  如果被保护的共享资源只在一个tasklet或timer上下文访问,那么不需要任何自旋锁保护,因为同一个tasklet或timer只能在一个CPU上运行,即使是在SMP环境下也是如此。
  实际上tasklet在调用tasklet_schedule标记其需要被调度时已经把该tasklet绑定到当前CPU,因此同一个tasklet决不可能同时在其他CPU上运行。timer也是在其被使用add_timer添加到timer队列中时已经被帮定到当前CPU,所以同一个timer绝不可能运行在其他CPU上。当然同一个tasklet有两个实例同时运行在同一个CPU就更不可能了。

4)被保护的共享资源只在两个或多个tasklet或timer上下文访问
  如果被保护的共享资源只在两个或多个tasklet或timer上下文访问,那么对共享资源的访问仅需要用spin_lock和spin_unlock来保护,不必使用_bh版本,因为当tasklet或timer运行时,不可能有其他tasklet或timer在当前CPU上运行。

5)被保护的共享资源只在一个软中断(tasklet和timer除外)上下文访问
  如果被保护的共享资源只在一个软中断(tasklet和timer除外)上下文访问,那么这个共享资源需要用spin_lock和spin_unlock来保护,因为同样的软中断可以同时在不同的CPU上运行。

6)被保护的共享资源在两个或多个软中断上下文访问
  如果被保护的共享资源在两个或多个软中断上下文访问,那么这个共享资源当然更需要用spin_lock和spin_unlock来保护,不同的软中断能够同时在不同的CPU上运行。

7)被保护的共享资源在软中断(包括tasklet和timer)或进程上下文和硬中断上下文访问
  如果被保护的共享资源在软中断(包括tasklet和timer)或进程上下文和硬中断上下文访问,那么在软中断或进程上下文访问期间,可能被硬中断打断,从而进入硬中断上下文对共享资源进行访问,因此,在进程或软中断上下文需要使用spin_lock_irq和spin_unlock_irq来保护对共享资源的访问。
  而在中断处理句柄中使用什么版本,需依情况而定,如果只有一个中断处理句柄访问该共享资源,那么在中断处理句柄中仅需要spin_lock和spin_unlock来保护对共享资源的访问就可以了。因为在执行中断处理句柄期间,不可能被同一CPU上的软中断或进程打断。但是如果有不同的中断处理句柄访问该共享资源,那么需要在中断处理句柄中使用spin_lock_irq和spin_unlock_irq来保护对共享资源的访问。
  在使用spin_lock_irq和spin_unlock_irq的情况下,完全可以用spin_lock_irqsave和spin_unlock_irqrestore取代,那具体应该使用哪一个也需要依情况而定。如果可以确信在对共享资源访问前中断是使能的,那么使用spin_lock_irq更好一些,因为它比spin_lock_irqsave要快一些。但是如果你不能确定是否中断使能,那么使用spin_lock_irqsave和spin_unlock_irqrestore更好,因为它将恢复访问共享资源前的中断标志而不是直接使能中断。
  当然,有些情况下需要在访问共享资源时必须中断失效,而访问完后必须中断使能,这样的情形使用spin_lock_irq和spin_unlock_irq最好。

spin_lock用于阻止在不同CPU上的执行单元对共享资源的同时访问以及不同进程上下文互相抢占导致的对共享资源的非同步访问,而中断失效和软中断失效却是为了阻止在同一CPU上软中断或中断对共享资源的非同步访问

原文地址:https://blog.csdn.net/wesleyluo/article/details/8807919

spin_lock、spin_lock_bh、spin_lock_irq、spin_lock_irqsave的使用相关推荐

  1. 【转】spin_lock、spin_lock_irq、spin_lock_irqsave区别

    为什么80%的码农都做不了架构师?>>>    转自:http://blog.csdn.net/luckywang1103/article/details/42083613 void ...

  2. [内核同步]自旋锁spin_lock、spin_lock_irq 和 spin_lock_irqsave 分析

    关于进程上下文,中断上下文,请看这篇文章 Linux进程上下文和中断上下文内核空间和用户空间 自旋锁的初衷:在短期间内进行轻量级的锁定.一个被争用的自旋锁使得请求它的线程在等待锁重新可用的期间进行自旋 ...

  3. Linux内核spin_lock与spin_lock_irq分析

    在Linux内核中何时使用spin_lock,何时使用spin_lock_irqsave很容易混淆.首先看一下代码是如何实现的. spin_lock的调用关系 spin_lock | + -----& ...

  4. spin_lock spin_lock_irq spin_lock_irqsave、spin_lock_bh

    1,为啥需要自旋锁 很多时候我们并不能采用其他的锁,比如读写锁.互斥锁.信号量等.一方面这些锁会发生上下文切换,他的时间是不可预期的,对于一些简单的.极短的临界区完全是一种性能损耗:另一方面在中断上下 ...

  5. 自旋锁spin : spin_lock_irq , spin_lock_irqsave

    Spinlock的目的是用来同步SMP中会被多个CPU同时存取的变量.在Linux中,普通的spinlock由于不带额外的语义,是用起来反而要非常小心. 在Linux kernel中执行的代码大体分n ...

  6. Linux kernel同步机制

    在现代操作系统里,同一时间可能有多个内核执行流在执行,因此内核其实像多进程多线程编程一样也需要一些同步机制来同步各执行单元对共享数据的访问,尤其是在多处理器系统上,更需要一些同步机制来同步不同处理器上 ...

  7. Linux kernel同步机制(上篇)

    在现代操作系统里,同一时间可能有多个内核执行流在执行,因此内核其实像多进程多线程编程一样也需要一些同步机制来同步各执行单元对共享数据的访问,尤其是在多处理器系统上,更需要一些同步机制来同步不同处理器上 ...

  8. 深度解析Linux kernel同步机制(上篇)

    在现代操作系统里,同一时间可能有多个内核执行流在执行,因此内核其实像多进程多线程编程一样也需要一些同步机制来同步各执行单元对共享数据的访问,尤其是在多处理器系统上,更需要一些同步机制来同步不同处理器上 ...

  9. 华为内部面试题库---(9)

    1.spinlock可通过配置选项CONFIG_DEBUG_SPINLOCK来打开内核编译选项,进行自旋锁调试,作用有(多选):(参考:Linux内核设计与实现,第二版,第9章,9.2节) A. 内核 ...

  10. 自旋锁、互斥锁和信号量

    自旋锁 Linux内核中最常见的锁是自旋锁(spin lock).自旋锁最多只能被一个可执行线程持有.如果一个执行线程试图获得一个已经被持有的自旋锁,那么该线程就会一直进行忙循环--旋转--等待锁重新 ...

最新文章

  1. 10个你必须知道的jQueryMobile代码片段(转)
  2. RNA和机器学习:多维生物标志物的合理设计
  3. 内存都是由半导体器件构成的_开启5G新时代——XPS成像技术在半导体器件中的应用...
  4. 【BZOJ1623】 [Usaco2008 Open]Cow Cars 奶牛飞车 贪心
  5. 企业中书写css,web前端开发企业级CSS常用命名,书写规范总结(示例代码)
  6. Cubieboard安装Debian在Nand中
  7. 阿里云日志服务SLS,打造云原生时代智能运维
  8. python一行输出多个数据_Python高效数据分析的8个技巧
  9. 1、.Net Core 基础
  10. 2019最新私塾在线高级java软件架构师培训项目实战(完整)
  11. 纯净的windows官方WinPE制作
  12. 软件智能:aaas系统设计概要 之 序 结篇:计算的三位一体主义
  13. APK的几种安装方式
  14. Septentrio板卡接收机连接方式
  15. 杰出人物的四大法宝——与成功学大师对话
  16. 单片机中断系统应用实例(两组实验+Proteus仿真+C源程序)
  17. 阿里云国际站相比阿里云中国站有什么优势?
  18. 计算机与科学专硕考研院校排名,22考研|全国首次专硕院校评估排名,看看有你想报的没...
  19. 案例 | 沃尔玛 x 腾讯云 Serverless 应用实践,全力保障消费者购物体验
  20. 高频丙类谐振功率放大器【Multisim】【高频电子线路】

热门文章

  1. 斐波那契问题(兔子问题)
  2. ssh免密登录服务器
  3. 经典Web容器解析漏洞
  4. 如何快速提升网站排名?
  5. vivado基本开发流程
  6. 【LeetCode】回溯 N皇后(DFS、子集、组合问题)
  7. 网络编程--TCP实例
  8. tftp 在嵌入式设备和主机之间传输文件
  9. 命令行提示符参数PS1, 但是不会自动换行
  10. 阿里云OSS文件上传,后台签名方案