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

关抢占 自旋锁_Linux学习第28节,什么是自旋锁?内核是如何设计,如何实现它的...

上一节主要介绍了 Linux内核中的原子操作,在某种程度上避免了多个线程对同一全局变量的竞争问题。要是内核中的其他C语言程序开发中的临界区都能像上一节介绍的原子变量那样简单就好了。

15f41a692c403251d8dc675314b77ac8.png

然而事与愿违,在某个C语言项目中,可能某个临界区甚至会跨越多个函数。例如,函数 A 负责从共享数据结构中取出数据,函数 B 负责处理这些数据,函数 C 则负责将这些数据分发。显然,在这个过程中,要保护共享数据结构,仅仅依赖上一节介绍的原子操作是远远不够的,这就需要更为复杂的同步方法——锁。

自旋锁简介

Linux 内核开发中,最常使用的锁是自旋锁。如果线程 A 获得了自旋锁,其他线程再请求锁的时候就无法获得,必须等待线程 A 释放自旋锁。也就是说,一个自旋锁同时只能被一个线程持有,用其保护共享资源就太合适了。

若线程 B 请求一个已经被线程 A 持有的自旋锁,则线程 B 会一直执行类似 while(1); 的自旋动作,直到线程 A 释放自旋锁。能够看出,自旋等待非常耗费处理器,因此任何线程都不应该长时间持有自旋锁。

Linux 内核还有其他类型锁的设计,它会让线程 B 睡眠,直到线程 A 释放自旋锁的时候才重新唤醒线程 B。这样处理器就不必白白把时间花在等待上,而是可以在此期间执行其他代码。那为什么还要设计自旋锁呢?

89b944fb0c170a63b84ea8330bb80c53.png

其实,让线程 B 睡眠再唤醒也是有一定的开销的:至少有两次上下文切换。如果这两次上下文切换的开销超出了自旋锁让处理器等待的开销,那明显使用自旋锁更好些。可以看出,自旋锁是一种非常轻量级的设计,在抢占式的 Linux 内核开发中占有非常重要的席位。

自旋锁的C语言代码实现

使用自旋锁是简单的,基本操作就是初始化锁、请求锁、释放锁,用C语言代码描述这一过程就是:

DEFINE_SPINLOCK(lock);spin_lock(&lock);/* 临界区 */spin_unlock(&lock);

从上述C语言代码可以看出,在使用自旋锁之前,应先初始化,DEFINE_SPINLOCK() 是一个宏,它的C语言代码如下:

#define DEFINE_SPINLOCK(x) spinlock_t x = __SPIN_LOCK_UNLOCKED(x)
ab348255c89a10ccdfbafc17df8e2090.png

显然,DEFINE_SPINLOCK() 宏其实就是定义了一个 spinlock_t 类型的变量,并对其赋了初值,spinlock_t 是一个结构体,它的C语言代码如下,请看:

20 typedef struct {| 21 raw_spinlock_t raw_lock;| 22 #ifdef CONFIG_GENERIC_LOCKBREAK| 23 unsigned int break_lock;| 24 #endif| 25 #ifdef CONFIG_DEBUG_SPINLOCK| 26 unsigned int magic, owner_cpu;| 27 void *owner;| 28 #endif| 29 #ifdef CONFIG_DEBUG_LOCK_ALLOC| 30 struct lockdep_map dep_map;| 31 #endif| 32 } spinlock_t;
b38ff2510ef262b23f637ceac8e087fc.png

自旋锁初始化后,在进入需要保护的临界区之前,应先调用 spin_lock() 请求自旋锁。spin_lock() 也是一个宏,它的C语言代码如下,请看:

#define spin_lock(lock) _spin_lock(lock)

继续跟踪 _spin_lock(),会发现其实自旋锁的最终核心功能由 __raw_spin_lock() 函数实现, __raw_spin_lock()函数的实现是因平台而异的,在x86 平台下,它的C语言代码如下,请看:

135 static __always_inline void __raw_spin_lock(raw_spinlock_t *lock)- 136 {| 137 int inc = 0x00010000;| 138 int tmp;| 139 | 140 asm volatile("lock ; xaddl %0, %1"| 141 "movzwl %w0, %2"| 142 "shrl $16, %0"| 143 "1:"| 144 "cmpl %0, %2"| 145 "je 2f"| 146 "rep ; nop"| 147 "movzwl %1, %2"| 148 /* don't need lfence here, because loads are in-order */| 149 "jmp 1b"| 150 "2:"| 151 : "+Q" (inc), "+m" (lock->slock), "=r" (tmp)| 152 :| 153 : "memory

相关文章:

  • 2019pro与air怎么选_iPad Air 2019 VS iPad Pro 10.5 | 普通人的角度简单思考
  • mysql安装教程与启动_MySql安装启动两种方法教程详解
  • apparmor mysql_Ubuntu 上更改 MySQL 数据库数据存储目录
  • mysql工程师需要会哪些_MySQL面试高频100问(工程师方向)
  • mysql 客户端 连接数_监控mysql上客户端的连接数
  • mysql带库名查询_MySQL优化
  • docker mysql 差8小时_docker之容器日志输出与系统时间相差8小时解决办法
  • java白盒测试问题_白盒测试项目实践经验总结(三)-返回码问题
  • 9点到17点半 cron_定时任务Quartz简单配置与cron表达式
  • python3 web服务器_python3实现微型的web服务器
  • python if else简写_python代码简写(推导式 if else for in)
  • mysql的终端窗口是什么意思_什么叫终端窗口
  • python给list添加元素_在python中正确地向ListStore添加元素
  • MySQL 备份 s3_linux – 完整的数据备份到Amazon S3
  • mysql5.6双向同步配置_mysql5.6数据库同步,单向双向同步问题
  • 【comparator, comparable】小总结
  • Date型的使用
  • Java多线程(4):使用线程池执行定时任务
  • Java教程_软件开发基础
  • JSDuck 与 AngularJS 融合技巧
  • Netty 4.1 源代码学习:线程模型
  • React16时代,该用什么姿势写 React ?
  • SpiderData 2019年2月25日 DApp数据排行榜
  • Unix命令
  • ViewService——一种保证客户端与服务端同步的方法
  • 从伪并行的 Python 多线程说起
  • 关键词挖掘技术哪家强(一)基于node.js技术开发一个关键字查询工具
  • 如何优雅地使用 Sublime Text
  • 携程小程序初体验
  • 由插件封装引出的一丢丢思考
  • 怎么将电脑中的声音录制成WAV格式
  • ​第20课 在Android Native开发中加入新的C++类
  • ​软考-高级-信息系统项目管理师教程 第四版【第23章-组织通用管理-思维导图】​
  • #[Composer学习笔记]Part1:安装composer并通过composer创建一个项目
  • #【QT 5 调试软件后,发布相关:软件生成exe文件 + 文件打包】
  • #Linux(Source Insight安装及工程建立)
  • (C语言)输入自定义个数的整数,打印出最大值和最小值
  • (HAL)STM32F103C6T8——软件模拟I2C驱动0.96寸OLED屏幕
  • (LeetCode 49)Anagrams
  • (附表设计)不是我吹!超级全面的权限系统设计方案面世了
  • (附源码)springboot 校园学生兼职系统 毕业设计 742122
  • (数位dp) 算法竞赛入门到进阶 书本题集
  • (译)计算距离、方位和更多经纬度之间的点
  • (转)Groupon前传:从10个月的失败作品修改,1个月找到成功
  • .halo勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复
  • .NET 除了用 Task 之外,如何自己写一个可以 await 的对象?
  • .NET 跨平台图形库 SkiaSharp 基础应用
  • .NET与 java通用的3DES加密解密方法
  • 。Net下Windows服务程序开发疑惑
  • @entity 不限字节长度的类型_一文读懂Redis常见对象类型的底层数据结构
  • @Pointcut 使用
  • @property python知乎_Python3基础之:property
  • @Transactional 竟也能解决分布式事务?
  • [ 2222 ]http://e.eqxiu.com/s/wJMf15Ku
  • [<事务专题>]