6.6 内核(2023 年 10 月)把 SCHED_NORMAL 的默认算法从 CFS 切换到 EEVDF。这是 Linux 调度器十多年来最大的一次骨架替换。CFS 的红黑树、cfs_rq、sched_entity 都还在,但选谁下一个的规则变了。
本文讲 EEVDF 的基本概念、它解决了 CFS 的哪些痛点、latency-nice 的新 API、以及生产影响。
一、先看图:CFS vs EEVDF 选择规则
flowchart LR
subgraph CFS[CFS · 按 vruntime]
A1[树按 vruntime 排]
A2[选最左 = 最小 vruntime]
A1-->A2
end
subgraph EEVDF[EEVDF · eligible + deadline]
B1[lag > 0 的 = eligible]
B2[eligible 中选 virtual deadline 最早]
B1-->B2
end
CFS -.6.6 替换.-> EEVDF
classDef old fill:#63636e22,stroke:#636e7b,color:#adbac7;
classDef new fill:#3fb95022,stroke:#3fb950,color:#adbac7;
class A1,A2 old
class B1,B2 new
二、CFS 的痛点
CFS 是”长期公平”。但长期公平允许短期极不均:一个任务连续跑 10ms 后让出 10ms,它和另一个连续跑 20ms 的任务看起来总量相同,但延迟感觉差很多。
具体问题:
- 没有 per-task
延迟控制:
sched_latency_ns是全局 - 长 sleep 回来的任务抢占不可控:依靠 min_vruntime + GENTLE_FAIR_SLEEPERS 启发式
- 交互任务需要更多”加分”:vruntime 体系不直接支持
三、EEVDF 的两个新概念
EEVDF 由 Stoica 等 1996 年提出,论文名 “Earliest Eligible Virtual Deadline First”。核心两步:
3.1 Eligible time & lag
每个任务有”理想累计服务时间” = 从上线到现在按权重分得的时间。
lag(i) = ideal_service(i) - actual_service(i)
lag > 0:欠它的 → eligible 可被选lag < 0:欠着别人(超跑了)→ 不 eligible,等 virtual time 追上
这避免了 CFS 里”某任务长睡后 vruntime 老小,回来无限碾压”的问题——睡期间它的 ideal_service 按暂停时间推进,回来时 lag 是有界的。
3.2 Virtual deadline
给任务一个 slice(时间片),对应的 virtual deadline:
vdeadline = veligible + slice / weight
选择规则:所有 eligible 任务中,virtual deadline 最早的优先。
直觉:slice 小 → deadline 来得早 → 被选得频繁,但每次跑得短 → 延迟敏感任务的主动让出策略。slice 大 → 批处理大段跑。
四、latency-nice:新 API
EEVDF 配套引入 latency-nice(或
sched_attr.sched_latency_nice),范围 -20 ~
19,默认 0。
作用:调整 slice 大小。
- latency-nice = -20 → slice 很小 → 频繁但短 → 低延迟
- latency-nice = 19 → slice 很大 → 批处理风格
与 nice(权重)正交:nice 决定份额,latency-nice 决定粒度。
API:
struct sched_attr attr = {
.size = sizeof(attr),
.sched_policy = SCHED_NORMAL,
.sched_latency_nice = -10,
};
sched_setattr(0, &attr, 0);或 systemd unit file:
[Service]
LatencyNiceOnCPU=-10
这对”交互式服务 + 批处理服务”混部是巨大改进——以前只能靠 autogroup / cgroup cpu.weight 粗粒度做。
五、实现侧:沿用 CFS 骨架
EEVDF 代码在
kernel/sched/fair.c,直接替换了部分逻辑:
cfs_rq仍在,还是红黑树,但键从 vruntime 改为 virtual deadline(实际是 eligible/deadline 的组合判断)sched_entity新增deadline、slice- 新的
pick_eevdf()替代pick_next_entity() - PELT、load-balance、frequency selection 等仍共享
因此生态(负载均衡、cgroup CPU controller、autogroup)大部分不变。过渡平滑。
六、早期 benchmark
社区测试(lwn.net 报道、phoronix):
- 桌面响应:小幅提升,感官上 Chrome/IDE 更顺
- 交互 + 批处理混合:明显改善,尤其
make -j并发下桌面不卡 - 纯 CPU-bound 吞吐:基本持平
- 短任务延迟(schbench):EEVDF p99 延迟降低 20-40%
- kernel compile:持平
Peter Zijlstra(作者)基于 IngoMolnár 对 CFS 的反思,说 EEVDF 是”我本应一开始就实现的调度器”。
七、与实时 / PREEMPT_RT 的关系
EEVDF 是 SCHED_NORMAL 算法;不影响 SCHED_FIFO/RR/DEADLINE。实时任务仍按老路径。
对 PREEMPT_RT(完全抢占内核):EEVDF 让普通任务延迟更可控,但硬实时仍需 SCHED_FIFO + rt_mutex 组合。
八、仍待解的问题
- latency-nice 的最佳值:生态还在摸索,很多应用尚未调参
- cgroup 接口:latency-nice 在 cgroup 层的表达还不完全
- energy-aware:与 EAS / Intel ITD 协同需要继续打磨
- MuQSS / BORE / PDS 等实验性调度器的研究价值继续观察
九、迁移实务
9.1 如何确认用的是 EEVDF
cat /sys/kernel/debug/sched/features
# EEVDF 相关 flag 在
dmesg | grep -i eevdf
内核 >= 6.6 默认就是。
9.2 旧 tunable 命运
kernel.sched_latency_ns、kernel.sched_min_granularity_ns
等保留但语义略变——仍用于 slice 大小计算。一些被移到
/sys/kernel/debug/sched/。
9.3 应用上 latency-nice 的场景
- 游戏引擎主线程:-10 ~ -20
- 视频播放:-5 ~ -10
- 编译器 worker:+10 ~ +15
- 监控 agent:0(默认)
9.4 容器里
containerd / runc 已有 PR 支持在 OCI spec 里传
latencyNice。K8s 1.29+ alpha
PodLatencyNice。
十、小结
- EEVDF 用 eligible + virtual deadline 取代纯 vruntime,把延迟变成一等目标
- latency-nice 与 nice 正交,per-task 控制 slice
- 6.6 起默认;CFS 骨架复用,过渡平滑
- 交互式/混部场景 p99 延迟明显改善
- 硬实时仍走 SCHED_DEADLINE
下一篇 C-22 讲 SCHED_FIFO/RR 与 PREEMPT_RT——把 Linux 变成软/硬实时的代价与收益。
参考文献
- Stoica, I.; Abdel-Wahab, H.; Jeffay, K.; Baruah, S.K.; Gehrke, J.E.; Plaxton, C.G. “A proportional share resource allocation algorithm for real-time, time-shared systems.” RTSS 1996
- Zijlstra, P. “EEVDF patch series” (lkml 2023)
- Corbet, J. “An EEVDF CPU scheduler for Linux.” LWN.net 2023-03
- Corbet, J. “EEVDF hits the mainline.” LWN.net 2023-09
- Linux kernel:
kernel/sched/fair.c(6.6+) - Documentation/scheduler/sched-eevdf.rst(chap 撰写中)
工具
schbench—— 交互延迟基准hackbench—— 调度器压力perf sched record/latencybpftrace -e 'tracepoint:sched:sched_switch ...'chrt -p、sched_setattr
上一篇:CFS 内部:vruntime 与红黑树 下一篇:SCHED_FIFO/RR 与 PREEMPT_RT
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【操作系统百科】内存回收
Linux 内存回收是 VM 最复杂的子系统之一。本文讲 active/inactive LRU、kswapd 与 direct reclaim、watermark 三线、swappiness 的真实含义、MGLRU 改造、memcg 回收与 PSI。
【操作系统百科】交换
swap 还值得开吗?本文讲 swap area 基础、swap cache、zram 压缩内存、zswap 前端压缩池、swappiness 的真实含义、容器里的 swap 策略,以及为什么现代 Android 全靠 zram 不靠磁盘。
【操作系统百科】Slab/SLUB 分配器
buddy 只管页粒度(4K+),内核大多数对象只有几十到几百字节。slab/SLUB 在 buddy 之上做对象级缓存。本文讲 slab 历史、SLUB 接手、SLOB 退场、kmem_cache、per-CPU cache、KASAN 集成。
【操作系统百科】用户态分配器
glibc malloc、tcmalloc、jemalloc、mimalloc 各有哲学。本文讲 arena、thread cache、size class、madvise 返还策略、碎片与 RSS 膨胀、如何根据负载选分配器。