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

进程的调度(超详细解读)

在特别老的操作系统中,进程的调度是根据FIFO调度算法进行调度,先进先出式的调度,其实就是队列,但是不能很好的体现进程的优先级,在上节讲解的进程优先级,知道nice值范围是[-20,19],然后创建一个进程默认优先级为80,所以真实的优先级范围是[60,99]。如果使用FIFO调度算法不能体现出进程优先级,虽然可以根据优先级队列插队,但是插队就会面临遍历,一旦遍历,时间复杂度就会大于O(1)。而且调度是一个非常频繁的过程,所以不可能让他的时间复杂度大于O(1)。

Linux中真实的调度算法(Linux内核O(1)调度算法)

实际上Linux操作系统为了体现优先级,他首先在自己的结构中有一个哈希,简单的说也就是数组,他的大小为140个元素,这个数组的类型是struct task_struct *(名)[140],每个元素类型为 task_struct *  。前一百个元素是给实时进程分配的,还剩四十个。

在上节说到进程的nice值范围刚好也是四十个数,刚好就对应了这剩下的四十个位置。为什么是四十个数,就是因为哈希表设置的大小是四十位。不能太多太多就影响公平性。

这是如果来了一个进程,他的优先级为61,这是我们根据算法:pir-statpir(60)+100。然后就可以确定该进程的位置,把该进程就可以挂在对应位置,因为上面说了元素类型为 task_struct *  ,如果又来了一个相同优先级的进程,就挂在他后面,相同优先级的挂在一切,优先级较小的就挂在数组靠后位置,这样做法时间复杂度就是O(1)。

调度他们的时候,就是从60位置开始往后依次找,如果该队列为空就往下找,不为空就取出调度。这样从前往后找,就是根据优先级来查找。

每一个CPU都有自己的运行队列,而这个队列中有两个这个哈希表。queue[140]。

根据上图,得出下面关系:

nr_active表示有多少个有效进程bitmap[5]是一个位图

active指向第一个数组位置,expired指向第二个数组位置。

1.CPU只会从active指向的数组中选择进程进行调度,时间片到了就选择下一个进程。CPU首先在runqueue中找到active,然后直接访问哈希表,从上往下进行扫描,为空就下一个,不为空就在该散列头部选取进程进行调度,纵向表示根据优先级顺序排队,横向根据先后顺序排队。

2.调度有三种情况:

  • 运行退出--->不放回调度队列
  • 不退出,但时间片到了
  • 有新的进程产生

借着有新的进程产生来说,为什么要维护两个队列呢?

1.先分析如果只有一个队列会有什么问题:

  如果CPU正在调度一个队列,CPU可能正在选择这个队列有哪些进程需要被选择,但是这时,如果突然了一个新进程,是不是要放在合适的位置,这时就会存在一个问题,对于这个新进程要插入,就是让OS进行插入,而CPU让OS进行调度,这时是新建还是调度呢?无论是哪种情况,都面临者,新建进程不断越来越多,只会影响当前进程的调度,更关键的是,我们有个非常重要的认识:调度器要非常均衡的进行进程调度!!!

调度器要非常均衡的进行进程调度,可是这与有一个概念非常冲突---优先级,优先级高的先被调度,如果新来的进程优先级特别高,一直插入在前面,那么CPU就一直在前面进行调度,导致后面的进程根本没时间进行调度。

所以一个进程优先级可以很高,但是不代表优先级越高就一定要一直优先吗?优先级越低,就一定要后调度吗?

如果只有一个队列,只要把一个进程不断调高(被调用完就调高优先级) ,或者用户启动更高优先级的进程,最后导致后面的进程一直不被调度,这种问题叫饥饿问题。

2.所以要维护两个队列

  所以要维护两个队列,一个active指向的队列,一个expired指向的队列,新建的进程不能进入active指向的队列中,只能进入expired队列中,同时,不退出时间片到了的进程也不能进入active指向的队列中,只能进入expired队列中,这样就可以保证active指向的队列永远是一个存量进程竞争的情况,即active队列中进程只会越来越少,不会增多。

而当active中进程全部调度完后,OS只需要交换active与expired指向的内容,让active指向第二个队列,expired指向第一个队列。

好处:
  1. swap之前按照优先级进行调度
  2. swap只后给了其他优先级高低的进程的调度机会
  3. 这样可以保证CPU可以非常均衡的进行调度

1.在回过头来看,为什么要在队列中维护一个nr_active呢?

因为nr_active决定了OS是否要进行交换!!!

2.为什么要有bitmap[5]呢?

如果新增一个进程,他插入时的操作时间复杂度是O(1),但如果后续调度呢?CPU要从这么多进程中进行选择,从上往下进行遍历哈希表,从60开始遍历检测数组,虽然拿取是O(1),但是检测数组哪个位置为空,哪个位置不为空,这样遍历虽然可以,但还是太慢了。

这时就维护了一个bitmap[5]来解决这个问题,为什么是5呢?因为bitmap是int类型32个比特位,5×32=160,160刚好覆盖140个位置,,用bitmap充当一个位图,bitmap第0个元素代表第一个位置,第一个元素代表第二个位置,以此类推。bitmap元素都是0或1,0表示位置为空,1表示该位置有进程,所以OS不需要遍历整个数组,只需要查看对应位图就可以了,为0不查,为1直接拿对应位置进程。

但是检测bitmap不还是要进行遍历吗?但是真实的检测方法如下:

检测方法是常数级的!!!

这就是Linux内核O(1)调度算法

所以进程是什么?进程是运行起来的程序,为什么进程是运行起来的程序,因为进程会被被CPU调度,会被切换。


http://www.mrgr.cn/news/66321.html

相关文章:

  • 使用WordPress快速搭建个人网站
  • conda迁移虚拟环境路径
  • 时刻练习基本功
  • 用Python遍历输出烟感名称和状态
  • Qt开发技巧(二十二)设置QPA,打开记忆文件,清除表单页注意判断存在性,工程文件去重添加,按钮组的顺序设置,Qt的属性用来传值,查找问题的方法
  • centos7 安装python3.9.4,解决import ssl异常
  • Day 49 || 1143.最长公共子序列、1035.不相交的线、 53. 最大子序和 、392.判断子序列
  • Java入门(8)--反射机制
  • 零基础学习Spring AI Java AI SpringBoot AI调用大模型OpenAi Ollama集成大模型
  • HarmonyOS开发 - Ability往页面(Pages)中传递数据
  • 年薪平均几十万?!哪些行业的软件测试工程师需求量大,前景好?
  • ubuntu工具 -- ubuntu服务器临时没有网络,急需联网下载东西怎么办? 使用手机提供网络
  • @ApiOperation(“修改帐号状态“)详细解释一下以上代码
  • 视频监控接入平台功能:视频平台系统的硬件性能直观显示和系统软件运行情况和状态显示
  • 【初阶数据结构篇】链式结构二叉树(续)
  • vue组件在项目中的常用业务逻辑(3)
  • 11.5 dmy NOIP模拟赛DAY4 总结
  • operator[ ]和迭代器,auto,for流,reserve
  • MySQL初学之旅(1)配置与基础操作
  • 数据库基础(4) . 数据库结构
  • Unity自动打包——Shell交互
  • 【C/C++】memcpy函数的使用
  • centos 6 yum安装 rabbitmq
  • 软硬链接与动静态库
  • 无需懂代码!用AI工具Bolt一键生成网站的入门指南!
  • RTX 50很快就能见面!3个月内 全家登场