Linux-进程的管理与调度30(基于6.1内核)---CFS总结
CFS(完全公平调度器) 是 Linux 内核中的一个主要调度器,首次引入于 2.6.23 内核版本,替代了以前的 O(1) 调度器。它设计的目标是提供一种公平的 CPU 时间分配机制,尤其是在多核系统中,确保每个进程在系统中得到一个合理且公平的执行时间。CFS 采用了基于时间片的调度,但与传统的时间片轮转调度方式不同,它采用了“虚拟时间”的概念,目标是让系统中的每个进程都能按需、平等地占用 CPU 资源。
1. CFS调度器设计目标
CFS 调度器的设计目标是 公平性、响应性 和 可伸缩性。具体来说,它主要关注以下几个方面:
-
公平性:每个进程应该获得相对公平的 CPU 时间。公平性并不是指每个进程都获得完全相等的时间,而是考虑到每个进程的优先级和运行时间,确保其在系统中的运行不会受到不公平的压制。
-
响应性:CFS 必须能够在面对高负载时,迅速响应系统状态变化,确保交互式任务(如用户界面的响应)能够获得及时调度。
-
可伸缩性:CFS 需要在大规模多核系统上表现出良好的可伸缩性,能够高效地管理大量进程,并减少锁的竞争。
2. CFS调度的核心概念
CFS 调度器的工作机制和许多传统的调度算法有所不同。它不使用固定时间片(time slice)来限制每个进程的执行时间,而是基于 虚拟运行时间(virtual runtime,vruntime) 来决定进程的调度顺序。
2.1 虚拟运行时间(vruntime)
-
vruntime 是 CFS 调度器用来衡量一个进程已经消耗的 CPU 时间的度量。每个进程的虚拟运行时间是动态计算的,基于该进程的优先级(或称为
weight
)来调整。虚拟运行时间较小的进程会被优先调度。 -
在 CFS 中,虚拟运行时间是不断增加的,它是一个相对值,表示一个进程从调度开始到当前时间所消耗的 CPU 时间。一个进程的虚拟运行时间越小,意味着它已经使用的 CPU 时间相对较少,它会优先于其他进程被调度。
2.2 公平性:负载均衡与权重
CFS 的一个重要设计思想是 公平性。它的调度目标是使得系统中的所有进程能够“公平”地占用 CPU 时间。为此,CFS 会根据进程的优先级(权重)调整进程的虚拟时间,权重较大的进程会获得更多的 CPU 时间。
-
每个进程有一个权重(
weight
),通常与其优先级相关。权重越大的进程,虚拟运行时间的增加就越慢,这意味着它获得的 CPU 时间会更多。例如,实时进程(如调度类 SCHED_FIFO 和 SCHED_RR)会有较高的权重,普通进程(SCHED_OTHER)则有较低的权重。 -
调度器的目标是根据进程的虚拟运行时间来维持一个尽可能公平的状态。虚拟运行时间小的进程(或者说等待时间长的进程)会被优先调度,从而达到负载均衡的效果。
2.3 红黑树(Red-Black Tree)
CFS 使用了 红黑树(一种自平衡的二叉查找树)来维护进程的调度队列。每个进程都按照其虚拟运行时间(vruntime)在红黑树中排序。每次调度时,CFS 都从树中选择 vruntime 最小的进程进行执行。
-
红黑树提供了高效的查找、插入和删除操作。通过这种数据结构,调度器可以快速找到当前 vruntime 最小的进程,并且确保系统中进程调度的公平性。
-
红黑树的使用避免了传统的基于队列的调度方法(如轮转调度),从而提高了高负载情况下的调度效率。
3. 调度流程
CFS 调度器的调度流程可以简要描述为以下几个步骤:
-
插入到红黑树中:每当一个进程被唤醒或刚启动时,它会根据自己的虚拟运行时间(vruntime)插入到调度队列(红黑树)中。
-
选择下一个执行的进程:每当一个进程执行完毕,CFS 会选择虚拟运行时间最小的进程(即等待时间最长的进程)进行调度。这个过程是基于红黑树的查找操作,效率很高。
-
更新虚拟运行时间:每次进程运行时,它的虚拟运行时间会根据进程的权重增加。虚拟运行时间较小的进程会优先调度,因此系统会动态调整各个进程的执行顺序。
-
负载均衡:CFS 会根据系统的负载状态,动态调整不同 CPU 核心上的进程分布,以实现系统的负载均衡。负载均衡的策略会根据每个 CPU 核心上的进程数量和进程的虚拟运行时间来调整。
4. 调度类和优先级
CFS 调度器支持多种不同的调度类,其中最重要的是以下几种:
-
SCHED_OTHER(默认调度类):这是最常用的调度类,适用于大多数普通进程。调度算法基于公平性,通过虚拟运行时间来管理进程。
-
SCHED_FIFO / SCHED_RR(实时调度类):实时任务具有比普通任务更高的优先级,不会受到虚拟运行时间的影响。它们使用不同的调度策略(如 FIFO 队列或轮转调度)。
-
SCHED_IDLE(空闲调度类):用于表示空闲的进程,通常具有最低优先级。
5. CFS的优化与扩展
CFS 在其发展过程中做了一些重要的优化和扩展,以确保其在不同类型的负载下都能保持高效性和公平性:
-
负载均衡:为了确保各 CPU 核心负载均衡,CFS 调度器引入了负载均衡机制,特别是在多核处理器中。CFS 会定期检查每个 CPU 核心上的负载情况,并通过迁移任务来实现负载均衡。
-
时钟滴答优化:为了减少时钟中断对调度性能的影响,CFS 在高负载的情况下优化了时钟滴答(tickless kernel),减少了对内核调度器的中断干扰。
-
NUMA优化:对于支持 NUMA 架构的系统,CFS 还会考虑内存访问的非均匀性,避免进程频繁迁移到远离其内存区域的 CPU 上运行,以减少延迟。
-
任务亲和性(CPU affinity):CFS 通过调度策略,尽量让任务运行在之前执行过该任务的 CPU 上,提升缓存的局部性,减少缓存失效和性能损失。
6. CFS的局限性与挑战
尽管 CFS 在公平性、响应性和可伸缩性方面做出了很多改进,但它依然存在一些挑战和局限性:
-
实时任务调度:虽然 CFS 对交互式任务有很好的响应性,但对于高精度的实时任务,它可能不如传统的实时调度器(如 SCHED_FIFO 和 SCHED_RR)那样可靠。
-
多核和大规模系统的性能:CFS 在大规模多核系统中依然存在一定的开销,特别是当负载不均衡时,CFS 的负载均衡机制可能需要消耗较多的 CPU 资源。
-
延迟问题:CFS 主要关注公平性,但在某些场景下,可能导致某些任务的调度延迟较高,特别是在进程数目非常多时,CFS 需要遍历红黑树来选择下一个执行的进程,这可能会导致延迟增加。
7. CFS的优化与扩展
-
Tickless 模式:CFS 引入了 tickless 内核(无时钟滴答机制),以减少高频率的时钟中断对系统性能的影响,特别是在负载较低时,避免了频繁的中断。
-
NUMA优化:对于 NUMA(非一致性内存访问)架构的系统,CFS 可以优化进程调度,减少进程在不同内存节点之间的迁移,提升内存访问性能。
-
调度负载均衡:CFS 会定期检查 CPU 核心上的进程负载,避免单个 CPU 核心过载。负载均衡的实现通过进程迁移来实现,确保每个核心的负载大致相同。
8. 总结
CFS 调度器是 Linux 内核中非常重要的一部分,它通过虚拟运行时间和红黑树等技术实现了高效的进程调度。它不仅关注进程的公平性,还能在多核和高负载系统中保持较好的性能表现。虽然在实时性和调度延迟方面存在一些局限性,但它的公平性和可伸缩性使得它成为适用于大多数普通应用场景的调度器。