• 周日. 9 月 13th, 2026

Linux 调度器新规则:用虚拟截止时间决定下一个跑谁

Linux 内核调度队列示意图,深蓝色与琥珀色矩形块沿横向时间轴排列,上方由细线连接形成调度决策节点关系。

很多人以为调度器就是「排队叫号」——谁先到谁先跑。但 Linux 内核里跑的那套规则,从最早的直接 O(n) 走到 O(1),再走到 CFS,最近又换了一颗新齿轮:EEVDF。它把「先来先跑」换成了「先到期先跑」。这套规则在 6.6 内核里默认上线,已经成为桌面、服务器和云上绝大多数 Linux 发行版的共同底座。

一、为什么「先来先跑」扛不住现代负载

经典轮转调度给每个进程固定长的时间片,谁先就绪谁上 CPU,时间片用完就排到队尾。问题是真实负载里进程类型天差地别:键盘事件 1 毫秒就要响应,压缩任务可能要霸占 CPU 整整一分钟。固定时间片没办法同时照顾两边——要么交互任务在队列里等到花都谢了,要么长任务被频繁切走白白浪费缓存。

完全公平调度器(CFS)改用「虚拟运行时间」记账:每个进程跑了一小段,把对应的时间按权重折算成虚拟时间,再选虚拟时间最小者上 CPU。这个思路非常优雅,但它隐含一个前提——所有进程的「需求量」大致稳定。一旦某个进程在某段时间里特别饥饿,CFS 没有给它单独的「加塞」通道,结果就是:突发型交互任务依然要排大队。

二、EEVDF 给每个进程一张「截止时间」小卡片

EEVDF 的核心是把「该谁上 CPU」这件事重新建模成「谁先到期」。每个进程被分到的不再只是一个时间片长度,而是一张虚拟截止时间卡片。这张卡片上写着三件事:

  • 截止时间:再晚到这一刻就视为「违约」
  • 滞后量:当前已经比截止时间晚多少(负数代表还有富余)
  • 时间片预算:这一轮允许你跑多长

调度器永远挑「截止时间最早 + 还没用完预算」的那个进程上 CPU。如果一个进程提前用完了预算,它会被立刻打回队列尾并补发新卡片;如果它还在「宽限期」里能继续跑,就让它跑完为止。这种「带截止时间的发牌」让调度器天然区分出两类任务:

  • 延迟敏感型:每次都要的预算很小、卡片「很急」,调度器几乎每轮都先看它一眼
  • 吞吐优先型:预算大、卡片相对宽松,可以一次跑很久再让位

三、虚拟时间到底是怎么算的

EEVDF 没有抛弃 CFS 的虚拟时间概念,而是在它之上叠了一层「提前 / 滞后」调整。具体来说有三个旋钮:

  • 权重 w:进程的重要程度,数字越大分到的时间片越长(nice 值映射过来)
  • 滞后上限 lag_limit:进程允许的最大「欠跑量」,超过就被强制调度一次
  • 宽限期 grace:进程用完预算后允许的额外跑量,避免微小抖动引发立即切换

调度决策本质上在解一个简单的优化:选出一个进程,让它的「虚拟截止时间」最小,同时又不至于太频繁切换。这个优化在数学上等价于一个按截止时间排序的红黑树——CFS 已经在用类似结构,EEVDF 借了过来,只是排序键从「虚拟时间」换成了「虚拟截止时间」。

四、为什么「公平」和「及时」能同时成立

很多人觉得公平调度和延迟敏感是矛盾的——给延迟敏感型多排几次队,它就比其他人多跑了好多 CPU 时间,公平性会被破坏。EEVDF 用一个非常巧妙的处理解决了这个矛盾:

每当一个进程被选中上 CPU,它会从「欠跑」变成「恰好不欠」甚至「微超」,但这个「超」会被记账到下一张卡片里。所以从长时间尺度看,所有进程的「实际跑量」仍然按权重比例分配;从短时间尺度看,延迟敏感型被反复照顾,因为它每张卡片都很紧。

这种「长程公平 + 短程及时」的双层结构,本质上和现实里高铁票务很像:日常按配额分配(公平),但春运加开临时车次(及时)。

五、它和 CFS 真正不一样的地方

CFS 选择下一个进程的依据是「虚拟运行时间最小」,EEVDF 是「虚拟截止时间最小」。两者听起来差别不大,但落地上有三个明显差异:

  • 调度延迟显式建模:CFS 通过「目标延迟 × 权重」隐式控制调度延迟,EEVDF 把截止时间变成一阶对象,调试时可以直接读 /proc/sched_debug 看到每个进程的 lag
  • 抢占判定更明确:CFS 抢占基于「虚拟时间差超过颗粒度」,EEVDF 抢占基于「当前进程是否仍在预算 / 宽限期内」——边界更干净
  • 对突发负载更友好:当大量进程同时进入可运行态,CFS 需要重新排虚拟时间,EEVDF 只需要补发新卡片

六、它正在悄悄取代 CFS 的路上

从 6.6 起,EEVDF 已经是多数发行版的默认调度器。但它并没有一夜之间替换 CFS——内核依然保留着 CFS 作为 fallback,运行时会根据系统特征自动选择。在延迟敏感型工作负载(数据库、在线服务、桌面交互)上 EEVDF 普遍给出更稳定的尾延迟;在纯计算型负载(科学计算、视频编码)上两者差距不大。

对于关心调度行为的工程师来说,最值得记住的是:现在跑 cat /proc/sched_debug,你能直接读懂每个进程的虚拟截止时间、滞后量和预算消耗。这在 CFS 时代是看不到的——EEVDF 把调度器的「内心独白」第一次主动摊在运维眼前。

七、一句话总结

EEVDF 不是为了更快,而是为了「更懂」——它把进程当成有 deadline 的任务来排,而不是当成排队买票的旅客。这套规则让 Linux 内核第一次同时拥有「长程公平」和「短程及时」两套语义,并且把决策过程暴露给了需要它的人。

资料来源:公开网络资料整理。

admin77

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注