如果完全公平调度程序始终以最低 vruntime 执行进程,它如何防止饥饿?

如果完全公平调度程序始终以最低 vruntime 执行进程,它如何防止饥饿?

我已经对完全公平调度程序进行了相当多的解释,但它们似乎都缺乏一个重要的细节。所有内容的解释方式本质上都归结为运行“队列”(实际上是保持高效排序的红黑树)的摘要,以及有关每个进程如何更新其 vruntime 值的详细信息。我将跳过主要细节的摘要,并专注于对我的问题重要的内容。

每次调度进程时,其 vruntime 都会更新以反映它在 CPU 上获得的时间。大多数这些描述都指出了该值单调递增(尽管“严格递增”会更准确)这一事实。每次操作系统决定调度一个新进程时,它都会查看 vruntime 最低的进程并将其发送出去执行。

这些描述中缺少一个重要细节。假设我有一个浏览器进程在运行、运行、运行......几天后,该进程的 vruntime 将变得巨大,反映了许多小时的 CPU 时间。在同一台机器上(为了便于讨论,它是单 CPU 硬件)是一个新的 CPU 绑定进程。调度程序查看这两个进程,发现 cpu hog 的 vruntime 为零并对其进行调度。

我见过的每个 CFS 描述都说这个进程将一直运行,直到另一个进程的 vruntime 更少,但这不可能是真的。如果是这种情况,浏览器进程将陷入饥饿状态,直到数小时或数天后 CPU 占用率赶上它。

其中肯定有一个捏造的因素,或者一定有比直接比较虚拟运行时更多的东西,但在所有这些描述中都跳过了这个重要的细节。我缺少什么?

(而且,有没有人注意到大型多 CPU 机器可能会溢出进程组的 vruntime?它必须获得大约 544 年的 CPU 时间,但 64 处理器机器可以工作 10 年...... ..没关系=])

答案1

因此,典型的 CFS 描述中遗漏了两三点。调度程序确实会选择并运行具有最低 vruntime 的进程。然而,vruntime实际上并不代表进程在CPU上花费了多少时间,也不代表当前运行队列中花费的时间。这是一个启发式计算的值,通过一些巧妙的数学巧妙地构建了所需的含义。

每个CPU都有自己的进程队列(实际上是一棵RB树,但它的行为就像一个队列)。每个进程队列都会跟踪它的“最左边”进程,即虚拟运行时间最小的进程。不要将队列中的任何虚拟运行时间视为跟踪总运行时间。相反,请考虑进程的虚拟运行时间和队列中最小运行时间之间的差异。这种差异具有重要意义。这是相关进程和最左边进程之间运行时间的差异。换句话说,就是该进程比最左边的进程多获得了多少 CPU 时间。

因此,当一个全新的进程进入CPU的队列时,它没有vruntime。它只是继承与最左边进程相同的值。由于它们的 vruntime 现在相同,因此它们的差异为零,这是有道理的,因为新进程不再预期的CPU 时间比最左边的进程长。有可能,新进程将在下一次上下文切换时调度。

进程出于多种原因离开运行队列;等待 IO 或睡眠是离开运行队列的常见触发器。任何导致进程暂时不需要 CPU 的情况都会导致它离开队列。发生这种情况时,将从休眠进程的 vruntime 中减去当前的最小 vruntime(最左边进程的 vruntime)。结果是,它的 vruntime 现在将反映它比下一个调度进程多出多少 CPU 时间。当它再次尝试加入运行队列时,该队列的最小 vruntime 将被添加到进程的 vruntime 中,并且它将进入队列中间的某个位置,大致与它离开前一个队列时享受的位置相同。

这就是解释。 vruntime 以一种巧妙的方式进行操作,以确保它可以被解释为 CPU 公平性历史记录中的相对差异,无论进程当前是在运行队列中,还是在可调度状态之间。

相关内容