[原创] 《奔跑吧Linux内核(第2版)卷2:调试与案例分析》 - 第一章 并发与同步

y909334873   2024-3-17 18:40 楼主

       对于一个linux的初学者来做说,在看到这本书的时候是比较吃力的,在读这本卷二的同时,也第一本卷一开始同步的读了起来,所以后续在文章或者一些点上的理解可能并不是很准确甚至有可能有问题,也欢迎指出。另外很高兴参加这次的书籍的试用,其实对我来做可能更好的能有一个力来促使我去坚持读完这本书,来避免我给找些理由来拖延这件事情,还是感谢论坛和作者。

        那么现在开始我读第一章的一些感受,一开始我看目录的时候,我感觉这一章应该预计很快的时间,毕竟谁又没有频繁接触使用并发和同步呢?但是正如一碗蛋炒饭都炒不好又怎么能算一个好厨子呢?章节的结构正如卷1一致的风格,以高频面试问题开头,提供了设计这个章节的各种问题,我大概看了这些问题,然后在看后续的小节,看到找到一个问题的答案,然后在继续找下一个问题的过程中还是蛮能激发我的阅读乐趣的,看完整个章节之后,在回头看看这些问题,全部掌握了也就算入门了这个知识点了。但是不结合实际的使用,也总会有纸上谈兵的感觉。但是看这本书的目的,能在后续遇到相关的问题的时候能有这方面的考虑,我感觉就是值得的。

        章节1-并发与同步,从原子操作->自旋锁->信号量->RCU,有熟悉的原子操作,信号量、互斥锁,也有相对陌生的操作,看着一小节的讲解描述,就能感觉到并发同步一个个的局限和优化,有的可以功能上看似一致,但是在某些场景里会更建议使用其中一种,因为它可以更快,更省资源,当然它也就有了自身局限性。比如原子操作开销小,但是它用于临界区的数据变量

位等简单数据结构。比如自旋锁又有着特定的发展,MCS锁、排队自旋锁等等。信号量和互斥锁,以前其实还蛮经常会用到,使用的过程中老感觉两者有一定的相似性,但是只知道在这个场景下应该用信号量,那个场景下应该使用互斥锁这种惯性使用方法。书中这么讲到“既然互斥锁类似于count值等于1的信号量,为什么内核社区要重新开发互斥锁,而不是复用信号量的机制呢?”,这是因为互斥锁相对于信号量要简单轻便一些。在锁争用激烈的测试场景下,互斥锁比信号量执行速度更快,可扩展性更好。当然互斥锁的优化方案也移植到读写信号量中了。其中互斥锁中实现乐观自旋等待机制,乐观自旋等待如其名当发现锁持有者正在临界区执行并且没有其他优先级高的进程要调度时,当前进程坚信锁持有者会很快离开临界区并释放锁,因此与其睡眠等待,不如乐观地自旋等待,以减少睡眠唤醒的开销。还特地去搜索一下,乐观锁和悲观锁也是如其名乐观锁乐观的把情况都想成好的,也有之对应的悲观锁把所有的都想成坏的,所以一个会阻塞修改拿锁,一个相信你不会修改。下面的书中的图表更直观理解互斥锁在各个阶段的状态,能反映在临界区,睡眠乐观自旋等待的各个状态。

图片.png  

后面提到的读写锁,完成了信号量没有区分临界区读写属性的这一问题,通过区分读写属性,读属性并不会完成修改,所以允许多个读属性线程并发进入,而写属性还是依旧只能单一线程进入,同时也不能读写进程同时进入。其实从整个章节来看,是一种由浅入深或者更准确的讲是从原材料讲到半成品再到各种各样的成品,原子操作跟内存屏障就像原材料,自旋锁就像是半成品,后续的信号量互斥锁,读写锁就像是各种成品。最后一节的RCU更像一个物尽其用的特制品一般.RCU机制的原理可以概括为RCU记录了所有指向共享数据的指针的使用者,当要修改共享数据时,首先创建一个副本,在副本中修改。所有读者线程离开读者临界区之后,指针指向修改后的副本,并且删除旧数据。简单来说大家读者完之后再更新数据。针对于读写进程,读进程没有或者只有很小的同步开销,所以摒弃了原子操作和内存屏障,把同步的工作交给写线程。下面提供一下书中关于锁机制的使用场景和特点,也欢迎补充。

 

锁 机 制 特点 使用规则
原 子 操 作 使用处理器的原子指令,开销
临界区中的数据是变量、位等简单的数据结
内 存 屏 障 使用处理器内存屏障指令或GCC
的屏障指令
读写指令时序的调整
自 旋 锁 自旋等待 中断上下文,短期持有锁,不可递归,临界
区不可睡眠
信 号 量 可睡眠的锁 可长时间持有锁
读 写 信 号 量 可睡眠的锁,多个读者可以同
时持有锁,同一时刻只能有一
个写者,读者和写者不能同时
存在
程序员界定出临界区后读/写属性才有用
互 斥 锁 可睡眠的互斥锁,比信号量快
速和简洁,实现自旋等待机制
同一时刻只有一个线程可以持有互斥锁,由
锁持有者负责解锁,即在同一个上下文中解
锁,不能递归持有锁,不适合内核和用户空
间复杂的同步场景
RCU 读者持有锁没有开销,多个读
者和写者可以同时共存,写者
必须等待所有读者离开临界区
后才能销毁相关数据
受保护资源必须通过指针访问,如链表等

 

阅读这一章节的过程中,其实第一遍大概在第二天已经初步看完了,但是每次反复看都有新的收获和学习点,可能在后续的过程中还是会时不时的翻看这一部分,同时建议在读的时候也可以借助一些类似chatgpt的工具,能更快的理解,也能搜索一些相关的名词和术语,同时如果可以的话还是要先看看作者的卷一,章节中有些名词或者知识都在卷1中有所说明,这样或许更能循序渐进,水到渠成。针对于上文提到的信号量,读写锁,RCU书中有更详细的说明和讲解,配合程序框图和一些案例图示能更方便的理解,也因为自己也是一个初学者,防止对曲解意思,就不在这个上面多多赘述,有兴趣的同学可以拜读原文。希望自己能每周坚持更新新的章节阅读,

 

最后看一下章节开始的问题,看看能够答出几个吧?也许你也需要读读这本书了

1、在ARM64处理器中,如何实现独占访问内存?
2、请举例说明内核使用内存屏障的场景?

3、为什么自旋锁的临界区不能睡眠(不考虑RT-Linux的情况)?

4、Linux内核中经典自旋锁的实现有什么缺点?为什么自旋锁临界区不允许抢占?

5、请阐述原子操作、自旋锁、信号量、互斥锁以及RCU的特点和使用规则。在KSM中扫描某个VMA以寻找有效的匿名页面时,假设此VMA恰巧被其他CPU销毁了,会不会有问题呢?

6、在mm/rmap.c文件中的page_get_anon_vma()函数中,为什么要使用rcu_read_lock()函数?什么时候注册RCU回调函数呢?在mm/oom_kill.c的select_bad_process()函数中,为什么要
使用rcu_read_lock()函数?什么时候注册RCU回调函数呢?
 

 
 

 

Hello astroturfers

回复评论 (2)

为什么自旋锁的临界区不能睡眠(不考虑RT-Linux的情况)?

看来还需要读读这本书

点赞  2024-3-18 07:47
引用: Jacktang 发表于 2024-3-18 07:47 为什么自旋锁的临界区不能睡眠(不考虑RT-Linux的情况)? 看来还需要读读这本书

自旋锁的临界区中主动睡眠以让出CPU,这样可能导致其他持锁的阻塞,导致并发的一个死锁的bug.RT-Linux不考虑这个我专门查了一下,是因为RT-Linux有优先级继承,睡眠等待锁机制来避免死锁和优先级反转问题。其他也有一些也有类似的支持。

Hello astroturfers
点赞  2024-3-18 10:36
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复