Linux内核同步机制之基本概念

一、概述

近期深入的学习了 Linux 内核同步机制,将相关内容整理于此,既是一次梳理,也是一个分享,希望能帮助到读者一二。当然,所谓的深入也只是笔者现有的技术能力所能达到的程度而已。由于能力有限,有错误之处还请各位读者不吝指教,一起学习一起进步。

常用的 Linux 内核同步机制有原子操作、Per-CPU 变量、内存屏障、自旋锁、Mutex 锁、信号量和 RCU 等,后面几种锁实现会依赖于前三种基础同步机制。在正式开始分析具体的内核同步机制实现之前,需要先澄清一些基本概念。

二、基本概念

2.1 同步

既然是同步机制,那就首先要搞明白什么是同步。同步是指用于实现控制多个执行路径按照一定的规则或顺序访问某些系统资源的机制。所谓执行路径,就是在 CPU 上运行的代码流。我们知道,CPU 调度的最小单位是线程,可以是用户态线程,也可以是内核线程,甚至是中断服务程序。所以,执行路径在这里就包括用户态线程、内核线程和中断服务程序。执行路径、执行单元、控制路径等等,叫法不同,但本质都一样。那为什么需要同步机制呢?请继续往下看。

2.2 并发与竞态

并发是指两个以上的执行路径同时被执行,而并发的执行路径对共享资源(硬件资源和软件上的全局变量等)的访问则很容易导致竞态。例如,现在系统有一个 LED 灯可以由 APP 控制,APP1 控制灯亮一秒灭一秒,APP2 控制灯亮 500ms 灭 1500ms。如果 APP1 和 APP2 分别在 CPU1 和 CPU2 上并发运行,LED 灯的行为会是什么样的呢?很有可能 LED 灯的亮灭节奏都不会如这两个 APP 所愿,APP1 在关掉 LED 灯时,很有可能恰逢 APP2 正要打开 LED 灯。很明显,APP1 和 APP2 对 LED 灯这个资源产生了竞争关系。竞态是危险的,如果不加以约束,轻则只是程序运行结果不符合预期,重则系统崩溃。在操作系统中,更复杂、更混乱的并发大量存在,而同步机制正是为了解决并发和竞态问题。同步机制通过保护临界区(访问共享资源的代码区域)达到对共享资源互斥访问的目的,所谓互斥访问,是指一个执行路径在访问共享资源时,另一个执行路径被禁止去访问。关于并发与竞态,有个生活例子很贴切。假如你和你的同事张小三都要上厕所,但是公司只有一个洗手间而且也只有一个坑。当张小三进入厕所关起门的那一刻起,你就无法进去了,只能在门外侯着。当小三哥出来后你才能进去解决你的问题。这里,公司厕所就是共享资源,你和张小三同时需要这个共享资源就是并发,你们对厕所的使用需求就构成了竞态,而厕所的门就是一种同步机制,他在用你就不能用了。更多内容请查阅宋宝华老师的《Linux 设备驱动开发详解》一书中第七章第一节,书中详细列举了竞态发生的场景,总结如下图。

2.3 中断与抢占

        中断本身的概念很简单,本文不予解释。当然,这并不是说 Linux 内核的中断部分也很简单。事实上,Linux 内核的中断子系统也相当复杂,因为中断对于操作系统来说实在是太重要了。以后有机会,笔者计划开专题再来介绍。对于同步机制的代码分析来说,了解中断的概念即可,不需要深入分析内核的具体代码实现。抢占属于进程调度的概念,Linux 内核从 2.6 版本开始支持抢占调度。进程调度(管理)是 Linux 内核最核心的子系统之一,异常庞大,本文只简单介绍基本概念,对于同步机制的代码分析已然足够。通俗地说,抢占是指一个正愉快地运行在 CPU 上的 task(可以是用户态进程,也可以是内核线程) 被另一个 task(通常是更高优先级)夺去 CPU 执行权的故事。中断和抢占之间有着比较暧昧的关系,简单来说,抢占依赖中断。如果当前 CPU 禁止了本地中断,那么也意味着禁止了本 CPU 上的抢占。但反过来,禁掉抢占并不影响中断。Linux 内核中用 preempt_enable() 宏函数来开启本 CPU 的抢占,用 preempt_disable() 来禁掉本 CPU 的抢占。这里,“本 CPU” 这个描述其实不太准确,更严谨的说法是运行在当前 CPU 上的 task。preempt_enable() 和 preempt_disable() 的具体实现展开来介绍的话也可以单独成文了,笔者没有深究过,就不班门弄斧了,感兴趣的读者可以去 RTFSC。不管是用户态抢占还是内核态抢占,并不是什么代码位置都能发生,而是有抢占时机的,也就是所谓的抢占点。抢占时机如下:
       用户态抢占:1、从系统调用返回用户空间时;2、从中断(异常)处理程序返回用户空间时。
       内核态抢占:1、当一个中断处理程序退出,返回到内核态时;2、task 显式调用 schedule();3、task 发生阻塞(此时由调度器完成调度)。
       以上情况是个人理解,欢迎补充其它 case。

2.4 编译乱序与编译屏障

参阅笔者转载的这篇文章 https://blog.csdn.net/weixin_43555423/article/details/113481578

2.5 执行乱序与内存屏障

《Linux 设备驱动开发详解》一书中第七章第二节对编译乱序和执行乱序都有简略的介绍,通俗易懂,不可不读。不管是编译乱序还是执行乱序,都是为了提升 CPU 的性能。执行乱序是处理器运行时的行为,和 CPU 内部设计架构有关。而对于从事在 Linux 内核的程序员来说,要真正的理解透执行乱序所带来的软件方面的影响,首先需要搞清楚 cache 的概念。强烈建议关注奔跑 Linux 社区这个公众号,里面有三篇关于 cache 的文章,分为上中下,作者笨叔讲解的非常透彻。内存屏障是为了解决执行乱序引入的问题,个人认为是同步机制里最难理解的一块。关于内存屏障,可以参阅 wowo 翻译的这几篇文章,链接如下:

http://www.wowotech.net/kernel_synchronization/Why-Memory-Barriers.html  Why Memory Barriers?中文翻译(上)
http://www.wowotech.net/kernel_synchronization/why-memory-barrier-2.html  Why Memory Barriers?中文翻译(下)
http://www.wowotech.net/kernel_synchronization/memory-barrier-1.html  perfbook memory barrier(14.2章节)中文翻译(上)
http://www.wowotech.net/kernel_synchronization/perfbook-memory-barrier-2.html  perfbook memory barrier(14.2章节)中文翻译(下)

perbook 一书的全称是《Is Parallel Programming Hard And If So What Can You Do About It?》,是并行编程领域的权威著作,作者是大名鼎鼎的 Paul E. Mckenney。Paul 目前供职于 IBM Linux 技术中心,也是 Linux 社区 RCU 模块的领导者和维护者。perbook 的中文版本名称是 《深入理解并行编程》,由国产内核大神谢宝友倾情翻译。谢大神目前就职于阿里,他和 Linux 之间也有一段传奇经历。关于两位大神更多的趣闻轶事,感兴趣的读者可以上网搜寻。谢大神的翻译非常贴近原文,但读起来终觉比较晦涩,可能是笔者功力不够。wowo 的翻译则优先考虑的是汉语表述的流畅性,所以读起来会很顺畅,唯一美中不足的就是排版[笑哭]。笔者在学习过程中重新整理了排版,并删掉了英文原文,然后用红色字体加入了自己的一些读书笔记,有兴趣的读者朋友可以留言索取,欢迎一起交流。如果有的读者朋友英语很好,强烈建议你们去读原文。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值