关注了就能看到更多这么棒的文章哦~

An EEVDF CPU scheduler for Linux

By Jonathan Corbet
March 9, 2023
DeepL assisted translation
https://lwn.net/Articles/925371/

内核的完全公平调度器(CFS, completely fair scheduler)管理着大多数 Linux 系统上运行的大多数进程的 CPU 时间分配决策。CFS 在 2007 年的 2.6.23 版本中被合并,并且经过无数次的不断调整,至今已经能很好处理了这个工作了。不过,CFS 并不完美,有些情况它还是处理得有些问题。由 Peter Zijlstra 发布的 EEVDF 调度器就提供了一种新的可能性,同时减少了它对那些脆弱的启发式方法的依赖。

CFS and scheduling constraints

CFS 的关键设计目标之一可以从它的名字中,也就是公平性,要确保系统中的每个进程都能公平地得到它的应有的 CPU 时间。这个目标是通过跟踪每个进程获得多少时间,然后找出那些获得的 CPU 时间比其他进程要少的进程来执行从而实现的。其中每个进程的运行时间都是要用 "nice" 优先级值来做一下调整从而计算得出的。换句话说,CFS 是一个加权的公平排队调度器(weighted fair queuing scheduler)。

事实证明,fairness 足以解决许多 CPU 调度问题。然而,除了公平分配 CPU 时间之外,调度程序还要考虑许多其他约束条件。例如,它应该要能让系统内存 cache 最大化利用起来,这需要尽量减少进程在 CPU 之间的移动。但与此同时,如果有工作要做的话,它又应该让所有的 CPU 都忙起来。电源管理也是一个复杂的问题;有时候不可以采用最有利于系统吞吐量的最佳决策,因为需要保护电池寿命。。那些 hybrid 系统(所有的 CPU 不全都是一样的)又增加了更多的复杂性,等等等等。

有一个需要改进的地方就是对于那些有明确 latency 要求的工作的处理。一些进程可能不需要大量的 CPU 时间,但当它们确实需要 CPU 时,要求能尽快拿到。其他进程可能需要更多的 CPU 时间,但如果有必要的话,可以等待一会儿再拿到 CPU。CFS 并没有给进程提供什么方法可以表达他们的 latency 要求;nice 值(优先级)可以用来给进程更多的 CPU 时间,但这不是一回事。实时调度类(realtime scheduling class)可以用在对延迟要求很高的工作上,但如果希望采用 realtime class 来调度运行,这需要有特权才可以实现,而且实时进程会严重影响系统其他部分的运行。

现在缺少的是一种方法,可以确保一些进程能够快速访问 CPU,而不一定让这些进程会获得超过其公平份额的 CPU 时间。作为解决这个问题的一个尝试,已经有一组 latency nice patch 在讨论了一段时间。它们可以让严格的 latency 要求的 CFS 进程能在想要运行时跳过队列直接获得 CPU。这些 patch 确实是有效的,但 Zijlstra 认为可能有一个更好的方法来解决这个问题。

Introducing EEVDF

EEVDF 全称 “Earliest Eligible Virtual Deadline First” 调度算法,它并不是什么新事物,是在 1995 年由 Ion Stoica 和
Hussein Abdel-Wahab 在 1995 年的论文中描述过。它的名字就暗示,它是跟内核的 deadline scheduler 所使用的 Earliest Deadline First algorithm 很类似。但是这里的差异是,EEVDF 不是一个 realtime 时调度程序,所以工作方式不一样。理解 EEVDF 需要掌握几个(相对)简单的概念。

EEVDF 跟 CFS 一样,试图把可用的 CPU 时间公平地分配给正在争夺它的那些进程。例如,如果有五个进程试图在一个 CPU 上运行,那么每个进程应该得到 20%的可用时间。每个进程的 nice 值可以用来调整其公平时间的计算结果,nice 值较低(因此优先级较高)的进程有权获得更多的 CPU 时间,而牺牲那些具有较高 nice 值的进程。这些内容都是以前就有的概念。

我们来考虑一下一秒钟时长的一个时间段;在这段时间内,我们的五个进程中,每个进程应该得到 200ms 的 CPU 时间。由于一些原因,没能按照安排来做到,其中一些进程得到了过多的时间,而另一些进程则被缩短了。EEVDF 会对每个进程计算出该进程应该得到的时间和它实际得到的时间之间的差异。这个差异被称为 "lag"。lag 值为正的进程就没有得到公平的份额,应该比 lag 值为负的过程要更早调度。

事实上,当且仅当计算出的 lag 值大于或等于零时,才认为这个进程是 "合格的(eligible)";任何具有负值的 lag 值的进程都都没有资格运行。对于任何不合格的过程,在未来的某个时间,它有权得到的时间逐渐增长,赶上它实际得到的时间,于是可以再次成为合格的进程;这个时间就被称为 "合格时间(eligible time)"。

因此,lag 值的计算是 EEVDF 调度器中的一个关键部分,大部分的 patch set 都是为了能正确得到这个值。即使使没有完整的 EEVDF 算法,也可以利用一个进程的 lag 值来将其公平地放在运行队列中;lag 值较高的进程应该先运行,从而以使整个系统的 lag 值比较均匀。

另一个起作用的因素是 "虚拟截止日期(virtual deadline)",它是一个进程应该得到其应有的 CPU 时间的最早时间。这个期限的计算方法是将一个进程分配的时间片与它的合格时间值相加。一个拥有 10ms 时间片的进程,如果其合格时间(eligible time)是未来 20ms,那么它的 virtual deadline 就是未来 30ms

EEVDF 的核心理念就可以从它的名字中看出,它将首先运行那些具有最早的 virtual deadline 的进程。因此,调度选择是结合了 fairness(用于计算合格时间的 lag 值)以及每个进程当前有用的时间值来共同决定的。

Addressing the latency problem

有了这个框架之后,对 latency 敏感的进程的优先调度就自然而然地实现好了。当调度器在计算每个进程的时间片时,它会考虑到该进程分配的 latency-nice 值;一个具有较低的 latency-nice 值的进程(也就意味着更严格的 latency 要求)的进程将得到一个更短的时间片。对 latency 相对漠不关心的进程将得到较长的时间片。请注意,给任何两个进程的 CPU 时间(具有相同的 nice 值)将是相同的,但低延迟的进程将获得更多更短小的时间片段。

前面说过,virtual deadline 是通过将时间片加上 eligible time 而计算出来的。这将导致具有较短时间片的进程可以具有更近的 virtual deadline,因此也会被首先执行。对延迟敏感的进程,通常不需要大量的 CPU 时间,而是希望能快速响应事件;而没有延迟要求的进程将被给予更长的运行时间
片,这可以帮助提高吞吐量。不再需要那些启发式的复杂规则来解决这个问题。

不过,要想让一篇学术论文的内容能在 Linux 内核中成为一个表现良好的实现,这还有很多工作要做。Zijlstra 才开始对他的 EEVDF 调度器进行 benchmark 测试;他的初步结论是:"有一些变好了,一些变坏了,但没有什么迹象表明这个方向是完全错误的"。一些结果 "似乎表明 EEVDF 的调度比 CFS 更稳定,并且有很多延迟方面的优势"。

虽然这显然是一个很好的起点,但 Zijlstra 承认仍有相当多的工作要做。但是,他说,"如果我们能做到这一点,我们就可以删除一大堆让人不舒服的启发式代码",用一个定义得更好的策略来取代它。这不是一个小的改动,他补充说:"它完全重塑了最基本的 scheduler、placement、preemption、picking 等等所有的机制。它们唯一的共同点是,它们都是基于 virtual time 的调度器。"

不用说,这样一个根本性的改动不太可能被快速合入内核。幸运的是,目前的 patch 将 EEVDF 作为一个选项,可以跟 CFS 并列。这样就可以进行更广泛的测试,而不需要实际取代当前的调度器。CPU 调度器必须为几乎任何人们可以想象得到的场景都做出正确的选择,包括内核可以支持的所有各种系统,这就表明,哪怕只对它做一些小改动,人们也有可能看到有一些场景变得更差了,更何况这样一个打改动。因此,在考虑用 EEVDF 取代 CFS 之前,必须先进行大量的测试。至于合适才能见到这件事情发生,我们并没有什么已知的 deadline,无论是不是 virtual 的。

全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。

欢迎分享、转载及基于现有协议再创作~

长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~

format,png

Logo

更多推荐