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的中断并不会给我们带来好运。当我们的代码存取一个shared variable时,另一颗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 IRQ
                                                                         extern spinlock_t lock;
                                                                         spin_lock(&lock);

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

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(&lock);
// do something
spin_unlock_irq(&lock);

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

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

Code 1                                                                        Code 2

extern spinlock_t lock1;

// ...

spin_lock_irq(&lock1);

extern spinlock_t lock2;

// ...
spin_lock_irq(&lock1);

// do something

spin_unlock_irq(&lock2);

// do something

spin_unlock_irq(&lock1);

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

unsigned long flags;
local_irq_save(flags);
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);

总结:

如果被保护的共享资源只在进程上下文访问和软中断上下文访问,那么当在进程上下文访问共享资源时,可能被软中断打断,从而可能进入软中断上下文来对被保护的共享资源访问,因此对于这种情况,对共享资源的访问必须使用spin_lock_bh和spin_unlock_bh来保护。

当然使用spin_lock_irq和spin_unlock_irq以及spin_lock_irqsave和spin_unlock_irqrestore也可以,它们失效了本地硬中断,失效硬中断隐式地也失效了软中断。但是使用spin_lock_bh和spin_unlock_bh是最恰当的,它比其他两个快。
如果被保护的共享资源只在进程上下文和tasklet或timer上下文访问,那么应该使用与上面情况相同的获得和释放锁的宏,因为tasklet和timer是用软中断实现的。
如果被保护的共享资源只在一个tasklet或timer上下文访问,那么不需要任何自旋锁保护,因为同一个tasklet或timer只能在一个CPU上运行,即使是在SMP环境下也是如此。实际上tasklet在调用tasklet_schedule标记其需要被调度时已经把该tasklet绑定到当前CPU,因此同一个tasklet决不可能同时在其他CPU上运行。
timer也是在其被使用add_timer添加到timer队列中时已经被帮定到当前CPU,所以同一个timer绝不可能运行在其他CPU上。当然同一个tasklet有两个实例同时运行在同一个CPU就更不可能了。
如果被保护的共享资源只在两个或多个tasklet或timer上下文访问,那么对共享资源的访问仅需要用spin_lock和spin_unlock来保护,不必使用_bh版本,因为当tasklet或timer运行时,不可能有其他tasklet或timer在当前CPU上运行。
如果被保护的共享资源只在一个软中断(tasklet和timer除外)上下文访问,那么这个共享资源需要用spin_lock和spin_unlock来保护,因为同样的软中断可以同时在不同的CPU上运行。
如果被保护的共享资源在两个或多个软中断上下文访问,那么这个共享资源当然更需要用spin_lock和spin_unlock来保护,不同的软中断能够同时在不同的CPU上运行。
如果被保护的共享资源在软中断(包括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上软中断或中断对共享资源的非同步访问

那些情况该使用它们spin_lock到spin_lock_irqsave相关推荐

  1. spin_lock到spin_lock_irqsave的使用

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

  2. spin_lock spin_lock_irq spin_lock_irqsave、spin_lock_bh

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

  3. spin_lock、spin_lock_bh、spin_lock_irq、spin_lock_irqsave的使用

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

  4. 自旋锁spin : spin_lock_irq , spin_lock_irqsave

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

  5. 内核同步机制——自旋锁

    由于关键代码区可以跨越了多个函数或数据结构,需要有更通用的同步方法:锁. 内核中最常见的一种锁就是自旋锁.相同的锁可用于多处. 自旋锁可用在不可睡眠的场景,如中断处理函数.自旋锁是一种互斥设备,只有两 ...

  6. 自旋锁spinlock解析

    1 基础概念 自旋锁与相互排斥锁有点类似,仅仅是自旋锁不会引起调用者睡眠.假设自旋锁已经被别的运行单元保持.调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁."自旋"一词就 ...

  7. Linux内核的同步机制---自旋锁

    自旋锁的思考:http://bbs.chinaunix.net/thread-2333160-1-1.html 近期在看宋宝华的<设备驱动开发具体解释>第二版.看到自旋锁的部分,有些疑惑. ...

  8. 关于自旋锁的一些文章摘记

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~第一部分~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 现在正在结合一篇文章 http://www.ibm.com/ ...

  9. Linux kernel同步机制

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

最新文章

  1. JavaScript是如何工作的:与WebAssembly比较及其使用场景
  2. Web负载均衡学习笔记之K8S内Ngnix微服务服务超时问题
  3. 一文了解P2P的前世今生
  4. 【机器视觉】 endswitch算子
  5. js数组如何按照固定的下标去重_js数组去重方法总结
  6. MultipartFile与File之间的相互转换
  7. python中casefold()函数的用法
  8. [渝粤教育] 武汉理工大学 复变函数与积分变换 参考 资料
  9. go语言编写同时支持Linux和Windows的单文件Web界面文件浏览器filebrowser介绍
  10. 高并发,我把握不住啊!
  11. SQLite用户权限
  12. 巨杉分布式数据库-学习笔记
  13. 什么是第三方支付公司?
  14. FOCUS projects 5 Pro(照片景深处理软件)官方正式版V5.34.03722 | 景深合成软件下载
  15. 《老梁四大名著情商课》笔记- 智商与情商:哪个重,哪个轻
  16. 感知机实现与门,与非门,或门,异或门
  17. 当谈论工程师文化时我们在谈些什么
  18. Postman(07)Postman+OMySQL操作数据库
  19. 系统集成---技术标准规范(一)
  20. python下载成功的标志_电脑安装python过后的图标

热门文章

  1. 干货 | 三维点云配准:ICP 算法原理及推导
  2. 面经:2020校招中兴提前批面试经历
  3. 菜鸟实现 PHP 二阶、三阶行列式计算和矩阵运算的加、减、乘
  4. 机器学习之线性回归模型的代价函数是凸函数的证明
  5. 使用IKAnalyzer进行中文分词
  6. Badboy入门操作手册
  7. 写程序的时候常用什么字体?
  8. k8s学习笔记——ceph rbd本地手动挂载
  9. 分析Perm()函数功能、代码、时间复杂度
  10. 【前端优化】超详细!带你体验常用的前端优化手段