09 | 瞧一瞧Linux:Linux的自旋锁和信号量如何实现?
该思维导图由 AI 生成,仅供参考
Linux 的原子变量
- 深入了解
- 翻译
- 解释
- 总结
本文深入介绍了Linux内核中的自旋锁和信号量,这两种机制是用来解决数据同步问题的重要工具。首先,文章详细介绍了Linux中的原子变量的实现,展示了Linux如何保证对共享资源的原子操作。其次,对中断控制函数进行了详细讲解,包括保存和恢复eflags寄存器以及开启和关闭中断的操作。最后,文章介绍了Linux中的自旋锁,包括原始自旋锁和排队自旋锁的实现原理和优化。通过这些机制,Linux内核保证了对共享资源的安全访问和操作。此外,文章还介绍了Linux中的信号量,包括单值信号量和多值信号量的使用案例,以及信号量的工作原理和对进程状态的影响。通过本文的阐述,读者可以深入了解Linux内核同步机制的实现细节和工作原理,为进一步学习和应用提供了重要参考。 文章还介绍了Linux中的读写锁,它是一种适用于读取数据频率远大于修改数据的情况的锁机制。通过对读写锁的原理和实现进行详细讲解,读者可以了解其工作方式和优势。此外,文章还总结了Linux上实现数据同步的五大利器,包括原子变量、中断控制、自旋锁、信号量和读写锁,强调了在设计算法时应尽量避免使用锁,以降低性能损失。 总的来说,本文通过深入介绍Linux内核中的同步机制和锁机制,为读者提供了全面的技术视角和实践经验,有助于读者更好地理解和应用这些关键概念。
《操作系统实战 45 讲》,新⼈⾸单¥68
全部留言(58)
- 最新
- 精选
- Qfeng置顶回答思考题:Linux 的读写锁,因为每次读锁加锁计数-1,所以最多支持0x01000000个进程并发读取共享数据。 这样的读写锁的不足:读或者写锁拿不到时忙等,可以优化成trylock,拿不到可以先干其他的,等一段时间再尝试拿锁。(不知道回答的对不对) 感悟:不论是单值信号量还是多值信号量,亦或是原始自旋锁、trylock版本自旋锁还是读写锁,各种机制的设计和优化都是为了资源(CPU等)的更合理更高效的使用而优化。互斥机制有很多,理解每种锁机制重要,但是理解我们的业务更重要,这样才能因地制宜选择合适的锁。 老师简明扼要,点到即止的文风太赞了,谢谢。
作者回复: 是的
2022-03-2927 - springXu置顶同步与锁 操作系统是让应用程序认为当前有超大的内存空间和强大的cpu来工作的硬件环境,但其实硬件没有这么强大。那如何解决呢?比如在单核cpu上可以用分时技术,让进程交替执行。对于一个进程来说,我们可以把一个进程变成了多个线程来执行。但这样就产生了同一个资源可以是内存的某一具体地址,可以是鼠标可以是磁盘上的某一文件被多个线程访问和修改的问题。这两节课提供了解决思路,一个是cosmos操作系统的方案,一个是linux的方案。 1.原子性。就是硬件执行指令时不被打断。对于x86是复杂指令集。一条指令可以做读修改内存值的操作,指令集中直接支持锁定操作。对于精简指令集,就相对麻烦些,硬件会提供bitband的操作。 2.中断控制是在执行时,防止中断信号突然来了把当前执行的过程打断了。 解决方法就是关闭中断。让中断信号等到可以通知时,才发起通知。 3.自旋锁。在多核的cpu环境下,当前核心的cpu要访问的资源是有可能被其他核的cpu来访问的。如果产生这种情况,那就让其他核的cpu自己执行空转。一直到当前核心的cpu把访问资源让出后,其他核的cpu通过检测到了可以访问资源,不在空转执行相关操作恢复正常运行。而这个过程就是自旋锁。这里会有一点浪费cpu的运行效率。毕竟有个cpu在空转。当空转时间过长时,浪费的效能更大。我们需要更好的利用cpu核的方式来解决这个问题。那就是互斥。 4.信号量 对于单一资源的信号量也可以说是互斥锁。 互斥锁和自旋锁的区别就是原来那个空转的核不再空转,而是把当前运行的线程或者进程睡眠去执行其他的线程或者进程了。 当资源被适当后,去通知睡眠线程或者进程。这就是信号量。linux下新版本的信号量在被移除。
作者回复: 你好,铁子总结 到位
2021-05-30319 - 老男孩置顶这个排队自旋锁的实现方式感觉很风骚啊。关于读锁最大支持进程数是0x01000000(学友们都已经解答了)关于写饥饿的问题,既然写锁和读锁在同时获取锁状态时候写锁优先,那么就应该对读锁做一个限制,不能让读锁朝着最大数奔去。比如,系统检测到有写锁在等待,那么就限制新的读锁加入,等已经存在的读锁都释放了,写锁马上加锁更新资源。然后等待的读锁再开始加锁读取。这个等待的队列要分为读锁队列和写锁队列。优先处理写锁队列,在没有写锁的时候才能继续加读锁,如果有写锁等待,那么新的读锁不管超没超出那个最大数,都要进入读锁队列等待写锁完成后再开始自己的表演。
作者回复: 嗯嗯见解独到
2021-05-28434 - pedro以后我就是第一东吹了😁! 像这样的清晰明了,言简意赅的Linux内核源码解读实在是太少了,这样的文章读起来实在是太爽了,强烈小编安排一下东哥的下一个专栏叫做 《纵览Linux源码,小白也能学透》。 对于思考题答案,读并发进程的最大个数就是0x01000000,只要lock大于0都是可以共享数据的。 至于读写锁的不足,我个人觉得最不友好的点在于读写互斥上,由于读锁对写锁是互斥的,如果一直有人读,那么计数器一直小于0x01000000,加写锁时也一直小于0,写锁一直也不会成功,会陷入长时间的写饥饿状态,并且一直自旋,浪费CPU资源。 所以改进点就在于,给写进程配上一个休眠队列,待加锁失败进入队列休眠等待,待解读锁时判断计数器,决定是否唤醒队列中的写进程。 当然还有很多其它的优化点,欢迎大家集思广益~
作者回复: 哈哈 欢迎
2021-05-28229 - blentle回答一下思考题 1.理论上可以支持x01000000这么多进程,但实际上受限于文件句炳也就是文件描述符的限制,还有考虑多个线程的问题等等,注定最终远远小于这个值 2.读写锁造成写饥饿的情况是不是可以参考jdk的读写锁的实现,在条件等待队列中判断队列第一个元素是不是一个写进程,如果是写进程,让其直接优先获取锁.
作者回复: 嗯嗯
2021-05-2815 - GeekYanger文中: “//Linux没有这样的结构,这只是为了描述方便 typedef struct raw_spinlock { union { unsigned int slock;//真正的锁值变量 u16 owner; u16 next; } }raw_spinlock_t;” 这里老师为了帮助我们理解汇编代码构造了一个这样的结构体,我觉得,这个owner和next要被包在一个struct中才是老师想要表述的意思,不然owner和next的取值是一样的,都是低16位。
作者回复: 对 对 对 我大意了
2021-12-1336 - 子青老师,我有两个问题想请教 1 。Linux自旋锁,如果一个进程在获取锁之后挂了怎么办,没人给owner +1了,后面排队的进程岂不是永远等不到锁释放? 2。信号量那里,down是在链表的头部插入,up是唤醒链表的头部,这样不会有饥饿问题吗,链表后面的可能永远拿不到资源?
作者回复: 1.进程调用自旋锁,是在内核态运行的内核代码,如果在这个代码路径上挂了,那就说明内核有BUG 需要修正 2. up之后会对进程的优先级进行处理的,不会后面的进程没机会的
2021-09-2324 - Geek_8c4220为什么这节里实现自旋锁的时候都没有关中断了呢?
作者回复: 因为我没有讲
2021-06-094 - doos感觉不学习汇编和c很多都看不懂
编辑回复: 看你的目的,为了理解这门课,很多内容可以现搜的。汇编到初始化那里,后面都是C。具体有啥不懂之处,你可以再留言提问。
2022-04-213 - 疯码请问下为什么保存和恢复eflags那段代码用push pop而不是mov呢
作者回复: eflags寄存器不能使用mov指令访问
2022-01-183