spin_lock相关

18 篇文章 0 订阅

spin_lock - 自旋锁


编程时, 经常需要考虑的问题是同步问题。

谁和谁之间同步呢?? 看你的关键代码段和共享数据区的访问/读写者。

有可能会很多, 很乱...

比如:

线程和线程, 

线程和中断,

中断和中断,

此核和彼核...


应用, 内核等都有很多锁, 互斥, 原子等机制...

内核中, 

比如

1. 原子变量 

2. 自旋锁 spin_lock

3. 互斥量 mutex

4. RCU (Read Copy Update)

......


其他还有很多, 我们常用的还是自旋锁和互斥量。

1. 自旋锁和互斥量的区别。

mutex - 如果锁被其他线程持有, 当前尝试mutex_lock的线程进入睡眠。 当对方释放锁后, 会唤醒该线程。

             另外mutex本身保护的代码段是允许睡眠的。

             即mutex只是保证两个访问共享资源的线程间互斥。

spin_lock - 自旋锁, 通常使用于多核场景。 跟mutex相比, 当某线程尝试去获得spin_lock时,  发现被其他线程持有, 则忙等待。

             只有等到其他线程释放锁后, 才能执行。未获得锁的线程会自旋/旋转等待这把锁。

             而持有该锁的线程, 必须尽快执行完关键代码段, 处理完共享数据区, 归还自旋锁。

             所以持有spin_lock后, 会关抢占, 它保护的代码段需要仔细编写, 不允许有导致睡眠的函数调用。

             如果spin_lock保护的代码段会导致睡眠, 则可能导致死锁。

             显然spin_lock效率更高, 但消耗系统资源也越大。


 

贴一下别人文章  ===>

spinlock的使用,由于开始时不太了解,开发内核模块时,出了问题,调试了好多天,后来才发现,原来在lock的代码里,调用的内核api中,执行了schedule!!!!

转发这篇文章以此来警醒我的无知

 

 

Linux系统提供的内核同步机制有很多种。 
spinlock只是一种选择, 并不是所有同步的地方都用spinlock. 
通常它适用于对内核(包括模块)的一些全局数据结构的访问。 
spinlock中所保护的codes最好能迅速完成,同时释放该锁。 
在无法获得该锁的情况下,内核不会切换,而是不断地尝试, 这也是spinlock名字的由来, 
也正因为此,所以spinlock中的代码不能有schedule()之类的放弃CPU的代码,也不会能被抢占,但可以有中断(下面说明)。因为如果正好调度到另一个kernel path也需要这个锁,整个系统将形成死锁。 
如果有中断服务也会申请该spinlock,那么请用spinlock_irqsave()的API,把中断关掉,不然还是会死锁。

 


5.5.2. 自旋锁和原子上下文
想象一会儿你的驱动请求一个自旋锁并且在它的临界区里做它的事情. 在中间某处, 你的驱动失去了处理器. 或许它已调用了一个函数( copy_from_user, 假设) 使进程进入睡眠. 或者, 也许, 内核抢占发威, 一个更高优先级的进程将你的代码推到一边. 你的代码现在持有一个锁, 在可见的将来的如何时间不会释放这个锁. 如果某个别的线程想获得同一个锁, 它会, 在最好的情况下, 等待( 在处理器中自旋 )很长时间. 最坏的情况, 系统可能完全死锁.

大部分读者会同意这个场景最好是避免. 因此, 应用到自旋锁的核心规则是任何代码必须, 在持有自旋锁时, 是原子性的. 它不能睡眠; 事实上, 它不能因为任何原因放弃处理器, 除了服务中断(并且有时即便此时也不行)

内核抢占的情况由自旋锁代码自己处理. 内核代码持有一个自旋锁的任何时间, 抢占在相关处理器上被禁止. 即便单处理器系统必须以这种方式禁止抢占以避免竞争情况. 这就是为什么需要正确的加锁, 即便你从不期望你的代码在多处理器机器上运行.

在持有一个锁时避免睡眠是更加困难; 很多内核函数可能睡眠, 并且这个行为不是都被明确记录了. 拷贝数据到或从用户空间是一个明显的例子: 请求的用户空间页可能需要在拷贝进行前从磁盘上换入, 这个操作显然需要一个睡眠. 必须分配内存的任何操作都可能睡眠. kmalloc 能够决定放弃处理器, 并且等待更多内存可用除非它被明确告知不这样做. 睡眠可能发生在令人惊讶的地方; 编写会在自旋锁下执行的代码需要注意你调用的每个函数.

这有另一个场景: 你的驱动在执行并且已经获取了一个锁来控制对它的设备的存取. 当持有这个锁时, 设备发出一个中断, 使得你的中断处理运行. 中断处理, 在存取设备之前, 必须获得锁. 在一个中断处理中获取一个自旋锁是一个要做的合法的事情; 这是自旋锁操作不能睡眠的其中一个理由. 但是如果中断处理和起初获得锁的代码在同一个处理器上会发生什么? 当中断处理在自旋, 非中断代码不能运行来释放锁. 这个处理器将永远自旋.

避免这个陷阱需要在持有自旋锁时禁止中断( 只在本地 CPU ). 有各种自旋锁函数会为你禁止中断( 我们将在下一节见到它们 ). 但是, 一个完整的中断讨论必须等到第 10 章了.

关于自旋锁使用的最后一个重要规则是自旋锁必须一直是尽可能短时间的持有. 你持有一个锁越长, 另一个进程可能不得不自旋等待你释放它的时间越长, 它不得不完全自旋的机会越大. 长时间持有锁也阻止了当前处理器调度, 意味着高优先级进程 -- 真正应当能获得 CPU 的 -- 可能不得不等待. 内核开发者尽了很大努力来减少内核反应时间( 一个进程可能不得不等待调度的时间 )在 2.5 开发系列. 一个写的很差的驱动会摧毁所有的进程, 仅仅通过持有一个锁太长时间. 为避免产生这类问题, 重视使你的锁持有时间短.

 

 

如果只要和其他CPU 互斥,就要用spin_lock/spin_unlock,如果要和irq及其他CPU互斥,就要用 
spin_lock_irq/spin_unlock_irq,如果既要和irq及其他CPU互斥,又要保存 EFLAG的状态,就要用spin_lock_irqsave/spin_unlock_irqrestore,如果要和bh及其他CPU互斥,就要用spin_lock_bh/spin_unlock_bh,如果不需要和 其他CPU互斥,只要和irq互斥,则用local_irq_disable/local_irq_enable, 如果不需要和其他CPU互斥,只要和bh互斥,则用local_bh_disable/local_bh_enable, 等等。值得指出的是,对同一个数据的互斥,在不同的内核执行路径中, 
所用的形式有可能不同

 

Mutex属于sleep-waiting类型的锁。

而Spin lock则属于busy-waiting类型的锁


Q: 自旋锁保护区域 时间片到了会睡眠吗?

A: ...

 

https://mp.weixin.qq.com/s/wQ8kj_OMngk2pVz9fA-RQA

https://blog.csdn.net/ypbsyy/article/details/79899090

https://www.iteye.com/blog/colorlife-1089815

http://www.wowotech.net/kernel_synchronization/460.html

http://www.wowotech.net/kernel_synchronization/spinlock.html

https://www.jianshu.com/p/30d5000dcf97

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值