Linux 进程调度之CFS调度类

1. 前言

进程调度是一个操作系统的核心功能之一。调度算法的优劣直接影响到了一个操作系统的使用体验。Linux 作为目前业界使用最为广泛的开源操作系统,其内部的进程调度器在社区的trade off下优化了一轮又一轮。目前最为成熟的调度器是CFS(Completely Fair Scheduler,完全公平调度器),CFS的设计目标是实现对 CPU 资源的公平分配,使得每一个进程都能获得相对相等的 CPU 运行时间。

CFS的特点包括:公平性、无饥饿、负载均衡以及动态优先级。

2. 虚拟运行时间

为了实现对 CPU 资源的公平分配,以使得每个进程都获得相对相等的 CPU 运行时间。CFS调度器抽象出来一个概念vritual clock (虚拟时钟)以及vruntime(虚拟运行时间)。

所谓虚拟其实是与真实相对的,我们都知道 CPU 中都存在一个时钟(Clock)部件,它以固定的频率产生脉冲,驱动CPU内部的各个部件按照时序执行操作。这个 CPU 中的时钟就是与虚拟时钟相对的真实时钟。一个进程在 CPU 上运行的时间,可以由 CPU 的时钟来计量。这个计量得到的时间就是进程的真实运行时间。

时钟可以是晶振构成、也可以是振荡器、亦或者时钟发生器等。

vruntime(虚拟运行时间)其实是在真实运行时间的基础上加入进程优先级的因素。在虚拟时钟中,进程的实际运行时间公式如下:

<math xmlns="http://www.w3.org/1998/Math/MathML"> 实际运行时间 = 调度周期 ∗ 进程权重 所有进程权重之和 实际运行时间 = 调度周期 * \frac{进程权重}{所有进程权重之和} </math>实际运行时间=调度周期∗所有进程权重之和进程权重

CFS调度器中,调度器认为相同的调度周期内所有进程的虚拟运行时间应该是要相同的。

所有的进程都会在一颗红黑树中按照虚拟运行时间的大小,从小到大进行排序。每个调度时机,CFS调度器都选择红黑树中虚拟运行时间最小的进程调度上 CPU 进行运行。以确保在相同的调度周期,红黑树中所有的进程都趋近相同的值。

3. 定时更新统计信息

与其他调度器更新运行时间直接加一不同。由于CFS调度器涉及虚拟运行时间,所以这部分代码比较复杂。更新虚拟运行时间的主要逻辑位于调度类的update_curr函数中,该函数会由 CPU 的时间中断定时触发,收集并计算当前的虚拟运行时间。

scss 复制代码
static void update_curr(struct cfs_rq *cfs_rq)
{
        .....
        // 更新虚拟运行时间
	curr->vruntime += calc_delta_fair(delta_exec, curr);
	update_deadline(cfs_rq, curr);
	update_min_vruntime(cfs_rq);
}

// 计算占用 CPU 的时间间隔
static inline u64 calc_delta_fair(u64 delta, struct sched_entity *se)
{
	if (unlikely(se->load.weight != NICE_0_LOAD))
		delta = __calc_delta(delta, NICE_0_LOAD, &se->load);

	return delta;
}

// 执行真正的计算
static u64 __calc_delta(u64 delta_exec, unsigned long weight, struct load_weight *lw)
{
	u64 fact = scale_load_down(weight);
	u32 fact_hi = (u32)(fact >> 32);
	int shift = WMULT_SHIFT;
	int fs;

	__update_inv_weight(lw);

	if (unlikely(fact_hi)) {
		fs = fls(fact_hi);
		shift -= fs;
		fact >>= fs;
	}

	fact = mul_u32_u32(fact, lw->inv_weight);

	fact_hi = (u32)(fact >> 32);
	if (fact_hi) {
		fs = fls(fact_hi);
		shift -= fs;
		fact >>= fs;
	}

	return mul_u64_u32_shr(delta_exec, fact, shift);
}

上面的代码可以总结为下面的公式:

<math xmlns="http://www.w3.org/1998/Math/MathML"> 进程的虚拟运行时间 = 进程的实际运行时间 ∗ N I C E _ 0 _ L A D 进程的优先级 进程的虚拟运行时间 = 进程的实际运行时间 * \frac{NICE\_0\_LAD}{进程的优先级} </math>进程的虚拟运行时间=进程的实际运行时间∗进程的优先级NICE_0_LAD

4. 结论

可以这样认为,在CFS调度器中,进程优先级越高的进程,将获得更多的机会上 CPU 运行。也即是说,同个调度周期,进程优先级越高的进程,实际运行时间越多。

5. 参考资料

查看文章原文

相关推荐
虽千万人 吾往矣20 分钟前
golang gorm
开发语言·数据库·后端·tcp/ip·golang
这孩子叫逆1 小时前
Spring Boot项目的创建与使用
java·spring boot·后端
coderWangbuer2 小时前
基于springboot的高校招生系统(含源码+sql+视频导入教程+文档+PPT)
spring boot·后端·sql
攸攸太上2 小时前
JMeter学习
java·后端·学习·jmeter·微服务
Kenny.志2 小时前
2、Spring Boot 3.x 集成 Feign
java·spring boot·后端
sky丶Mamba3 小时前
Spring Boot中获取application.yml中属性的几种方式
java·spring boot·后端
千里码aicood4 小时前
【2025】springboot教学评价管理系统(源码+文档+调试+答疑)
java·spring boot·后端·教学管理系统
程序员-珍4 小时前
使用openapi生成前端请求文件报错 ‘Token “Integer“ does not exist.‘
java·前端·spring boot·后端·restful·个人开发
liuxin334455664 小时前
教育技术革新:SpringBoot在线教育系统开发
数据库·spring boot·后端
数字扫地僧5 小时前
HBase与Hive、Spark的集成应用案例
后端