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

查看文章原文

相关推荐
ajsbxi几秒前
苍穹外卖学习记录
java·笔记·后端·学习·nginx·spring·servlet
颜淡慕潇1 小时前
【K8S问题系列 |1 】Kubernetes 中 NodePort 类型的 Service 无法访问【已解决】
后端·云原生·容器·kubernetes·问题解决
尘浮生2 小时前
Java项目实战II基于Spring Boot的光影视频平台(开发文档+数据库+源码)
java·开发语言·数据库·spring boot·后端·maven·intellij-idea
尚学教辅学习资料2 小时前
基于SpringBoot的医药管理系统+LW示例参考
java·spring boot·后端·java毕业设计·医药管理
monkey_meng3 小时前
【Rust中的迭代器】
开发语言·后端·rust
余衫马3 小时前
Rust-Trait 特征编程
开发语言·后端·rust
monkey_meng3 小时前
【Rust中多线程同步机制】
开发语言·redis·后端·rust
paopaokaka_luck7 小时前
【360】基于springboot的志愿服务管理系统
java·spring boot·后端·spring·毕业设计
码农小旋风9 小时前
详解K8S--声明式API
后端
Peter_chq9 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端