<script>function StorePage(){d=document;t=d.selection?(d.selection.type!='None'?d.selection.createRange().text:''):(d.getSelection?d.getSelection():'');void(keyit=window.open('http://www.365key.com/storeit.aspx?t='+escape(d.title)+'&u='+escape(d.location.href)+'&c='+escape(t),'keyit','scrollbars=no,width=475,height=575,left=75,top=20,status=no,resizable=yes'));keyit.focus();}</script>
作者:刘洪涛,华清远见嵌入式学院金牌讲师,ARM公司授权ATC讲师。
关于自旋锁用法介绍的文章,已经有很多,但有些细节的地方点的还不够透。我这里就把我个人认为大家容易有疑问的地方拿出来讨论一下。
一、自旋锁(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事件的处理进程会抢占当前进程,系统的响应速度与调度时间片的长度无关。
分享到:
相关推荐
MCS spinlock的Linux内核模块实现.pdf
linux内核调度与spinlock的相互关系.docx
一、自旋锁(spinlock)简介 自旋锁在同一时刻只能被最多一个内核任务持有,所以一个时刻只有一个线程允许存在于临界区中。这点可以应用在多处理机器、或运行在单处理器上的抢占式内核中需要的锁定服务。 二...
關於自旋鎖用法介紹的文章,已經有很多,但有些細節的地方點的還不夠透。我這裏就把我個人認為大家容易有疑問的地方拿出來討論一下。 一、自旋鎖(spinlock)簡介
Linux 2.4内核正好细化了多CPU下的内核线程同步机构,对不可并行的指令块用spinlock和rwlock作了细致的表示,该补丁的实现可谓水到渠成。具 体的方法就是在进程的任务结构上增加一个preempt_count变量作为内核抢占锁...
根据操作系统课程设计指导书所提供的基础知识,分析文件kernel/sched.c 和include/asm-i386/spinlock.h 中关于Linux的调度和时钟中断的代码,了解一个LINUX操作系统的进程调度以及时钟中断的处理方式,得到相关的...
详细描述了linux内核同步的方法,spinlock在不同情况下的详细用法
Linux 2.4内核正好细化了多CPU下的内核线程同步机构,对不可并行的指令块用spinlock和rwlock作了细致的表示,该补丁的实现可谓水到渠成。具体的方法就是在进程的任务...当 preempt_count为0时表示可以进行内核调度。
CPU在内核中运行时并不是处处不可抢占的,内核中存在一些空隙,在这时进行抢占是安全的,内核抢占补丁的基本原理就是将SMP可并行的代码段看成是可以进行内核抢占的区域。...当preempt_count为0时表示可以进行内核调度。
在主流的Linux内核中包含了几乎所有现代的操作系统具有的同步机制,这些同步机制包括:原子操作、信号量(semaphore)、读写信号量(rw_semaphore)、spinlock、BKL(Big Kernel Lock)、rwlock、brlock(只包含在...
linux系统中基于自旋锁的进程调度的实现, 有代码和详细的文档说明,自旋锁(spinlock) 是用C和汇编指令实现的,有助于了解linux系统 内核的加锁机制。 很不错的哦。。。
16:不可睡眠锁:自旋锁spinlock编码示例_rec 17:不可睡眠锁:RCUread-copy-update_rec 18:可睡眠锁-互斥量mutex_rec 19:可睡眠锁:信号量semaphore_rec 20:可睡眠锁:读写信号量rwsem_rec 21:可睡眠锁:完成...
本文主要以2.6.22.6内核分析Linux中spinlock在ARM及X86平台上的实现(不同版本的内核实现形式会有一些差异,但原理大致相同)。此处默认大家已经熟悉了spinlock的使用,重点解释容易引起迷惑的体系结构相关的实现...
因为自旋锁会关抢占,该进程一旦睡眠,该CPU上就无法再调度其他任务执行,只能响应中断 在实际使用中,在关抢占的情况下调用schedule,内核允许会报bug 2.3.5.3 自旋锁变种分析 自旋锁变种使用的原则就是谁抢CPU就...
Linux内核同步机制,挺复杂的一个东西,常用的有自旋锁,信号量,互斥体,原子操作,顺序锁,RCU,内存屏障等。这里说说它们的特点和基本用法。 自旋锁 :通用的 和读写的 特点: 1. 处理的时间很短。 2...
cas实现spinlock
goos: linux goarch: amd64 pkg: github.com/daskol/spinlock BenchmarkMutex/1-4 50000000 37.7 ns/op BenchmarkMutex/2-4 30000000 52.4 ns/op BenchmarkMutex/4-4 20000000 64.4 ns/o
extern spinlock_t rtc_lock; MODULE_AUTHOR("GM Corp."); MODULE_LICENSE("GM License"); extern int GM_i2c_xfer(struct i2c_msg *msgs, int num, int clockdiv); /* block read */ static int i2c_read_regs...
为了效率,不使用C++语言提供的Mutex互斥量,而使用不使用线程被阻塞的方式,即所谓的自旋锁,这是自旋锁的一种实现方式,使用C++11的原子变量,不用锁机制,实现的一种无锁的自旋锁
实验1 进程与线程 实验2 Linux系统调用 实验3 字符设备 实验4 基于SpinLock的进程调度