Linux CFS调度器:原理和实现
目录
- CFS调度器的重点
- 基本概念
- 一些问题
- 源码和补充
- 调度类和调度策略
- 优先级分类
- 多处理器系统 rq的平衡
概念部分只讲解
CFS
调度器
CFS调度器的重点
基本概念
Linux 2.6
版本在我看来是一个比较完美的版本,而2.4
和2.6
最大的差异就在于CFS
调度器的引入
由于优先级的引入,CFS
对于cpu
资源的分配(即实际运行时间runtime
)是根据权重来进行分配的,权重weight
和优先级nice
的映射如下,优先级和权重呈反比
static const int 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,
};
实际运行时间runtime
的计算公式则为
r u n t i m e = s c h e d u l e p e r i o d × w e i g h t i / ∑ i = 1 n w e i g h t i ( i = 1 , 2... n ) runtime = scheduleperiod \times weight_i / \sum_{i=1}^{n} weight_i(i=1,2...n) runtime=scheduleperiod×weighti/i=1∑nweighti(i=1,2...n)
此处设置了一个虚拟运行时间vruntime
v r u n t i m e = r u n t i m e × ( 1024 / w e i g h t i ) vruntime = runtime\times(1024/weight_i) vruntime=runtime×(1024/weighti)
化简可得每一个调度实体的vruntime
不由自身的权重改变而改变,因此从宏观上来看每个调度周期时每一个调度实体的vruntime
应当是一样的,这是理想状态下的
v r u n t i m e = s c h e d u l e p e r i o d × ( 1024 / ∑ i = 1 n w e i g h t i ( i = 1 , 2... n ) ) vruntime = scheduleperiod \times (1024/\sum_{i=1}^{n} weight_i(i=1,2...n)) vruntime=scheduleperiod×(1024/i=1∑nweighti(i=1,2...n))
而某一调度实体由于某些原因导致进入阻塞或睡眠态,此时便会主动将时间片让出去,导致其vruntime
暂时不变,而其他调度实体获得了该时间片开始运行,导致其vruntime
增加,此时便会形成不对等,这是不公平的,因此需要在下一进程切换时调度vruntime
最小的进程
为什么让优先级高的和优先级低的分配不同的
runtime
却说完全公平呢?
因为该公平是在vruntime
的逻辑上,而不是runtime
的逻辑上,cfs
保证了每一个调度实体在vruntime
的相等,如果有较小的vruntime
就优先调度它
优先级较高的runtime
较大,优先级较低的runtime
较小,但其vruntime
是一样的,因此在这种情况下优先级较低的实际上是时钟有了更高的衰减率
一些问题
新进程的vruntime
的初始值是不是0
?
- 子进程在创建时,
vruntime
初值首先被设置为min_vruntime
- 如果
sched_features
中设置了START_DEBIT
位,vruntime
会在min_vruntime
的基础上再增大一些 - 设置完子进程的
vruntime
之后,检查sched_child_runs_first
参数,如果为1的话,就比较父进程和子进程的vruntime
,若是父进程的vruntime
更小,就对换父、子进程的vruntime
,这样就保证了子进程会在父进程之前运行
休眠进程的vruntime
的值一直保持不变吗?
在休眠进程被唤醒时重新设置vruntime
值,以min_vruntime
值为基础,给予一定的补偿,但不能补偿太多
进程占用的时间片可以无穷小吗?
CFS
设定了进程占用CPU的最小时间值, sched_min_granularity_ns
,正在CPU上运行的进程如果不足这个时间是不可以被调离CPU
的
liuzixuan@10-60-73-159:~$ cat /proc/sys/kernel/sched_min_granularity_ns
1500000
进程从一个CPU
迁移至另外一个CPU
的时候vruntime
会变化吗?
当进程从一个CPU的运行队列中出来时,它的vruntime
要减去队列的min_vruntime
值; 而当进程加入另一个CPU
的运行队列时,它的vruntime
要加上该队列的min_vruntime
值。 这样,进程从一个CPU
迁移到另一个CPU
之后,vruntime
保持相对公平
vruntime
无限累加,产生溢出怎么办?
红黑树中的key
不是vruntime
而是vruntime-min_vruntime
,min_vruntime
是红黑树中最小的key
,减去一个最小的vruntime
将所有进程的key
围绕在最小vruntime
的周围,换句话来说就是,只比较相对大小
static inline int less(u32 left, u32 right)
{return (less_eq(left, right) && (mod(right) != mod(left)));
}
源码和补充
调度类和调度策略
Linux
调度类:按所需分配的计算能力, 向系统中每个进程提供最大的公正性
fair_sched_class
:CFS
完全公平调度器idle_sched_class
:每个处理器有一个空闲线程,即0
号线程rt_sched_class
:为每个调度优先级维护一个队列
struct sched_class {const struct sched_class *next;void (*enqueue_task) (struct rq *rq, struct task_struct *p, int wakeup);void (*dequeue_task) (struct rq *rq, struct task_struct *p, int sleep);void (*yield_task) (struct rq *rq);void (*check_preempt_curr) (struct rq *rq, struct task_struct *p, int sync);struct task_struct * (*pick_next_task) (struct rq *rq);void (*put_prev_task) (struct rq *rq, struct task_struct *p);#ifdef CONFIG_SMPint (*select_task_rq)(struct task_struct *p, int sync);unsigned long (*load_balance) (struct rq *this_rq, int this_cpu,struct rq *busiest, unsigned long max_load_move,struct sched_domain *sd, enum cpu_idle_type idle,int *all_pinned, int *this_best_prio);int (*move_one_task) (struct rq *this_rq, int this_cpu,struct rq *busiest, struct sched_domain *sd,enum cpu_idle_type idle);void (*pre_schedule) (struct rq *this_rq, struct task_struct *task);int (*needs_post_schedule) (struct rq *this_rq);void (*post_schedule) (struct rq *this_rq);void (*task_wake_up) (struct rq *this_rq, struct task_struct *task);void (*set_cpus_allowed)(struct task_struct *p,const struct cpumask *newmask);void (*rq_online)(struct rq *rq);void (*rq_offline)(struct rq *rq);
#endifvoid (*set_curr_task) (struct rq *rq);void (*task_tick) (struct rq *rq, struct task_struct *p, int queued);void (*task_new) (struct rq *rq, struct task_struct *p);void (*switched_from) (struct rq *this_rq, struct task_struct *task,int running);void (*switched_to) (struct rq *this_rq, struct task_struct *task,int running);void (*prio_changed) (struct rq *this_rq, struct task_struct *task,int oldprio, int running);#ifdef CONFIG_FAIR_GROUP_SCHEDvoid (*moved_group) (struct task_struct *p);
#endif
};
static const struct sched_class fair_sched_class; // 公开调度类
static const struct sched_class idle_sched_class; // 空闲调度类
static const struct sched_class rt_sched_class; // 实时调度类
Linux
调度策略:决定什么时候以怎么样的方式选择一个新进程占用CPU
运行
SCHED_NORMAL
:普通进程调度策略,使调度实体通过cfs
调度器运行SCHED_FIFO
:实时进程调度策略,先进先出调度算法SCHED_RR
:实时进程调度策略,时间片轮转算法SCHED_BATCH
:普通进程调度策略,批量处理,使调度实体通过cfs
调度器运行SCHED_IDLE
:普通进程调度策略,使调度实体以最低优先级通过cfs
调度器运行
#define SCHED_NORMAL 0
#define SCHED_FIFO 1
#define SCHED_RR 2
#define SCHED_BATCH 3
#define SCHED_IDLE 5
可以通过
sched_getscheduler()
系统调用获得某一进程调度策略
优先级分类
关于优先级问题,优先级一般分为静态优先级和动态优先级
- 静态优先级:用
100
到139
表示普通进程的静态优先级,用来估价系统中这个进程和其他普通进之间调度的程度,本质上决定了进程的基本时间片 - 动态优先级:用
100
到139
表示普通进程的动态优先级,其是调度程序在选择新进程来运行的时候使用的数
多处理器系统 rq的平衡
一个原则:任何一个可运行进程都不可能同时出现在两个或多个运行队列中
调度域:是一个cpu
集合,它的工作量应当由内核保持平衡,其组成类似于基数树,每个调度域被依次划分为一个或多个组,每个组待办调度域的一个cpu
子集,工作量的平衡总是在调度域的组之间来完成
系统中所有物理cpu
的sched_domain
描述符都放在每cpu
变量phys_domains
中
static DEFINE_PER_CPU(struct static_sched_domain, phys_domains);
它们的初始化在各个机器目录中
/* sched_domains SD_NODE_INIT for SGI IP27 machines */
#define SD_NODE_INIT (struct sched_domain) { \.parent = NULL, \.child = NULL, \.groups = NULL, \.min_interval = 8, \.max_interval = 32, \.busy_factor = 32, \.imbalance_pct = 125, \.cache_nice_tries = 1, \.flags = SD_LOAD_BALANCE \| SD_BALANCE_EXEC \| SD_WAKE_BALANCE, \.last_balance = jiffies, \.balance_interval = 1, \.nr_balance_failed = 0, \
}
Linux CFS调度器:原理和实现相关推荐
- 用c语言实现对n个进程采用“短进程优先”算法的进程调度_为什么Linux CFS调度器没有带来惊艳的碾压效果?...
文章转自公众号"人人都是极客" 但凡懂Linux内核的,都知道Linux内核的CFS进程调度算法,无论是从2.6.23将其初引入时的论文,还是各类源码分析,文章,以及Linux内核 ...
- 为什么Linux CFS调度器没有带来惊艳的碾压效果? | CSDN博文精选
任何领域,革命性的碾压式推陈出新并不是没有,但是概率极低,人们普遍的狂妄在于,总是认为自己所置身的环境正在发生着某种碾压式的变革,但其实,最终大概率不过是一场平庸. 作者 | dog250 责编 | ...
- Linux进程调度-CFS调度器原理分析及实现,懂了
1. 概述 (1) Completely Fair Scheduler,完全公平调度器,用于Linux系统中普通进程的调度. (2) CFS采用了红黑树算法来管理所有的调度实体 sched_entit ...
- linux cfs调度器_模型实现
调度器真实模型的主要成员变量及与抽象模型的对应关系 I.cfs_rq结构体 a) struct sched_entity *curr 指向当前正在执行的可调度实体.调度器的调度 ...
- Linux CFS调度器分析
进程被调度的条件是什么,以及真正发生调度的时刻又是在哪里? 以下结论和代码分析都是基于最新Linux master分支(Linux5.0) 1. 调度的时刻 1.1 当前进程主动放弃CPU或者调用ms ...
- cpu调度的最小单位_Linux CFS调度器
一直没有写过关于Linux内核调度器的内容,这几天被问起,简单的讲了讲,收到一堆challenge,这次决定做一个通篇总结方便自己整理思路. 要说Linux2.4和2.6最大的差异就在于CFS调度器的 ...
- (5)Linux进程调度-CFS调度器
目录 背景 1. 概述 2. 数据结构 2.1 调度类 2.2 rq/cfs_rq/task_struct/task_group/sched_entity 3. 流程分析 3.1 runtime与vr ...
- Linux进程调度 - CFS调度器 LoyenWang
背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...
- 【Linux 内核】CFS 调度器 ⑥ ( CFS 调度器就绪队列 cfs_rq | Linux 内核调度实体 sched_entity | “ 红黑树 “ 数据结构 rb_root_cached )
文章目录 一.CFS 调度器就绪队列 cfs_rq 二.Linux 内核调度实体 sched_entity 三." 红黑树 " 数据结构 rb_root_cached 一.CFS ...
最新文章
- expect--自动批量分发公钥脚本
- TFS命令tf:undo - 强制签入别人签出的文件
- MySQL数据类型以及元数据的使用
- oracle写入导出文件时出错,帮忙!EXP-00015:错误!EXP-00002: 写入导出文件时出错
- hibernate中的id特殊属性hilo剖解(多用于继承关系)
- java学习(110):日期date类
- 什么叫pmt测试分析_RVS — 面向目标硬件的软件性能测试工具
- 小程序开发教程 | 来自小程序开发者的实例教程
- OpenCV编程:最大熵阈值分割算法实现(代码可运行)
- 如何设计更好的脉搏血氧仪:实施
- python生成倒计时图片_用Python自动化生成新年倒计时图片
- Pearl Pairing
- 腾讯云TCP架构高级工程师认证考试大纲、考题下载及说明
- 百度智能云落子贵阳,工业互联网进入新赛段
- 【f1c200s/f1c100s】使用genimage工具制作img系统镜像
- 图书管理系统模块,通过模糊查询实现查找图书的功能
- FFA 议程上线!实时化浪潮下,Apache Flink 还将在大数据领域掀起怎样的变革?...
- 你非要躺平,我也没办法啊,逼着学吗
- 渗透测试中信息收集的那些事
- 冯巩的155句经典语,太有才了