[内核同步]自旋锁spin_lock、spin_lock_irq 和 spin_lock_irqsave 分析
转自:http://blog.csdn.net/wh_19910525/article/details/11536279
自旋锁的初衷:在短期间内进行轻量级的锁定。一个被争用的自旋锁使得请求它的线程在等待锁重新可用的期间进行自旋(特别浪费处理器时间),所以自旋锁不应该被持有时间过长。如果需要长时间锁定的话, 最好使用信号量。
单处理器的自旋锁:
- 首先,自旋锁的目的如果在系统不支持内核抢占时,自旋锁的实现也是空的,因为单核只有一个线程在执行,不会有内核抢占,从而资源也不会被其他线程访问到。
- 其次,支持内核抢占,由于自旋锁是禁止抢占内核的,所以不会有其他的进程因为等待锁而自旋.
- 最后,只有在多cpu下,其他的cpu因为等待该cpu释放锁,而处于自旋状态,不停轮询锁的状态。所以这样的话,如果一旦自旋锁内代码执行时间较长,等待该锁的cpu会耗费大量资源,也是不同于信号量和互斥锁的地方。
简单来说,自旋锁在内核中主要用来防止多处理器中并发访问临界区,防止内核抢占造成的竞争。
自旋锁内睡眠禁止睡眠问题:如果自旋锁锁住以后进入睡眠,而此时又不能进行处理器抢占(锁住会disable prempt),其他进程无法获得cpu,这样也不能唤醒睡眠的自旋锁,因此不相应任何操作。
自旋锁为什么广泛用于内核:自旋锁是一种轻量级的互斥锁,可以更高效的对互斥资源进行保护。自旋锁本来就只是一个很简单的同步机制,在SMP之前根本就没这个东西,一切都是Event之类的同步机制,这类同步机制都有一个共性就是:一旦资源被占用都会产生任务切换,任务切换涉及很多东西的(保存原来的上下文,按调度算法选择新的任务,恢复新任务的上下文,还有就是要修改cr3寄存器会导致cache失效)这些都是需要大量时间的,因此用Event之类来同步一旦涉及到阻塞代价是十分昂贵的,而自旋锁的效率就远高于互斥锁。
在Linux内核中何时使用spin_lock,何时使用spin_lock_irqsave很容易混淆。首先看一下代码是如何实现的。
- spin_lock
spin_lock -----> raw_spin_lock
static inline void __raw_spin_lock(raw_spinlock_t *lock)
{
preempt_disable();
spin_acquire(&lock->dep_map, , , _RET_IP_);
LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
}
1. 只禁止内核抢占,不会关闭本地中断
2. 为何需要关闭内核抢占:假如进程A获得spin_lock->进程B抢占进程A->进程B尝试获取spin_lock->由于进程B优先级比进程A高,先于A运行,而进程B又需要A unlock才得以运行,这样死锁。所以这里需要关闭抢占。 这个原理RTOS的mutex/semaphore是否相同?
a. 因为ThreadX的semaphore,假如进程B获取sema失败,会一直等待,直到A进程释放,不会死锁。
b. Mutex: mutex获取一旦失败,进程会进入sleep,直到其他进程释放;而spin_lock则不同,会一直轮训访问,且直到时间片耗完。
- spin_lock_irq
spin_lock_irq------> raw_spin_lock_irq
static inline void __raw_spin_lock_irq(raw_spinlock_t *lock)
{
local_irq_disable();
preempt_disable();
spin_acquire(&lock->dep_map, , , _RET_IP_);
LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
}
1. 禁止内核抢占,且关闭本地中断
2. 那么在spin_lock中关闭了内核抢占,不关闭中断会出现什么情况呢?假如中断中也想获得这个锁,会出现和spin_lock中举得例子相同。所以这个时候,在进程A获取lock之后,使用spin_lock_irq将中断禁止,就不会出现死锁的情况
3. 在任何情况下使用spin_lock_irq都是安全的。因为它既禁止本地中断,又禁止内核抢占。
4. spin_lock比spin_lock_irq速度快,但是它并不是任何情况下都是安全的。
- spin_lock_irqsave
spin_lock_irqsave------>__raw_spin_lock_irqsave
static inline unsigned long __raw_spin_lock_irqsave(raw_spinlock_t *lock)
{
unsigned long flags; local_irq_save(flags);
preempt_disable();
spin_acquire(&lock->dep_map, , , _RET_IP_);
/*
* On lockdep we dont want the hand-coded irq-enable of
* do_raw_spin_lock_flags() code, because lockdep assumes
* that interrupts are not re-enabled during lock-acquire:
*/
#ifdef CONFIG_LOCKDEP
LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
#else
do_raw_spin_lock_flags(lock, &flags);
#endif
return flags;
}
1. 禁止内核抢占,关闭中断,保存中断状态寄存器的标志位
2. spin_lock_irqsave在锁返回时,之前开的中断,之后也是开的;之前关,之后也是关。但是spin_lock_irq则不管之前的开还是关,返回时都是开的(?)。
3. spin_lock_irq在自旋的时候,不会保存当前的中断标志寄存器,只会在自旋结束后,将之前的中断打开。
1. spin_lock/spin_unlock:
进程A中调用了spin_lock(&lock)然后进入临界区,此时来了一个中断(interrupt),该中断也运行在和进程A相同的CPU上,并且在该中断处理程序中恰巧也会spin_lock(&lock), 试图获取同一个锁。由于是在同一个CPU上被中断,进程A会被设置为TASK_INTERRUPT状态,中断处理程序无法获得锁,会不停的忙等,由于进程A被设置为中断状态,schedule()进程调度就无法再调度进程A运行,这样就导致了死锁!
但是如果该中断处理程序运行在不同的CPU上就不会触发死锁。 因为在不同的CPU上出现中断不会导致进程A的状态被设为TASK_INTERRUPT,只是换出。当中断处理程序忙等被换出后,进程A还是有机会获得CPU,执行并退出临界区。所以在使用spin_lock时要明确知道该锁不会在中断处理程序中使用。
2. spin_lock_irq/spin_unlock_irq
spin_lock_irq----->raw_spin_lock_irq
spin_lock_irq 和 spin_unlock_irq, 如果你确定在获取锁之前本地中断是开启的,那么就不需要保存中断状态,解锁的时候直接将本地中断启用就可以啦
3. spin_lock_irqsave/spin_unlock_irqrestore
使用spin_lock_irqsave在于你不期望在离开临界区后,改变中断的开启/关闭状态!进入临界区是关闭的,离开后它同样应该是关闭的!
如果自旋锁在中断处理函数中被用到,那么在获取该锁之前需要关闭本地中断,spin_lock_irqsave 只是下列动作的一个便利接口:
1 保存本地中断状态(这里的本地即当前的cpu的所有中断)
2 关闭本地中断
3 获取自旋锁
解锁时通过 spin_unlock_irqrestore完成释放锁、恢复本地中断到之前的状态等工作
参考微博:
1. 什么情况下使用什么样的自旋锁:http://blog.csdn.net/wesleyluo/article/details/8807919
2. 本地中断概念的理解:http://blog.csdn.net/adaptiver/article/details/6177646
3. 深入理解自旋锁:http://blog.csdn.net/vividonly/article/details/6594195
4. 自旋锁和互斥量区别:http://blog.csdn.net/kyokowl/article/details/6294341
5. 自旋锁的临界区本地cpu不会发生任何进程调度:http://blog.chinaunix.net/uid-23769728-id-3367773.html
总结:
1. 这样是否可以这么说,spin_lock为了防止内核的抢占死锁,spin_lock_irq为了防止内核和中断的抢占死锁,spin_lock_irqsave为了防止进入自旋状态丢掉之前的中断状态。
2.
[内核同步]自旋锁spin_lock、spin_lock_irq 和 spin_lock_irqsave 分析的更多相关文章
- [内核同步]自旋锁spin_lock、spin_lock_irq 和 spin_lock_irqsave 分析【转】
转自:https://www.cnblogs.com/x_wukong/p/8573602.html 转自;https://www.cnblogs.com/aaronLinux/p/5890924.h ...
- LINUX内核笔记:自旋锁
目录 自旋锁作用与基本使用方法? 在SMP和UP上的不同表现? 自旋锁与上下文 使用spin_lock()后为什么不能睡眠? 强调:锁什么? 参考 1.自旋锁作用与基本使用方法? 与其他锁一样,自 ...
- 自旋锁spin_lock和raw_spin_lock
自旋锁spin_lock和raw_spin_lock Linux内核spin_lock.spin_lock_irq 和 spin_lock_irqsave 分析 http://blog.csdn.ne ...
- 自旋锁spin_lock和raw_spin_lock【转】
转自:http://blog.csdn.net/droidphone/article/details/7395983 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[-] 临界区Cr ...
- 自旋锁spin_lock、spin_lock_irq 和 spin_lock_irqsave
自旋锁和互斥锁的区别是,自旋锁不会引起睡眠,所以可用于不能休眠的代码中(如IRQ) 自旋锁保持期间抢占失效,而信号量保持期间可以被抢占 定义 spinlock_t lock; init #define ...
- kernel笔记——内核同步与锁
内核同步 内核同步解决并发带来的问题,多个线程对同一数据进行修改,数据会出现不一致的情况,同步用于保护共享数据等资源. 有两种形式的并发: 同时进行式并发,在不同cpu上执行的进程同时访问共享数据 二 ...
- Linux 内核同步之自旋锁与信号量的异同【转】
转自:http://blog.csdn.net/liuxd3000/article/details/8567070 Linux 设备驱动中必须解决的一个问题是多个进程对共享资源的并发访问,并发访问会导 ...
- 漫画|Linux 并发、竞态、互斥锁、自旋锁、信号量都是什么鬼?(转)
知乎链接:https://zhuanlan.zhihu.com/p/57354304 1. 锁的由来? 学习linux的时候,肯定会遇到各种和锁相关的知识,有时候自己学好了一点,感觉半桶水的自己已经可 ...
- Linux内核同步
Linux内核剖析 之 内核同步 主要内容 1.内核请求何时以交错(interleave)的方式执行以及交错程度如何. 2.内核所实现的基本同步机制. 3.通常情况下如何使用内核提供的同步机制. 内核 ...
随机推荐
- Sed简单入门实例
1. Sed简介 sed 是一种在线编辑器,它一次处理一行内容.处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用sed命令处理缓冲区中的内容,处理完成后 ...
- python with as用法
with as用途:取代原来的try...finally,主要是用于处理异常 基本语法:with EXPRESSION [ as VARIABLE] WITH-BLOCK 基本要求:with所求值的对 ...
- dede取子栏目时重复显示同级栏目的终极解决方法
使用channelartlist标签时,当栏目没有子栏目是,会出现重复同级栏目的问题,解决方法如下: 先看下面的代码{dede:channelartlist typeid='2'} {dede:ty ...
- 设置webconfig 解决asp.net上传文件过大问题
对于asp.net,默认只允许上传4M文件,增加如下配置,一般可以自定义最大文件大小. <httpRuntime executionTimeout="800" maxRequ ...
- 使用配置方式进行ssh的整合以及管理员管理的案例(二)
(续) 删除Hibernate配置文件的写法: 在applicationContext.xml中添加数据库操作的相关配置: <!-- 配置数据库连接池 --> <bean id ...
- 穿越泥地(mud) (BFS)
问题 C: 穿越泥地(mud) 时间限制: 1 Sec 内存限制: 128 MB提交: 16 解决: 10[提交][状态][讨论版] 题目描述 清早6:00,FJ就离开了他的屋子,开始了他的例行工 ...
- Unity光照
广义地说,Unity有2种光源.1.动态光源 2.Backed Lighting 1.动态光源 就是实时计算的.只要摆光源就可以了 2.Backed Lighting 提前处理好光照贴图.贴在物体上 ...
- 谷歌大牛Jeff Dean是如何成为互联网战神的
“光在真空中的速度曾经是大约每小时35英里,然后Jeff Dean花了一个周末优化了基础物理学.”——出自“关于Jeff Dean的事实” 其实,“关于Jeff Dean的事实”这个G+帖中描述的并非 ...
- Python-正则零宽断言及命名捕获(类PHP)
(一)零宽断言 说明:本文的例子使用python描述 首先说明一下什么是零宽断言,所谓零宽断言就是并不去真正的匹配字符串文本,而仅仅是匹配对应的位置. 正则表达式中有很多这样的断言 ...
- python tornado框架使用
处理方法 t_handler.py from tornado.web import RequestHandler class IndexHandler(RequestHandler): def get ...