当前位置: 首页 > news >正文

spinlock与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事件的处理进程会抢占当前进程,系统的响应速度与调度时间片的长度无关。 (作者:刘洪涛,华清远见嵌入式培训中心金牌讲师,ARM公司授权ATC讲师。

相关文章:

  • R学习 -- R中的five number
  • usb鼠标驱动注解及测试
  • R学习-- R中的绘图命令练习
  • Web Asp.net 中的Messagebox 判断..
  • R学习-- R连接 SQL SERVER
  • 使用AweSocket 收发消息
  • __asm__ __volatile__内嵌汇编用法简述
  • CLR 学习-- .net中的共享组件与GAC
  • PXA270嵌入式系统设计一:电源管理部分
  • Azure Machine Learning Studio 实例 -- 预测xx年的食物工厂总数
  • azure machine learning 预测分类实例-- 预测出口国
  • PXA270嵌入式系统设计二:时钟及复位部分
  • azure machine learning 机器学习算法的选择
  • linux内核对S3C2410睡眠模式的支持
  • 机器学习 -- 最小二乘法与梯度下降法
  • [译]Python中的类属性与实例属性的区别
  • 230. Kth Smallest Element in a BST
  • css的样式优先级
  • HTTP传输编码增加了传输量,只为解决这一个问题 | 实用 HTTP
  • idea + plantuml 画流程图
  • magento 货币换算
  • vue--为什么data属性必须是一个函数
  • 二维平面内的碰撞检测【一】
  • 每个JavaScript开发人员应阅读的书【1】 - JavaScript: The Good Parts
  • 山寨一个 Promise
  • 通过几道题目学习二叉搜索树
  • 物联网链路协议
  • 最近的计划
  • ​LeetCode解法汇总1410. HTML 实体解析器
  • #LLM入门|Prompt#2.3_对查询任务进行分类|意图分析_Classification
  • #NOIP 2014#day.2 T1 无限网络发射器选址
  • #我与Java虚拟机的故事#连载02:“小蓝”陪伴的日日夜夜
  • #周末课堂# 【Linux + JVM + Mysql高级性能优化班】(火热报名中~~~)
  • $分析了六十多年间100万字的政府工作报告,我看到了这样的变迁
  • (02)vite环境变量配置
  • (2/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序
  • (TOJ2804)Even? Odd?
  • (附源码)基于SpringBoot和Vue的厨到家服务平台的设计与实现 毕业设计 063133
  • (附源码)计算机毕业设计高校学生选课系统
  • (译) 函数式 JS #1:简介
  • (转)母版页和相对路径
  • (转)我也是一只IT小小鸟
  • ***详解账号泄露:全球约1亿用户已泄露
  • **PHP二维数组遍历时同时赋值
  • **python多态
  • ./和../以及/和~之间的区别
  • .net6 webapi log4net完整配置使用流程
  • @column注解_MyBatis注解开发 -MyBatis(15)
  • @JsonFormat与@DateTimeFormat注解的使用
  • @Query中countQuery的介绍
  • @select 怎么写存储过程_你知道select语句和update语句分别是怎么执行的吗?
  • [AutoSar]BSW_Com02 PDU详解
  • [AX]AX2012开发新特性-禁止表或者表字段
  • [BZOJ]4817: [Sdoi2017]树点涂色
  • [Electron] 将应用打包成供Ubuntu、Debian平台下安装的deb包