第4章 第三节 内核同步
发布日期:2021-06-30 18:59:18 浏览次数:2 分类:技术文章

本文共 2224 字,大约阅读时间需要 7 分钟。

640?wx_fmt=png640?wx_fmt=png640?wx_fmt=png640?wx_fmt=png640?wx_fmt=png

抢占式内核和非抢占式内核

Linux 内核有两个空间,一个是内核空间一个是用户空间,如果一个进程正在内核态执行的时候,允许内核打断他的执行,让另一个进程执行,那么这个内核就是可抢占式内核。

还有一种情况就是,进程执行到内核态的时候,不允许进程切换,就是说,一定要等到进程自己释放CPU使用权,这个内核就是非抢占式内核。

抢占式内核的优点非常明显,就是可以优先让那些优先级大的任务先执行,可以打断低优先级的进程,但是缺点也比较明显,就是内核抢占导致的问题就是要保护全局变量,静态变量,因为这些变量存在全局区,很有可能被打断后导致全局变量被修改。

非抢占式内核的缺点也很明显,不能让高优先级进程优先执行,我都拉肚子了,你还不让我上厕所,不得憋死我啊,优点也是很明显,非抢占式内核也不担心函数的可重入问题,因为一个时间片只有一个进程在内核空间执行,全局变量也不会被未知的纂改。

640?wx_fmt=png

非抢占式内核的任务调度逻辑

640?wx_fmt=png

抢占式内核的任务调度逻辑

内核抢占的设计逻辑

内核做什么事情呢?

内核的主要工作就是分配CPU时间给各个进程使用,并执行处于就绪态的最高优先级的进程,我们知道进程有三种状态,一个是就绪态,一个时执行态,一个时阻塞态,阻塞也就是睡觉了,他自己没事情干了,就睡觉了,就绪就是早上起床去排队准备上地铁,成功上了地铁其实就是进入了执行态了。

Linux 的调度是基于分时技术,因为CPU的时间被分成时间片,给每个就绪态的进程分一个时间片,调度算法不会在意那些阻塞的进程的,毕竟你们都睡觉了,我就不给你们准备午饭了,就调度这些就绪的进程好了。

那什么时候可以被调度呢?

中断和系统调用,中断执行结束后,内核调度器,如果只是说内核的话,有点笼统,内核里面有一个调度器专门用来调度进程的,这个内核调度器就去判断,当前是不是需要重新调度,系统调度也是一样,Linux 内核有无数个系统调度函数,而且系统调度会阻塞,阻塞的时候,CPU如果没事干会非常闲,那就需要调度到其他进程上去执行。

关于调度的知识后面再继续说明,我们先通过CPU的调度说明一个问题,因为多进程调度的原因,肯定会存在一个问题,就是资源竞态。

什么是资源竞态呢?

就是有多个进程去操作一个资源,可以是一个文件,一个变量等待,造成的结果就是不符合预期结果。

什么是内核同步?

我们在上一章说了原子操作,如果多进程对同一个资源操作,就有可能造成这个变量的不确定性,内核同步就是为了解决这种不确定的问题而出现的。

举个例子

有一个房间A里面放了一个篮球,房间没有锁,小明喜欢打篮球,这一天,他没有上课,来到了A房间拿走了篮球,小龙也喜欢打篮球,这一天,小龙也没有上课,小龙也想去A房间拿篮球,发现篮球没有了,所以小龙就没有篮球打了,小龙很生气,就跟老师说,为什么谁拿走了篮球也不知道,被偷了都不懂,为什么不给房间上个锁?

然后学校的老师听了小龙的建议,在A房间上了个锁,锁的钥匙就老师自己拿着,然后,又来了,小明又想去A房间拿篮球,这时候,发现房间上锁了,就只好去找老师拿钥匙,小明拿了钥匙过来开锁拿走了篮球,然后把A房间锁上了。

这时候,小龙也想去打篮球了,小龙就去找老师,老师说,钥匙已经被小明拿走了,要等小明把钥匙还回来,你才可以拿到钥匙,小龙就在旁边看书等着,这时候,小明把钥匙还回来了,老师把钥匙给了小龙,小龙就拿着钥匙开心的去A房间开房拿篮球了。

640?wx_fmt=png

内核同步的方法

内核信号量

老师突然发现,我应该给那些公共场所的教室都上锁,这样才能安全一些,嗯,就应该这么办?

但是电脑室有十几台电脑,只配一把钥匙感觉不够用,所以老师就配了和电脑数量一样的钥匙,拿到钥匙的同学才可以开门进去使用电脑,没有钥匙的同学要先去老师那里申请钥匙。

这个就是Linux 内核里面的信号量,内核信号量是一种睡眠的锁,如果有一个任务试图获得一个不可用的信号量时,信号量就会把这个任务放到一个等待队列里面,让这个任务睡眠,等这个信号量可以用后,再把这个任务唤醒。

信号量这个代码往上就很多了,我建议大家看代码最好是直接用内核源码来看,分开看很多东西都不是很明白,作为一个Linux 开发者,自己下载一份Linux源码应该是标配了吧,毕竟是开源的。

互斥锁

信号量可以用很多把钥匙,钥匙资源只有一个,那么就只能配一把钥匙,这时候就是二值信号量,也叫做互斥锁。

自旋锁

自旋锁和上面的最大区别就是,自旋锁等待的时候不会睡眠,会继续使用CPU时间,会不断的查询锁的状态,直到获取锁,所以我们如果使用自旋锁的时候,应该使用在那些临界区时间片非常短的资源上,要不然长时间占用CPU,这是对CPU性能的一大挑战。

至于这几个的代码实现方式,想放到下个章节再说明,我认为理解其中的原理,比如何实现更加重要,除了这些,还有有读写锁等等

技术

说明

适用范围

每CPU变量

在CPU之间复制数据结构

所有CPU

原子操作

对一个计数器原子地“读-修改-写”的指令

所有CPU

内存屏障

避免指令重新排序

本地CPU或所有CPU

自旋锁

加锁时忙等

所有CPU

信号量

加锁时阻塞等待

所有CPU

顺序锁

基于访问计数器的锁

所有CPU

本地中断的禁止

禁止单个CPU上的中断处理

本地CPU

本地软中断的禁止

禁止单个CPU上的可延迟函数处理

本地CPU

读-复制-更新(RCU)

通过指针而不是锁来访问共享数据结构

所有CPU

640?wx_fmt=jpeg

插播一个招聘

640?wx_fmt=png

转载地址:https://linus.blog.csdn.net/article/details/99688067 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:第四章 第四节 per_cpu
下一篇:你今天忘本了吗?

发表评论

最新留言

第一次来,支持一个
[***.219.124.196]2024年04月10日 05时54分56秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章