首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >CFS Scheduler(CFS调度器)

CFS Scheduler(CFS调度器)

作者头像
DragonKingZhu
发布于 2020-03-24 08:27:13
发布于 2020-03-24 08:27:13
1.7K00
代码可运行
举报
运行总次数:0
代码可运行

前面我们分享了O(n)和O(1)调度器的实现原理,同时也了解了各个调度器的缺陷和面临的问题。总的来说O(1)调度器的出现是为了解决O(n)调度器不能解决的问题,而O(1)调度器在Linux2.4内核的在服务器的变形是可行的,但是Linux2.4以后随着移动设备的逐渐普遍,面临的卡顿问题逐渐明晰,这才导致后来的CFS调度器的出现。

本节我们重点来关注下CFS调度器实现,在学习CFS代码之前,我们先看CFS的实现原理,搞清楚它的来龙去脉,以及为啥CFS调度器需要这样设计,基本就可以掌握CFS调度器了。

CFS引入

完全公平调度器(CFS)最早是在2017年merged进Linux2.6.23版本中的,一直到现在都是系统中默认的调度器。内核文章中的sched-design-CFS.txt文档对CFS调度器有一个简单的介绍。

80% of CFS's design can be summed up in a single sentence: CFS basically models an "ideal, precise multi-tasking CPU" on real hardware.

这句话的意思是CFS的80%的设计总结起来就一句话“在一个真实的硬件上,实现公平,精确的多任务CPU”

"理想的,精确的,多任务CPU"这句话是啥意思呢? 到底怎么理解呢?我们来通过例子做下解释

"Ideal multi-tasking CPU" is a (non-existent :-)) CPU that has 100% physical power and which can run each task at precise equal speed, in parallel, each at 1/nr_running speed. For example: if there are 2 tasks running, then it runs each at 50% physical power --- i.e., actually in parallel.

内核文档是这样说的。"理想的,多任务CPU"是在同一时刻每个任务以1/nr_running_speed来运行,也就是同一时刻每个进程瓜分CPU的时间是相同的。例如如果有两个进程运行的话,每个进程占有50%的CPU时间。

举一个例子:

两个批处理进程,总共只能运行10ms。

实际情况:每个进程运行5ms,占有100%的CPU利用率

理想情况:每个进程运行10ms,占有50%的CPU利用率。

而所谓的理想情况就是CFS提到的"Ideal multi-tasking CPU"

上述的例子在一个单核CPU,只有一个处理核心的CPU上是完全不可能的,因为同一时刻只能运行一个进程,另外一个进程必须等待。而CFS就是为了的达到完全公平调度,它应该怎么做呢?

如何才能实现完全公平调度

在O(n)调度器和O(1)调度器中,我们知道都是通过优先级来分配对应的timeslice,也就是时间片。而这些时间片都是固定的。比如在O(n)调度器中nice0对应的时间片是60ms。而在CFS调度器中,不再有时间片的概念。而是根据当前系统中可运行进程的总数来计算出进程的可运行时间的。

在O(n)调度器和O(1)调度器中,普通进程都是通过nice值来获取对应时间片,nice值越大获取的时间片就越多,运行机会就越多。而在CFS调度器中引入了权重weight的概念,通过nice值转化为对应的权重,优先级越高的进程对应的权重就越大,意味着就可以获得更多的CPU时间。

则进程占用CPU的时间 =

进程的weight / 总的可运行进程weight

CFS是让进程通过运行一段时间来达到公平,进程占用的时间就是进程的weight占总的可运行进程的总权重的比值。

举例:总共10ms的时间,单核cpu

  • 进程的优先级相同:

如果两个进程的优先级相同,则对应的权重相同,则每个进程占用5ms的CPU时间;如果有5个进程,每个进程占用2ms的CPU时间;如果共10个进程,每个进程占用1ms的CPU时间。

  • 进程的优先级不同:

如果两个进程的优先级不同,比如A进程nice是0,B的nice值1,则A进程的优先级就高,weight就越大,对应B的优先级小,weight也小于A。假设A的权重是6,B的权重是4。则A占2/3的CPU时间,B占1/3的CPU时间。

这样一来就达到了公平性,每个进程在各子拥有的权重比例下,占用不同份额的CPU时间。

再结合生活举一例:

公司发年终奖,一般来说一个部门的总包(CPU时间)是固定的。而为了公平老板不会给每个人发同样的奖金的,这样反而不公平了。而是通过平时在公司的表现,工作的认真态度之类(进程的weight)来衡量,比如张XX很辛苦,经常加班,进程出差,年终奖(进程占用CPU的时间)就多发。刘XX经常迟到,下班就没人了,年终奖(进程占用CPU的时间)少发。这样就显得公平。

CFS调度器是如何选择进程的

CFS的目标是让各个进程在一段时间内实现公平,也就是根据进程的权重来瓜分CPU的时间。权重越大则瓜分的CPU时间就越多,分配的CPU时间多就等同于有更大的机会得到CPU。

CFS调度是通过进程的虚拟时间vruntime来选择要运行的进程。vruntime的计算公式如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
vruntime = (wall_time * NICE0_TO_weight) / weight

其中,wall_time代表的是进程实际运行的时间,NICE0_TO_Weight代表的是nice值等于0对应的权重,weight就是该进程对应的权重。可以看出vruntime的值其实是实际运行时间乘以nice0对应的weight除以进程的权重。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/*
 * Nice levels are multiplicative, with a gentle 10% change for every
 * nice level changed. I.e. when a CPU-bound task goes from nice 0 to
 * nice 1, it will get ~10% less CPU time than another CPU-bound task
 * that remained on nice 0.
 *
 * The "10% effect" is relative and cumulative: from _any_ nice level,
 * if you go up 1 level, it's -10% CPU usage, if you go down 1 level
 * it's +10% CPU usage. (to achieve that we use a multiplier of 1.25.
 * If a task goes up by ~10% and another task goes down by ~10% then
 * the relative distance between them is ~25%.)
 */
const int sched_prio_to_weight[40] = {
 /* -20 */     88761,     71755,     56483,     46273,     36291,
 /* -15 */     29154,     23254,     18705,     14949,     11916,
 /* -10 */      9548,      7620,      6100,      4904,      3906,
 /*  -5 */      3121,      2501,      1991,      1586,      1277,
 /*   0 */      1024,       820,       655,       526,       423,
 /*   5 */       335,       272,       215,       172,       137,
 /*  10 */       110,        87,        70,        56,        45,
 /*  15 */        36,        29,        23,        18,        15,
};

此表就是nice值和weight的转换变,此表已经计算好了,在代码中需要计算vruntime的时候,只需要根据nice值查表即可。

从注释上可以看出,当nice增大一个台阶,将会减少10%的CPU占用时间;当nice值减少一个台阶,将会获得10%的cpu时间。

从上面计算vruntime的公式可以得出,nice等于0的进程的虚拟时间等于物理时间。当一个进程的weight越大,则对应的进程的vruntime就越小;当一个进程的weight越小,则对应的vruntime就越大。

CFS每次调度原则是,总是选择vruntime最小的进程来调度,vruntime最小的进程weight越大,优先级越高,则就需要更高的获取CPU的时间。

举例说明:总共6ms的时间,有3个进程,一个进程A的权重是1024,另外一个进程B的权重是335,进程C的权重是3121

进程A vruntime = (2ms * 1024) / 1024 = 2ms, CPU占用率 = 1024/(1024+335+3121) = 22%

进程B vruntime = (2ms * 1024) / 335 = 6ms,CPU占用率 = 335/ (1024+335+3121) = 7%

进程C vruntime = (2ms * 1024) / 3121 = 0.65ms,CPU占用率 = 3121/ (1024+335+3121) = 70%

可以看出

  1. 各个CPU利用率都是相差50%,因为nice值每增加一个台阶,CPU占用率有10%的差别
  2. 进程的权重越大,分母也就越大,则vruntime则就越小,而在下一次选择进程时则高优先级选择它
  3. nice0=1024权重的进程的虚拟时间和物理时间是一样的
  4. 可以理解权重越大,虚拟时间越小,对应的虚拟时间轴跑的越快
  5. 权重越小,虚拟时间越大,对应的虚拟时间轴跑的越慢

调度周期(sched_period)

之前说过一个进程占用的CPU时间是根据进程的weight和系统中总的可运行进程的权重的比值。

进程占用CPU的时间 =

进程的weight / 总的可运行进程weight

比如两个优先级相同进程,总共10ms的时间,每个进程占用5ms。当系统中可运行的进程数目逐渐增多,则每个进程占用的cpu的时间就会越来越小,趋近于0。这就会导致进程之前频繁的发生上下文切换,CPU的大多数时间是用来处理进程的上下文切换了,导致系统效率下降。

所以对于此问题再CFS中则引入了调度周期,调度周期的计算通过如下代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/*
 * The idea is to set a period in which each task runs once.
 *
 * When there are too many tasks (sched_nr_latency) we have to stretch
 * this period because otherwise the slices get too small.
 *
 * p = (nr <= nl) ? l : l*nr/nl
 */
static u64 __sched_period(unsigned long nr_running)
{
	if (unlikely(nr_running > sched_nr_latency))
		return nr_running * sysctl_sched_min_granularity;
	else
		return sysctl_sched_latency;
}

static unsigned int sched_nr_latency = 8;
unsigned int sysctl_sched_latency			= 6000000ULL;
unsigned int sysctl_sched_min_granularity			= 750000ULL;

从注释上看,这个函数的目的是为了让每个进程都可以运行一次。当系统中的进程数目逐渐增大时,则需要增大调度周期。

当进程的数目小于8时,则调度周期等于调度延迟等于6ms。当系统的进程数目大于8时,则调度器周期等于进程的数目乘以0.75ms。sysctl_sched_min_granularity可以理解为在一个调度周期中一个进程至少保证执行0.75ms。

CFS总结:

  • 在O(n)和O(1)调度器中都是通过nice值来分配固定的时间片,CFS中没有时间片的概念
  • CFS调度器中通过进程的静态优先级来计算进程的权重,进程的权重就代表了此进程需要获取的CPU的时间比例
  • 通过进程的weight和进程的实际运行时间来计算进程的vruntime虚拟时间。
  • 当进程加入到运行队列,调度器会时刻来更新进程的vruntime,来达到公平
  • 调度器每次调度的时候只选择运行队列中虚拟时间最小的进程,当此进程运行一段时间后,vruntime就会变大
  • 这时候就需要调度时候就需要重新选择新的最小vruntime的进程来执行,上次被调度出去的进程则就需要根据vrumtime的值来选择自己在运行队列的位置
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
CFS调度器(1)-基本原理
首先需要思考的问题是:什么是调度器(scheduler)?调度器的作用是什么?调度器是一个操作系统的核心部分。可以比作是CPU时间的管理员。调度器主要负责选择某些就绪的进程来执行。不同的调度器根据不同的方法挑选出最适合运行的进程。目前Linux支持的调度器就有RT scheduler、Deadline scheduler、CFS scheduler及Idle scheduler等。我想用一系列文章呈现Linux 调度器的设计原理。
233333
2022/05/10
9570
CFS调度器(1)-基本原理
Linux CFS调度器之虚拟时钟vruntime与调度延迟--Linux进程的管理与调度(二十六)
CFS为了实现公平,必须惩罚当前正在运行的进程,以使那些正在等待的进程下次被调度。
233333
2018/12/13
3.5K0
吐血整理 | 肝翻 Linux 进程调度所有知识点
前面我们重点分析了如何通过 fork, vfork, pthread_create 去创建一个进程或者线程,以及后面说了它们共同调用 do_fork 的实现。现在已经知道一个进程是如何创建的,但是进程何时被执行,需要调度器来选择。所以这一节我们介绍下进程调度和进程切换的详情。
刘盼
2021/12/13
2.2K0
吐血整理 | 肝翻 Linux 进程调度所有知识点
K8S 中的 CPUThrottlingHigh 到底是个什么鬼?
这个告警信息说明 kube-proxy 容器被 throttling 了,然而查看该容器的资源使用历史信息,发现该容器以及容器所在的节点的 CPU 资源使用率都不高:
米开朗基杨
2020/10/30
10.3K0
K8S 中的 CPUThrottlingHigh 到底是个什么鬼?
Linux Kernel调度器的过去,现在和未来
Linux Kernel Development 一书中,关于 Linux 的进程调度器并没有讲解的很全面,只是提到了 CFS 调度器的基本思想和一些实现细节;并没有 Linux 早期的调度器介绍,以及最近这些年新增的在内核源码树外维护的调度器思想。所以在经过一番搜寻后,看到了这篇论文 A complete guide to Linux process scheduling,对 Linux 的调度器历史进行了回顾,并且相对细致地讲解了 CFS 调度器。整体来说,虽然比较啰嗦,但是对于想要知道更多细节的我来说非常适合,所以就有了翻译它的冲动。当然,在学习过程也参考了其它论文。下面开启学习之旅吧,如有任何问题,欢迎指正~
刘盼
2020/04/20
2.7K0
Linux Kernel调度器的过去,现在和未来
Linux内核调度分析(进程调度)
本文是《Linux内核设计与实现》第四章的阅读笔记,代码则是摘自最新的4.6版本linux源码(github),转载请注明出处。
Marky Lumin
2018/01/23
15.3K0
完全公平调度算法
针对没有实时需求的普通进程,Linux内核使用完全公平调度器(Completely Fair Scheduler,CFS)。普通进程的nice值(相对优先级,基准值是120)的取值范围是-20~19,值越小表示优先级越高,不同优先级的进程应该享受不同的待遇,优先级高的进程应该获得更多的处理器时间。为了兼顾进程优先级和公平性,完全公平调度算法引入了虚拟运行时间,如下。
用户7244416
2021/10/26
1.1K0
探索CPU的调度原理
软件工程师们总习惯把OS(Operating System,操作系统)当成是一个非常值得信赖的管家,我们只管把程序托管到OS上运行,却很少深入了解操作系统的运行原理。确实,OS作为一个通用的软件系统,在大多数的场景下都表现得足够的优秀。但仍会有一些特殊的场景,需要我们对OS进行各项调优,才能让业务系统更高效地完成任务。这就要求我们必须深入了解OS的原理,不仅仅只会使唤这个管家,还能懂得如何让管家做得更好。
元闰子
2022/02/27
9610
Linux CFS调度器之负荷权重load_weight--Linux进程的管理与调度(二十五)
负荷权重用struct load_weight数据结构来表示, 保存着进程权重值weight。其定义在/include/linux/sched.h, v=4.6, L1195, 如下所示
233333
2018/12/11
1.7K0
Linux CFS调度器之负荷权重load_weight--Linux进程的管理与调度(二十五)
CFS调度主要代码分析一
在前面学习了CFS调度的原理和主要的数据结构,今天我们就来进入代码分析环节。当然了代码分析只看主要主干不看毛细,同时我们也是根据一个进程是如何被调度的思路来分析一些重要的代码。
DragonKingZhu
2020/03/24
2.5K0
CFS调度主要代码分析一
进程调度:我太难了!
为了实现切换,我们提供一个API,这两个程序执行一会儿就主动调用一下这个API,然后在这个API内部实现任务的切换。
轩辕之风
2022/05/17
3980
进程调度:我太难了!
CFS 调度器数据结构篇
在上一节我们了解了CFS的设计原理,包括CFS的引入,CFS是如何实现公平,CFS工作原理的。本小节我们重点在分析CFS调度器中涉及到的一些常见的数据结构,对这些数据结构做一个简单的概括,梳理各个数据结构之间的关系图出来。
DragonKingZhu
2020/03/24
1.4K0
CFS 调度器数据结构篇
Linux CFS调度器之pick_next_task_fair选择下一个被调度的进程--Linux进程的管理与调度(二十八)
每个调度器类sched_class都必须提供一个pick_next_task函数用以在就绪队列中选择一个最优的进程来等待调度, 而我们的CFS调度器类中, 选择下一个将要运行的进程由pick_next_task_fair函数来完成
233333
2018/12/14
2.2K0
Linux CFS调度器之pick_next_task_fair选择下一个被调度的进程--Linux进程的管理与调度(二十八)
云原生下,TencentOS “如意” CPU QoS之绝对抢占
前言 上篇《减少碳排放上万吨是什么样的高科技?》结合腾讯TencentOS团队在混部方面的落地实战经验,重点介绍混部概念以及TencentOS Server大规模容器集群混部服务器QoS产品“如意”。 “如意”最大实现了动态的资源调度和抢占,空闲时高优先级容器借资源借给低优先级容器使用,需要时再快速抢回来,整个过程在服务器内部完成,容器集群调度层面无需感知和干预,真正做到了提升资源利用率和业务隔离的统一,CPU利用率也从混部前的15%提升到现在45%左右,可使上万台服务器节省电能近2亿度,相当于减少碳排放
腾讯大讲堂
2021/09/14
2.6K1
Linux 进程管理
Linux是一个多用户多任务的操作系统。多用户是指多个用户可以在同一时间使用同一个linux系统;多任务是指在Linux下可以同时执行多个任务,更详细的说,linux采用了分时管理的方法,所有的任务都放在一个队列中,操作系统根据每个任务的优先级为每个任务分配合适的时间片,每个时间片很短,用户根本感觉不到是多个任务在运行,从而使所有的任务共同分享系统资源,因此linux可以在一个任务还未执行完时,暂时挂起此任务,又去执行另一个任务,过一段时间以后再回来处理这个任务,直到这个任务完成,才从任务队列中去除。这就是多任务的概念。 上面说的是单CPU多任务操作系统的情形,在这种环境下,虽然系统可以运行多个任务,但是在某一个时间点,CPU只能执行一个进程,而在多CPU多任务的操作系统下,由于有多个CPU,所以在某个时间点上,可以有多个进程同时运行。 进程的的基本定义是:在自身的虚拟地址空间运行的一个独立的程序,从操作系统的角度来看,所有在系统上运行的东西,都可以称为一个进程。
黄规速
2022/04/14
4.3K0
Linux 进程管理
Linux进程调度器的设计--Linux进程的管理与调度(十七)
调度器面对的情形就是这样, 其任务是在程序之间共享CPU时间, 创造并行执行的错觉, 该任务分为两个不同的部分, 其中一个涉及调度策略, 另外一个涉及上下文切换.
233333
2018/12/04
3.8K0
Linux进程调度器的设计--Linux进程的管理与调度(十七)
Linux 完全公平调度算法
之前我写过一篇分析 O(1)调度算法 的文章:O(1)调度算法,而这篇主要分析 Linux 现在所使用的 完全公平调度算法。
用户7686797
2021/02/24
1.5K0
【操作系统 OS】什么是Linux CFS?完全公平调度器是什么?
Linux 中 CFS 的全称是 Completely Fair Scheduler,完全公平调度器,是 Linux 内核中的一种进程调度算法。
Lokinli
2024/07/31
7560
一文搞懂 | Linux内核 CFS 调度器
Linux内核作为一个通用的操作系统(OS),需要兼顾各种各样类型的进程,包括实时进程、交互式进程、批处理进程等。而调度器(Scheduler)作为OS的核心组件——CPU时间的管理器,主要负责选择某些就绪的进程来执行。不同的调度器根据不同的方法挑选出最适合运行的进程。目前,在Linux内核中支持的调度器有CFS调度器、Realtime调度器、Deadline调度器和Idle调度器 。本篇将简单介绍CFS调度器的设计原理。
混说Linux
2022/11/18
1.3K0
一文搞懂 | Linux内核 CFS 调度器
C|进程调度|公平调度Lottery&CFS
除了上一篇文章提到的MLFQ外,另一种调度名为proportional-share/fair-share,这种调度policy的目标是控制每个进程占用CPU时间的比例。这种policy的一种早期实现名为lottery scheduling,意思是应该运行更久的进程会更有机会获得lottery(彩票中奖,喻CPU使用)。linux内部则使用CFS作为另一种实现。
朝闻君
2021/11/22
5220
C|进程调度|公平调度Lottery&CFS
相关推荐
CFS调度器(1)-基本原理
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档