当内核从系统调用返回,或者从中断处理程序返回,内核都会检查当前进程是否设置了TIF_NEED_RESCHED标志;或者进程主动放弃CPU时(sched_yield,sleep或者收到SIGSTOP,SIGTTOP信号)都会进入主调度器。同样的我们先看一下主调度的框架部分,该部分就是sched.c:schedule(void):
关闭内核抢占
如果进程之前是不可运行并且被内核抢占了,那么如果它现在有非阻塞信号,则将它的状态改为TASK_RUNNING而且不移出就绪队列,否则该进程(不可运行)从就绪队列中取出deactivate_task
判断是否要进行负载均衡(当前运行队列为空)
通知调度器类将当前(活动)进程将被其它进程替换掉(
put_prev_task)
选择下一个将要被执行的进程(
pick_next_task),并清除前一个进程的TIF_NEED_RESCHED
进行上下文切换(context_switch)
重新计算新进程所在的cpu及rq,即为当前cpu(因为新的进程之前可能在不同的cpu上运行了,同样老的进程唤醒时也是所这里开始)
如果新的进程也被设置了TIF_NEED_RESCHED,则再次调度
大体过程如下图:


图 schedule与CFS的交互

下面我们主要来分析下CFS的相关的三个操作: deactivate_task :该函数最终调用CFS的dequeue_task_fair,并且将进程的p->se.on_rq置0,表示该进程不在运行队列里。 dequeue_task_fair 对于非组调度的话就是调用dequeue_entity更新执行进程的信息update_curr,把该se从buddies中去掉(clear_buddies,见后面的分析),如果这个se不是正在运行的进程则把该se从运行队列的红黑树上删除掉(运行的进程已经不存在红黑树里),置se->on_rq = 0,并且减少运行列队的相应load(update_cfs_load:这里更新的是统计值的load,account_entity_dequeue这个才是真正更新跟进程调度相关的cfs_rq->load)及se的weight(update_cfs_shares)其它统计信息(注意:当se出队时如果它不是DEQUEUE_SLEEP必须把vruntime标准化se->vruntime -= cfs_rq->min_vruntime,否则就不需要标准化, 这里不是很明白? )。对于组调度,它从当前进程开始dequeue_entity,如果它的父group load为0,那么说明这个父group也应该被 dequeue_entity ,直到不为0(该group有其它进程就绪)的祖先group为止,到这里就把从叶子(当前进程)到该进程向上递归load为空的父group都出队了;然后再更新从这个非空的父group到根的其余group se的load(这里只是更新统计的load update_cfs_load,而cfs_rq的load因为只记录它本层的se的load之和不递归,所以不需要再更新该load),shares及h_nr_running统计,因为它们下层的se已经被出队列了。另外,所有被dequeue的se的on_rq被置为0

put_prev_task_fair :该过程是与上一个函数不一样的,上一个把 不可运行 的进程从运行队列中删除掉,而 put_prev_task_fair 主要是通知CFS当前进程将会被调度出去了,如果当前进程已经不是可运行进程(on_rq=0), 那么这个函数只会把当前 cfs_rq->curr 置为 NULL ,表示当前cfs_rq没有进程正在运行,否则如果当前进程还是可运行的那么还需要对它的状态进行更新:update_curr更新它的实质物理运行时间,虚拟时间及它从现在开始就是进入等待的时间,并且再次将该进程重新入队列(__enqueue_entity当前进程还是可运行状态)。对于组调度同样的需要更新从该进程到它的根group的所有se,包括每个se的执行时间(这里的执行时间并不是代表它在cpu的执行时间,而是由它的下级执行时间的一个反映),至于每个层次的se都把它的cfs_rq->curr置为NULL是因为:在一个CPU上某一时刻只有一个进程在运行,当当前运行要被调度出去的时候,也代表了它的所有上层group在这个CPU上将被调度出去(对于group这只是一个理论概论,它并不会真正在CPU上运行,只是为了与真正task统一起来才有这个标志,表示当它的叶子task在CPU上运行;同样的,当某group的叶子被调度[运行]时,它的所有上层group在它所在的运行队列里也被表示为运行的)。

pick_next_task :挑选一个最需要运行的进程来运行。如果当前队列的等待运行的进程总数等于cfs等待的数目,那么就直接从cfs中挑选一个,否则从高优先策略的调度类中挑选一个进程来运行。这里我们直接看CFS的pick_next_task_fair(这里从根的cgroup开始一层一层往左边找):通过 pick_next_entity 从当前层的cfs_rq判断哪个se将被取出,它采用这样的优先级(从高到低)——已经被要求运行的se(cfs_rq->next,即next要求抢占),上一个运行的se(cfs_rq->last),不是被skip的se,而且这三个优先级都还需要满足——它们比起cfs_rq最左边的se更需要先被运行(wakeup_preempt_entity,它们的虚拟运行时间小于最左边的虚拟运行时间,或者比最左边再运行最小运行时间后的新的虚拟时间还小,减少不必要的切换);这样就能选出一个合适的se;然后调用set_next_entity将该se设置为当前cfs_rq上正在运行的进程:如果这个se还在运行队列里则更新它的等待结束时间,及出队列(运行进程不应该放在就绪队列里,注这里调用的是__dequeue_entity而不是 dequeue_entity ,后者是把这可运行的se出队列并需要更新nr_running--,on_rq=0等,而前者是不需要的,即运行的进程虽然不在红黑树里,但是se->nr_rq还是等于1,cfs_rq->nr_running还是包括这个运行的进程);更新开始执行的时钟,将cfs_rq->curr置为该se。对于非组调度这样就能把该se的task返回;而对于组调度其实也很简单,如果pick_next_entity取得的是一个group的话,那么再从它的运行队列里se->my_q里选出一个合适的se出来,直到该se是非group,而且这些group的se所在的cfs_rq也会把curr置为当前递归的group se(这也是我们上面说的put_prev_entity的反操作)。
    总之,schedule是为了完成从prev进程切换到next进程的过程,如果prev是不可运行的并且没收到信号那么应该先把它从运行队列里去掉(deactivate_task),注意此时还是它占用的CPU所以还需要更新它的执行时间(update_curr);然后告诉CFS该prev将要被调度出去了,此时也是需要考虑它是否是可运行的状态,还是不可运行状态,如果是不可运行状态,那么上面它已经被从运行队列中去掉,并且on_rq的标志也被清0,所以只需要把cfs_rq->curr置为NULL就可以了,否则就是它是可运行的,那么首先也是先更新它的执行时间update_curr,然后把它重新放到运行队列里(当前运行的进程是不在运行队列里的),最后同样把cfs_rq->curr置为NULL;接着从CFS里挑选一个合适的进程来执行,一些比较优先考虑的进程被保存在buddies(next,last),所以它先从这些里及最左筛选,筛选后把该se从运行队列中出队,相应的最后需要把cfs_rq->curr置为当前被筛选出来的se,表示该se是当前cfs_rq上运行的se。
         这样我们就把调度器两个主要部件介绍完了,下面介绍到task创建时的调度器对新任务的初始化过程。我们估且把该过程称为进程调度初始化,下面我们就来分析该过程。

linux调度器(四)——主调度器与CFS相关推荐

  1. 【Linux 内核】CFS 调度器 ④ ( 调度子系统组件模块 | 主调度器、周期性调度器 | 调度器类 )

    文章目录 一.调度子系统组件模块 二.主调度器.周期性调度器 三.调度器类 一.调度子系统组件模块 调度器 需要对 被调度的进程 进行 排序 和 调度管理 , 进程管理过程需要 调度器 的 组件模块 ...

  2. 深入理解Linux内核之主调度器(下)

    4.进程上下文切换 接前文:深入理解Linux内核之主调度器(上) 前面选择了一个合适进程作为下一个进程,接下来做重要的上下文切换动作,来保存上一个进程的"上下文"恢复下一个进程的 ...

  3. kernel调度(2)----主调度器和周期性调度器

    进程切换 上一章介绍了调度相关的基础知识,那么这章准备介绍一下进程切换相关的原理.包括主调度器和周期性调度器都做了哪些工作.以及进程切换都做了哪些工作. 周期性调度器都做了什么 我们知道周期性调度器就 ...

  4. Scheduler 学习之二:主调度器_schedule函数

    Summary 1.1 _schedule函数是又被称作为主调度器,负责选择新的task run到当前的cpu当中 1.2 _scheduler将选核的功能交给各调度类完成 1.3 pick_next ...

  5. 【Linux 内核】调度器 ① ( 调度器概念 | 调度器目的 | 调度器主要工作 | 调度器位置 | 进程优先级 | 抢占式调度器 | Linux 进程状态 | Linux 内核进程状态 )

    文章目录 一.调度器 0.调度器概念 1.调度器目的 2.调度器主要工作 3.调度器位置 4.进程优先级 5.抢占式调度器 二.Linux 内核进程状态 API 简介 三.Linux 进程状态 一.调 ...

  6. Linux调度系统全景指南(下篇)

    点击上方蓝字关注公众号,更多经典内容等着你 | 导语本文主要是讲Linux的调度系统, 由于全部内容太多,分三部分来讲,本篇是下篇(主要线程和进程),上篇请看(CPU和中断):Linux调度系统全景指 ...

  7. 鼠眼看Linux调度器

    一.耗子 vs Linux ? "鼠目寸光",应该是个暴光率挺高的成语了,常用来说某人看事情没有深度,看不透本质.毫无疑问,这是一个贬义100%的词.但不管是认识什么未知事物,都一 ...

  8. 鼠眼看Linux调度器 by raise_sail @ chinaunix

    来源:http://my.chinaunix.net/space.php?uid=20043340&do=blog&id=154837 /* 这是一篇4年前的文章.本人最近学习linu ...

  9. Linux调度器 - deadline调度器

    一.概述 实时系统是这样的一种计算系统:当事件发生后,它必须在确定的时间范围内做出响应.在实时系统中,产生正确的结果不仅依赖于系统正确的逻辑动作,而且依赖于逻辑动作的时序.换句话说,当系统收到某个请求 ...

  10. Linux 调度器发展简述

    引言 进程调度是操作系统的核心功能.调度器只是是调度过程中的一部分,进程调度是非常复杂的过程,需要多个系统协同工作完成.本文所关注的仅为调度器,它的主要工作是在所有 RUNNING 进程中选择最合适的 ...

最新文章

  1. COBOL 学习笔记 之 入門篇(续集)
  2. linux双网卡 ipv4 ipv6 双栈,ipv4 ipv6双栈哪个优先
  3. xp 远程桌面无法找到该计算机,XP系统中远程无法连接指定计算机的处理方法
  4. centos 32 mysql_CentOS 6.8 32位 安装mysql8
  5. 怎么在长方体上挖孔_被雷军狂喷的挖孔屏到底好不好?分析你必须知道的优缺点!...
  6. win10运行Flink
  7. 洪湖市计算机软件学校,湖北省教育厅关于公布“第十届湖北省中小学电脑制作作品评选”暨“第四届湖北省中小学信息技术创新与实践活动”获奖名单的通知...
  8. 使用Mockito创建Mcok和Spy
  9. comsol如何定义狄利克雷边界_comsol边界条件怎么设置
  10. 面向对象之反射,元类
  11. Electron编译报错:include: could not find: ****StdUtils.nsh“的解决
  12. 入门互联网IT行业就业前景如何?
  13. 【用pandas_alive几行代码绘制竞赛动图】10.新南威尔士州 COVID 可视化(测试代码+数据集+绘图参数解析)
  14. 华为云桌面--引领移动办公新潮流
  15. 如何提取出word里的图片
  16. xtu oj Patchouli的金字塔
  17. 利用 API 爬取数据,试着爬取 QQ 音乐流行指数榜
  18. 腾讯微搭小程序获取微信用户信息
  19. c语言中什么叫单变量,【单选题】具有单变量特征的是()。 A. 以上都不是 B. 推论 C. 关系 D. 属性...
  20. 基于人脸识别终端应用的智慧校园

热门文章

  1. 修改hosts文件无效?附解决办法
  2. spring cloud redis lock
  3. 『TensorFlow』读书笔记_TFRecord学习
  4. (油菜花)为什么别的项目中的category拖到自己项目中无法使用?
  5. BZOJ1085:[SCOI2005]骑士精神——题解+IDA*粗略讲解
  6. 将页面强制保持在所属框架中
  7. 学点PYTHON基础的东东--数据结构,算法,设计模式---访问者模式
  8. Python实战(3)指定的文本列求和求平均
  9. Java StringBuffer 用法
  10. 光伏行业缘何抢屋顶?