当你的数据库 VM、Web 服务器、AI 训练任务可以在同一台物理机上各自"请"一个专属调度器——会发生什么?
一、开篇:一个困扰了虚拟化领域十几年的老问题
想象一下这个场景:
你在一台物理服务器上跑了三个虚拟机——一个跑着延迟敏感的数据库、一个在做 HPC 计算、还有一个是桌面云。三个 VM 共享着同一批物理 CPU 核。
问题是:宿主机调度器不知道每个 VM 内部在干什么。
数据库 VM 可能正在处理一个关键事务,它的 vCPU 急需被调度——但宿主机调度器看不到这个信息,它只是公平地轮转所有 vCPU 线程。于是数据库的延迟多出了几个毫秒,而这在金融交易场景里可能就是不可接受的。
反之亦然。HPC VM 的 vCPU 可能在空转,但宿主机不知道,仍然给它分配了时间片,导致资源浪费。
这就是虚拟化领域著名的 "双重调度(Double Scheduling)"问题。十几年来,人们提出了各种方案:
❌ 早期方案:让 KVM(内核虚拟机)内嵌调度策略 → 被拒绝,KVM maintainer 说"调度策略不该是 KVM 的事" ❌ 中期方案:KVM 做握手 + 内核模块做策略 → 又被拒绝,因为握手逻辑也不该进 KVM ✅ 最终共识:VMM 做握手 + BPF 调度器做策略
而这个 BPF 调度器方案,正是本文的主角——sched_ext。
二、sched_ext 是什么?一句话版
sched_ext = “插件化调度器框架”
它允许你像装一个 App 一样,动态加载一个用 BPF(Berkeley Packet Filter) 写的 CPU 调度器,而不需要编译内核、不需要重启机器。
想试试一个新的调度算法?写几十行 BPF 代码,跑一个二进制文件,它就生效了。不满意?再跑一个命令就切回去了。就这么简单。
这个功能在 Linux 6.12(2024 年下半年)正式合入主线,背后站着 Meta、Google、Valve 等大厂。
三、它背后的设计哲学
为什么传统方式不行?
Linux 的默认调度器(EEVDF/CFS)是一个通用调度器——它试图在所有场景下都表现良好。但现实世界是:
一个调度器搞定所有?不可能。
sched_ext 的设计哲学就是:不要试图用一个调度器取悦所有人,而是让大家自己写调度器。
核心技术:DSQ(Dispatch Queue,分发队列)
sched_ext 引入了一个聪明的抽象——DSQ,它是 BPF 调度器和内核核心调度器之间的缓冲层:
BPF 调度器(策略层) ↕ 通过 DSQ 通信内核核心调度器(执行层) ↕物理 CPUDSQ 有三种内置类型:
SCX_DSQ_GLOBAL— 全局 FIFO 队列 SCX_DSQ_LOCAL— 每个 CPU 独占的 FIFO 队列 - 自定义 DSQ
— BPF 自己创建管理的队列
无论 BPF 调度器做出什么复杂决策,最终每个 CPU 只是从自己的本地 DSQ 里取出任务执行——简单、确定、高效。
调度四步曲
一个任务从醒来(wakeup)到最终在 CPU 上执行,经过四个阶段:
① select_cpu() → 给"即将醒来"的任务挑个 CPU② enqueue() → 把任务放入某个队列③ dispatch() → CPU 空闲了,从队列里取出任务④ CPU 执行每个阶段都是一个 BPF 回调,你可以在任何一个阶段插入自己的调度逻辑。
四、子调度器(Sub-Scheduler):让调度器可以"套娃"
如果说 sched_ext 是"你可以自己写调度器",那子调度器就是"你可以写一堆调度器,让它们协作"。
为什么需要子调度器?
回到开头的场景:一台物理机上有数据库 VM、HPC VM、桌面云 VM。
宿主机├── cgroup /vm/db-vm → 我需要低延迟调度!├── cgroup /vm/hpc-vm → 我需要最少噪声!└── cgroup /vm/desktop-vm → 我需要交互流畅!子调度器的方案:
根调度器 (scx_layered) ← 控制总带宽 │ ├── /vm/db-vm ← 子调度器 A: deadline-aware ├── /vm/hpc-vm ← 子调度器 B: scx_tickless └── /vm/desktop-vm ← 子调度器 C: scx_lavd每个 VM 跑着自己的专属调度器,互不干扰。
核心机制:scx_bpf_sub_dispatch()
父调度器通过调用这个函数,把 CPU 控制权交给子调度器:
c // 父调度器的 dispatch 回调voidBPF_STRUCT_OPS(parent_dispatch, s32 cpu, ...){// 先调度自己的任务 ...// 把 CPU 交给数据库 VM 的子调度器 scx_bpf_sub_dispatch(cgroup_of_db_vm, ...);// 把 CPU 交给 HPC VM 的子调度器 scx_bpf_sub_dispatch(cgroup_of_hpc_vm, ...);}
关键点:父调度器决定"谁获得 CPU",子调度器决定"拿到 CPU 后跑谁"。这是层次化带宽控制的最优雅实现。
安全隔离
最多嵌套 4 层(防止栈溢出) 子调度器挂了 → bypass 级联向上传播,保证系统不卡死 父调度器挂了 → 所有子调度器自动 fallback 到 EEVDF
Linux 7.2 的新基础设施
2026 年 6 月合入的 Linux 7.2 为子调度器铺好了路:
| Topological CPU IDs (CIDs) | |
| cmask | |
| BPF Arena 集成 | |
| scx_qmap 重写 |
五、挑战与展望
当然,任何新技术都不是银弹:
| 调试难度 | |
| 性能开销 | |
| 成熟度 | |
| ABI 不稳定 |
但方向是明确的——从 LKML 到 LPC,从 Meta 到 Google,社区已经在这条路上投入了大量资源。
六、写在最后
sched_ext 的子调度器代表了 Linux 内核调度领域的一次范式转变:
从 “一个调度器管所有” 到 “可组合的调度器层级”。
对虚拟化平台而言,这意味着:
✅ 每个 VM 跑专属调度器 ✅ CPU 弹性共享,无需硬分区 ✅ Host 能感知 Guest 负载(pvSched) ✅ 调度策略热切换,零重启
KVM 社区已经明确表态:调度策略是 sched_ext 的事,不是 KVM 的事。
参考来源:
Phoronix: Linux 7.2 sched_ext LWN: Sub-schedulers for sched_ext sched-ext OVERVIEW.md LPC 2024: Paravirtual Scheduling
夜雨聆风