linux调度器(四)——主调度器与CFS
当内核从系统调用返回,或者从中断处理程序返回,内核都会检查当前进程是否设置了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,则再次调度
大体过程如下图:
下面我们主要来分析下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相关推荐
- 【Linux 内核】CFS 调度器 ④ ( 调度子系统组件模块 | 主调度器、周期性调度器 | 调度器类 )
文章目录 一.调度子系统组件模块 二.主调度器.周期性调度器 三.调度器类 一.调度子系统组件模块 调度器 需要对 被调度的进程 进行 排序 和 调度管理 , 进程管理过程需要 调度器 的 组件模块 ...
- 深入理解Linux内核之主调度器(下)
4.进程上下文切换 接前文:深入理解Linux内核之主调度器(上) 前面选择了一个合适进程作为下一个进程,接下来做重要的上下文切换动作,来保存上一个进程的"上下文"恢复下一个进程的 ...
- kernel调度(2)----主调度器和周期性调度器
进程切换 上一章介绍了调度相关的基础知识,那么这章准备介绍一下进程切换相关的原理.包括主调度器和周期性调度器都做了哪些工作.以及进程切换都做了哪些工作. 周期性调度器都做了什么 我们知道周期性调度器就 ...
- Scheduler 学习之二:主调度器_schedule函数
Summary 1.1 _schedule函数是又被称作为主调度器,负责选择新的task run到当前的cpu当中 1.2 _scheduler将选核的功能交给各调度类完成 1.3 pick_next ...
- 【Linux 内核】调度器 ① ( 调度器概念 | 调度器目的 | 调度器主要工作 | 调度器位置 | 进程优先级 | 抢占式调度器 | Linux 进程状态 | Linux 内核进程状态 )
文章目录 一.调度器 0.调度器概念 1.调度器目的 2.调度器主要工作 3.调度器位置 4.进程优先级 5.抢占式调度器 二.Linux 内核进程状态 API 简介 三.Linux 进程状态 一.调 ...
- Linux调度系统全景指南(下篇)
点击上方蓝字关注公众号,更多经典内容等着你 | 导语本文主要是讲Linux的调度系统, 由于全部内容太多,分三部分来讲,本篇是下篇(主要线程和进程),上篇请看(CPU和中断):Linux调度系统全景指 ...
- 鼠眼看Linux调度器
一.耗子 vs Linux ? "鼠目寸光",应该是个暴光率挺高的成语了,常用来说某人看事情没有深度,看不透本质.毫无疑问,这是一个贬义100%的词.但不管是认识什么未知事物,都一 ...
- 鼠眼看Linux调度器 by raise_sail @ chinaunix
来源:http://my.chinaunix.net/space.php?uid=20043340&do=blog&id=154837 /* 这是一篇4年前的文章.本人最近学习linu ...
- Linux调度器 - deadline调度器
一.概述 实时系统是这样的一种计算系统:当事件发生后,它必须在确定的时间范围内做出响应.在实时系统中,产生正确的结果不仅依赖于系统正确的逻辑动作,而且依赖于逻辑动作的时序.换句话说,当系统收到某个请求 ...
- Linux 调度器发展简述
引言 进程调度是操作系统的核心功能.调度器只是是调度过程中的一部分,进程调度是非常复杂的过程,需要多个系统协同工作完成.本文所关注的仅为调度器,它的主要工作是在所有 RUNNING 进程中选择最合适的 ...
最新文章
- COBOL 学习笔记 之 入門篇(续集)
- linux双网卡 ipv4 ipv6 双栈,ipv4 ipv6双栈哪个优先
- xp 远程桌面无法找到该计算机,XP系统中远程无法连接指定计算机的处理方法
- centos 32 mysql_CentOS 6.8 32位 安装mysql8
- 怎么在长方体上挖孔_被雷军狂喷的挖孔屏到底好不好?分析你必须知道的优缺点!...
- win10运行Flink
- 洪湖市计算机软件学校,湖北省教育厅关于公布“第十届湖北省中小学电脑制作作品评选”暨“第四届湖北省中小学信息技术创新与实践活动”获奖名单的通知...
- 使用Mockito创建Mcok和Spy
- comsol如何定义狄利克雷边界_comsol边界条件怎么设置
- 面向对象之反射,元类
- Electron编译报错:include: could not find: ****StdUtils.nsh“的解决
- 入门互联网IT行业就业前景如何?
- 【用pandas_alive几行代码绘制竞赛动图】10.新南威尔士州 COVID 可视化(测试代码+数据集+绘图参数解析)
- 华为云桌面--引领移动办公新潮流
- 如何提取出word里的图片
- xtu oj Patchouli的金字塔
- 利用 API 爬取数据,试着爬取 QQ 音乐流行指数榜
- 腾讯微搭小程序获取微信用户信息
- c语言中什么叫单变量,【单选题】具有单变量特征的是()。
A. 以上都不是 B. 推论 C. 关系 D. 属性...
- 基于人脸识别终端应用的智慧校园