Linux 调度器换了心脏:EEVDF 替代 CFS 之后

Linux 6.6 在 2023 年末改掉了沿用十五年的 CFS 调度器,换成了 EEVDF。这件事在内核社区引发过不少讨论,在更广泛的工程圈里几乎没有激起什么水花。

大多数工程师对调度器的感知,停留在”它在底下自动跑,我不用管”。这个感知在大部分场景里没错,但在越来越多的延迟敏感型工作负载里,调度器的行为已经成了 p99 延迟的直接解释变量。


CFS 是什么,以及它的问题

CFS(Completely Fair Scheduler)由 Ingo Molnár 在 2007 年引入,核心思路优雅:用一个叫做 vruntime(虚拟运行时间)的值来衡量每个进程”已经运行了多少时间”,调度器总是选择 vruntime 最小的进程运行——即”最饥饿的那个”。

这个设计在理论上保证了公平性:每个进程在权重相等的情况下,获得的 CPU 时间大致相同。实现上用红黑树管理所有可运行进程,按 vruntime 排序,最左节点就是下一个运行的候选。

问题在于,”公平”和”低延迟”是两件事。

CFS 的局限在实践中逐渐积累:

时间片边界不清晰。CFS 没有固定的时间片,进程何时被抢占,取决于 vruntime 的相对值和系统中可运行进程的数量。这导致进程实际获得的调度时机是动态的,难以预测。

对短暂唤醒的处理有缺陷。一个进程睡眠后被唤醒,它的 vruntime 会被重置到一个”最小合理值”(min_vruntime),避免它因为睡眠太久而在唤醒后获得过多 CPU 补偿。但这个重置逻辑在很多场景下会产生误判:刚唤醒的延迟敏感进程,可能需要等待一个已积累了大量 vruntime 的 CPU 密集型进程先让出来。

cgroup 嵌套的行为难以推理。cgroup 的 CPU 权重调度叠加在 CFS 上,在嵌套层级深的时候,实际调度行为和配置意图之间的距离会显著拉大。

这些问题不是设计缺陷,它们是 CFS 设计假设的自然结果:CFS 是为公平吞吐量设计的,不是为可预测延迟设计的。


EEVDF 换了什么

EEVDF(Earliest Eligible Virtual Deadline First)不是全新发明。它的理论来自 1995 年 Waldspurger 和 Weihl 的论文,Peter Zijlstra 在 2023 年将其移植到 Linux 内核,替代 CFS 的进程选择逻辑。

核心机制有两个约束:

Eligible(可调度资格):一个进程要被选中运行,必须满足它的”虚拟开始时间”小于等于当前虚拟时钟。这是一个资格门槛——还没到分配时间窗口的进程,即使 deadline 最近,也不能抢占当前进程。

Virtual Deadline(虚拟截止时间):每个进程被分配一个虚拟截止时间,基于它的权重和时间片计算得出。在所有 eligible 的进程中,优先选择 deadline 最近的那个。

两个约束合在一起,形成了比 CFS 更有结构的调度顺序:进程的运行时机基于它”应该在什么时候完成这次时间片”,而不是”它比别人少运行了多少”。

对工程师而言,最直接的变化是:唤醒延迟更可预测了

一个等待 I/O、网络包或者锁的进程,在 EEVDF 下被唤醒时,有更高概率在合理时间内拿到 CPU,而不是在红黑树里等待某个 vruntime 先追上其他进程。调度行为从”公平竞争”变成了”按约定时间表运行”。


实际影响有多大

Linux 6.6 发布后,早期测试结果出现了分歧。

游戏工作负载(Phoronix 的 Linux gaming benchmarks)在某些场景下出现了小幅退化,主要集中在帧时间的 p99 尾延迟上——这引发了社区讨论和若干可调参数的后续修正。

数据库和网络服务的基准测试则大多显示持平或略有改善,尤其在高并发、I/O 密集型场景下,调度延迟的方差有所收窄。PostgreSQL 社区有工程师测试了 EEVDF 对 pg_bench 的影响,OLTP 场景下 p99 延迟下降了约 8-12%,吞吐量几乎没有变化。这个结果符合 EEVDF 的设计意图:不试图提高吞吐量,专注于让延迟分布更紧凑。

EEVDF 同时引入了一个新的参数 latency_nice(通过 sched_setattr 系统调用设置),允许进程声明自己对延迟的敏感度。这是 CFS 时代 nice 值之外的新维度——nice 控制权重(影响吞吐量分配),latency_nice 控制调度优先级(影响响应时间)。

这个参数目前在 Linux 用户态工具链里的支持还不完整,但方向是正确的:给应用层一个显式的方式来表达”我是延迟敏感的”,而不是靠调整 nice 值或 SCHED_FIFO 来曲线救国。


从工程实践角度看

几件值得实际关注的事:

大多数工作负载感受不到差异。如果你的服务是 CPU 密集型、批处理型,或者本身对延迟容忍度高,EEVDF 对你的影响几乎为零。这些场景里,调度器的优化空间远不如网络栈、内存分配器、锁竞争优先。

延迟敏感服务值得做一次对比测试。实时音视频处理、低延迟交易系统、游戏服务器、在线推理服务——这些场景里,调度延迟是 p99 的组成部分之一,升级到 6.6+ 内核后做一次 before/after 对比是合理的投入。

cgroup 行为有变化。EEVDF 改变了 cgroup CPU 权重在调度组内的展开方式。如果你的服务运行在容器里,且 CPU request/limit 配置得比较精细,升级后应该验证资源隔离行为是否符合预期,不要假设行为不变。

调度器 tuning 的方式变了。CFS 时代常见的 kernel.sched_min_granularity_nskernel.sched_wakeup_granularity_ns 等参数,在 EEVDF 下部分失效或语义发生了变化。从旧内核迁移过来时,把之前的调度器 tuning 清零重测,比直接沿用更稳妥。


为什么这件事被低估

调度器是操作系统最深处的机制之一,也是最难感知的。它的影响藏在 p99、p999 延迟里,藏在偶发的卡顿里,藏在 profiling 时那些看起来”很短但加起来不少”的调度等待里。

CFS 在 2007 年是个好答案。2007 年的主流工作负载是 CPU 密集型的桌面应用和服务器进程,公平调度直接对应了用户体验。2026 年的主流工作负载是网络密集型、I/O 密集型、延迟敏感的云原生服务——它们大部分时间在等待,不在运算,被唤醒时需要立即响应。

“公平”在这里反而成了代价:一个等了很久的 CPU 密集型进程和一个刚从网络等待中醒来的 RPC handler,用 vruntime 公平竞争,对后者来说是不公平的。

EEVDF 不是银弹,但它是一次方向正确的修正:从”每个进程得到公平的 CPU 时间”,转向”每个进程在应该运行的时候运行”。

这两句话听起来相似,但背后是完全不同的设计哲学。


Linux 调度器换了心脏:EEVDF 替代 CFS 之后
https://www.krli.org/2026/05/07/Linux-调度器换了心脏-EEVDF-替代-CFS/
作者
李科燃
发布于
2026年5月7日
许可协议