嵌入式linux中文站关于自旋锁用法介绍的文章,现已有许多,但有些细节的当地点的还不行透,因而咱们在这儿将侧重介绍自旋锁相关的常识。
一、自旋锁(spinlock)简介
自旋锁在同一时刻只能被最多一个内核使命持有,所以一个时刻只要一个线程答应存在于临界区中。这点能够运用在多处理机器、或运行在单处理器上的抢占式内核中需求的确定服务。
二、信号量简介
这儿也介绍下信号量的概念,由于它的用法和自旋锁有类似的当地。
Linux中的信号量是一种睡觉锁。假如有一个使命企图取得一个已被持有的信号量时,信号量会将其推入等候行列,然后让其睡觉。这时处理器取得自在去履行其它代码。当持有信号量的进程将信号量开释后,在等候行列中的一个使命将被唤醒,然后便能够取得这个信号量。
三、自旋锁和信号量比照
在许多当地自旋锁和信号量能够挑选任何一个运用,但也有一些当地只能挑选某一种。下面比照一些两者的用法。
表1-1自旋锁和信号量比照
运用场合
信号量or自旋锁
低开支加锁(临界区履行时刻较快)
优先挑选自旋锁
低开支加锁(临界区履行时刻较长)
优先挑选信号量
临界区或许包括引起睡觉的代码
不能选自旋锁,能够挑选信号量
临界区坐落非进程上下文时,此刻不能睡觉
优先挑选自旋锁,即便挑选信号量也只能用down_trylock非堵塞的办法
四、自旋锁与linux内核进程调度联系
咱们评论下表1-1中的第3种状况(其它几种状况比较好了解),假如临界区或许包括引起睡觉的代码则不能运用自旋锁,不然或许引起死锁。
那么为什么信号量维护的代码能够睡觉而自旋锁就不能呢?
先看下自旋锁的完成办法吧,自旋锁的根本形式如下:
spin_lock(&mr_lock);
//临界区
spin_unlock(&mr_lock);
盯梢一下spin_lock(&mr_lock)的完成
#define spin_lock(lock) _spin_lock(lock)
#define _spin_lock(lock) __LOCK(lock)
#define __LOCK(lock) /
do { preempt_disable(); __acquire(lock); (void)(lock); } while (0)
注意到“preempt_disable()”,这个调用的功用是“关抢占”(在spin_unlock中会从头敞开抢占功用)。从中能够看出,运用自旋锁维护的区域是作业在非抢占的状况;即便获取不到锁,在“自旋”状况也是制止抢占的。了解到这,我想咱们应该能够了解为何自旋锁维护的代码不能睡觉了。试想一下,假如在自旋锁维护的代码中心睡觉,此刻产生进程调度,则或许别的一个进程会再次调用spinlock维护的这段代码。而咱们现在知道了即便在获取不到锁的“自旋”状况,也是制止抢占的,而“自旋”又是动态的,不会再睡觉了,也便是说在这个处理器上不会再有进程调度产生了,那么死锁天然就产生了。
咱们能够总结下自旋锁的特色:
● 单处理器非抢占内核下:自旋锁会在编译时被疏忽;
● 单处理器抢占内核下:自旋锁只是当作一个设置内核抢占的开关;
● 多处理器下:此刻才干彻底发挥出自旋锁的效果,自旋锁在内核中首要用来避免多处理器中并发拜访临界区,避免内核抢占形成的竞赛。
五、linux抢占产生的时刻
最终在了解下linux抢占产生的时刻,抢占分为用户抢占和内核抢占。
用户抢占在以下状况下产生:
● 从体系调用回来用户空间
● 从中止处理程序回来用户空间
内核抢占会产生在:
● 当从中止处理程序回来内核空间的时分,且其时内核具有可抢占性;
● 当内核代码再一次具有可抢占性的时分。(如:spin_unlock时)
● 假如内核中的使命显式的调用schedule()
● 假如内核中的使命堵塞。
根本的进程调度便是产生在时钟中止后,而且发现进程的时刻片现已运用完了,则产生进程抢占。一般咱们会使用中止处理程序回来内核空间的时分能够进行内核抢占这个特性来进步一些I/O操作的实时性,如:当I/O事情产生的是时分,对应的中止处理程序被激活,当它发现有进程在等候这个I/O事情的时分,它会激活等候进程,而且设置当时正在履行进程的need_resched标志,这样在中止处理程序回来的时分,调度程序被激活,本来在等候I/O事情的进程(很或许)取得履行权,然后确保了对I/O事情的相对快速呼应(毫秒级)。能够看出,在I/O事情产生的时分,I/O事情的处理进程会抢占当时进程,体系的呼应速度与调度时刻片的长度无关