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. 参考资料

查看文章原文

相关推荐
昵称为空C5 分钟前
SpringBoot数据存储时区选择,符合国际化和特定时区方案
spring boot·后端
ldj20201 小时前
SpringBoot为什么使用new RuntimeException() 来获取调用栈?
java·spring boot·后端
超龄超能程序猿1 小时前
Spring 应用中 Swagger 2.0 迁移 OpenAPI 3.0 详解:配置、注解与实践
java·spring boot·后端·spring·spring cloud
江南一点雨1 小时前
Tokenizer 和 BPE
后端
风象南1 小时前
SpringBoot配置属性热更新的轻量级实现
java·spring boot·后端
洛阳泰山1 小时前
Spring Boot 整合 Nacos 实战教程:服务注册发现与配置中心详解
java·spring boot·后端·nacos
江南一点雨2 小时前
ChatGPT与最大似然估计
后端
程序员爱钓鱼3 小时前
Go语言实战案例-判断一个数是否为质数
后端·google·go
程序员爱钓鱼3 小时前
Go语言实战案例-读取本地文本文件内容
后端·google·go
mCell11 小时前
Go 并发编程基础:从 Goroutine 到 Worker Pool 实践
后端·性能优化·go