- 引言 — 标题与提交记录之间的落差
- 复习 — sched_ext 曾经是什么
- 为什么要子调度器 — cpuset 的局限
- 已有的做法 — scx_flatcg 把层级压平
- 设计 — 把调度器挂到 cgroup 上
- 7.1 里实际落地的东西 — 以及缺失的部分
- 时间线 — 为什么会花这么久
- 7.2 中继续推进的工作
- 那现在该做什么
- 结语
- 参考资料
引言 — 标题与提交记录之间的落差
Linux 7.1 已经在 2026 年 6 月 14 日发布。调度器方面的一条标题是这样的 — sched_ext 加入了 cgroup 子调度器支持。单看这句话,是件相当大的事 — 意味着可以给每个 cgroup 挂上不同的 CPU 调度器。跑容器的人听到这个应该会心动。
可是同一条消息放到 LWN 的合并窗口总结里读,语气就明显不一样了。Jonathan Corbet 在7.1 合并窗口总结里这样写道。原文照搬如下。
The extensible scheduler class (sched_ext) subsystem now has the beginning of support for sub-schedulers, meaning that each control group can run with its own custom CPU scheduler. As of 7.1, this implementation is not complete (it lacks the enqueue path in particular), but much of the needed core structure is there.
关键在第二句 — 截至 7.1,这个实现尚未完成,尤其缺了 enqueue 路径。落地的是「子调度器支持的开端」,不是子调度器本身。
本文讲的正是这个落差。这是个想做什么的功能、为什么需要它、7.1 里实际落地了什么、还缺什么、所以你现在该做什么(大体上是「什么都不做」)。
复习 — sched_ext 曾经是什么
从 CFS 到 EEVDF 的演进另有文章整理过,这里只简单复习一下 sched_ext。
sched_ext 是合入内核 6.12 的可扩展调度器类(原本瞄准 6.11,晚了一个周期)。它让调度策略可以用 BPF 程序编写,在运行时加载、替换。把回调填进struct sched_ext_ops并加载后,从那一刻起,就是这段 BPF 代码来决定哪个任务在哪个 CPU 上、什么时候运行。
这里最重要的设计决定是安全机制。照搬内核文档的原话。
System integrity is maintained no matter what the BPF scheduler does. The default scheduling behavior is restored anytime an error is detected, a runnable task stalls, or on invoking the SysRq key sequence SysRq-S.
不管 BPF 调度器做了什么,系统完整性都会得到维持;一旦检测到错误,或者有可运行的任务卡住了,就会自动恢复到默认调度行为。也可以用 SysRq-S 手动切回去。正因为有这道监视机制,「把内核调度器换成用户代码」这个听起来鲁莽的想法才真正立得住。
为什么要子调度器 — cpuset 的局限
问题的提出,在Tejun Heo 发的补丁系列附信里说得很清楚。情况是这样的:一台大机器上跑着好几种性格不同的工作负载,各自想要的调度策略不一样 — 对延迟敏感的服务、看重吞吐量的批处理任务、像游戏那样看重交互性的东西,全都混在一个箱子里。
到目前为止的答案是硬分区。照搬附信原话。
Traditional approaches rely on hard partitioning via cpuset, but this approach lacks the dynamism required by modern workloads.
用 cpuset 切分 CPU 的做法,缺了现代工作负载所需要的那种动态性。为什么不够,附信也讲得很具体。
Users typically care less about specific CPU assignments and more about optimizations available on larger machines: opportunistic over-commit, improved latency for critical workloads while preserving bandwidth fairness, control mechanisms beyond simple CPU time (such as memory bandwidth isolation), and intelligent placement to optimize cache locality.
换句话说:人们真正想要的不是「这个工作负载归 3 号到 7 号核心」。他们想要的是只有共享一台大机器才能拿到的东西 — 机会主义式的超额分配、在保住带宽公平性的同时改善关键工作负载的延迟、超越单纯 CPU 时间的控制手段(比如内存带宽隔离)、以及照顾缓存局部性的智能布局。CPU 一旦提前切好,这些一样也做不到 — 只能在切好的那块地里打转。
至于为什么内核不能代劳这件事,答案在Phoronix 引用的第二版附信里。
Applications often have domain-specific knowledge that generic schedulers cannot possess. Database systems understand query priorities and lock holder criticality.
应用程序拥有通用调度器不可能具备的领域知识。数据库知道哪个查询更急,哪个线程握着锁、现在不让它跑大家就都会卡住。内核不知道。sched_ext 最初的论点是「那就让懂的一方来写策略」,而子调度器正是把这个论点从整台机器下放到 cgroup 这一级的尝试。
已有的做法 — scx_flatcg 把层级压平
为免误解,有件事得先说清楚:并不是说 sched_ext 之前完全不懂 cgroup。示例调度器里就有一个scx_flatcg,内核文档是这样描述它的。
implements hierarchical weight-based cgroup CPU control by compounding each cgroup's share at every level into a single flat scheduling layer
它实现了基于权重的层级式 cgroup CPU 控制,但做法是把每个 cgroup 在每一层的份额相乘,最终合并成一层扁平的调度层。名字里的 flat 就是这个意思。
这是个重要的对比。现有方式表现出了层级,但最后还是压扁成一层,由一个调度器统一处理。权重是守住了,但每个 cgroup 用不同策略这件事依然做不到 — 归根结底只有一个调度器。子调度器要改变的正是这一点 — 不要压平层级,而是在层级的每个节点真正放上不同的调度器。
设计 — 把调度器挂到 cgroup 上
Corbet 的《Sub-schedulers for sched_ext》(2026 年 1 月 29 日)把 v1 阶段的结构整理得很清楚。核心规则只有一句话。
Each task in the system will be governed by the scheduler attached to its control group (or to the nearest ancestor group).
系统里的每个任务都受自己所在控制组挂载的调度器支配。如果那个组没有调度器,就跟随最近的祖先组。这是个熟悉的模式,跟 DNS 解析或文件系统权限继承是一路的。
用附信的说法是这样的。
schedulers to attach anywhere in the cgroup hierarchy, with parent schedulers dynamically controlling CPU allocation to their children.
调度器可以挂载在 cgroup 层级的任意位置,父调度器动态控制分给子级的 CPU 配额。分工就在这里划开:按 Corbet 的说法,CPU 控制器依然负责「这些东西能分到多少 CPU 时间」,子调度器则负责「在那段时间里怎么调度自己组里的进程」。
深度是有限制的。附信原话。
The framework supports scheduling hierarchies up to SCX_SUB_MAX_DEPTH levels (currently set to 4).
上限是SCX_SUB_MAX_DEPTH,当前值是 4。不是无限嵌套,而是 4 层。
画出来是这样的。
root cgroup ── 根 BPF 调度器(把 CPU 分给子级)
│
├── /db ── scx 调度器 A(懂查询优先级、锁持有者的策略)
│
├── /web ── scx 调度器 B(延迟优先)
│ └── /web/batch ── 没有调度器 -> 跟随最近的祖先(B)
│
└── /batch ── 没有调度器 -> 跟随根调度器
深度限制: SCX_SUB_MAX_DEPTH = 4
还有一个机制更值得一提,是 Corbet 指出的安全模型 — 给 kfunc 附加一个隐式参数,让它能访问bpf_prog_aux结构体。结果就是这样。
BPF programs themselves need never specify which scheduler they are operating on.
BPF 程序完全不需要自己申报自己属于哪个调度器。要是让它自己申报,它就可能撒谎 — 想象一个 BPF 程序去碰别人调度器的队列,就立刻明白为什么这一点重要。内核直接从程序本身读出归属。
7.1 里实际落地的东西 — 以及缺失的部分
这才是本文的正题。
7.1 里落地的是dispatch 路径。按 Corbet 的定义,dispatch 是「让调度器挑出下一个要运行的任务,放进某个 CPU 的派发队列」的回调。也就是说,「在已经准备好运行的任务里,什么时候派发什么」这件事的层级化处理骨架进来了。
缺失的部分更重要。Corbet 在评审 v1 时指出的未实现项如下。
select_cpu()回调 — 任务被唤醒时挑选发往哪个 CPU 的路径enqueue()回调 — 把任务放入队列的路径
而且有人指出 bypass 模式需要重新设计。理由很具体。
allowing one sub-scheduler to toss processes into a global FIFO queue could lead to interference with other sub-schedulers
让某一个子调度器把进程扔进全局 FIFO 队列,可能会干扰其他子调度器。这个功能本来就是为了隔离,结果出现一条会泄漏隔离性的路径,这就不是能轻描淡写带过的问题了。顺带一提,这个问题留下了痕迹 — 当前内核文档里唯一提到子调度器的地方,实际上只有SCX_EV_SUB_BYPASS_DISPATCH事件计数器说明的这一行,内容是「tasks dispatched from sub-scheduler bypass DSQs (only relevant with CONFIG_EXT_SUB_SCHED)」。它只是作为诊断计数器存在,而不是功能文档。
附信自己也没有回避这个状态。
While the enqueue path and other components require further development, this patchset establishes the core mechanisms for nested scheduler operation.
enqueue 路径和其他组件还需要进一步开发,但这个补丁集建立了嵌套调度器运作的核心机制。这是句诚实的话。
kernelnewbies 的7.1 变更总结也守着同一条线 — 写道 sched_ext 加入了子调度器支持,而这个功能的目的是「在未来版本中」让不同的 cgroup 用上不同的调度器。原文是「The purpose of this feature is to eventually allow (in future releases) using different schedulers in different cgroups」。eventually 和 in future releases 是关键词。
归纳一下就是这样。
子调度器 — 走到了哪一步(截至 2026-07-16)
dispatch 路径 [ 已合入 7.1 ]
enqueue 路径 [ 未完成 — 后续补丁集开发中 ]
select_cpu() 路径 [ v1 评审时尚未实现 ]
bypass 模式 [ 需要重新设计(全局 FIFO 干扰问题) ]
每个 cgroup 各用不同调度器 [ 还做不到 — 「未来版本」 ]
时间线 — 为什么会花这么久
按时间顺序排列,就能看出这个功能的推进速度。
2024-11 sched_ext 本身合入内核 6.12(原定 6.11,延后)
2025-09-19 Tejun Heo 发布 cgroup 子调度器支持的 RFC 补丁集
2026-01-29 LWN《Sub-schedulers for sched_ext》— v1 评审
2026-02-25 发布 v2,标记在 sched_ext/for-7.1 分支上
2026-03-04 合入 7.1 的这组补丁的附信
2026-04-27 LWN 7.1 合并窗口总结 — 「实现尚未完成」
2026-06-14 Linux 7.1 发布(包含 dispatch 路径)
2026-06-18 LWN 7.2 合并窗口 — 「子调度器支持的加入仍在继续」
2026-07-16 (今天)主线是 7.2-rc3,稳定版是 7.1.3
从 RFC 到部分合入用了 9 个月,而且现在还用不了。这里有必要重新看一下 Corbet 在 v1 评审时留下的预测。
One should thus not expect to see sub-scheduler support in the kernel for some time yet.
短时间内不要指望在内核里看到子调度器支持。这句话写于 1 月,5 个月后 7.1 里「支持的开端」已经进来了,但它依然成立。因为骨架进来了和功能能用是两回事。内核里的大功能拆成碎片、分好几个周期进来是正常的,甚至是健康的做法。只是把其中第一块合入的消息读成功能发布,就会踩空。
7.2 中继续推进的工作
7.2 合并窗口里,工作还在继续。Corbet 的《7.2 合并窗口前半段》总结只有短短一行 — 「The addition of support for sub-schedulers in sched_ext continues.」
Phoronix 对 7.2 中 sched_ext 的总结(2026 年 6 月 22 日)说得更具体一些。7.2 里落地的是基于拓扑的 CPU ID(CID)、cmask 基础设施、BPF arena 集成,以及重写后用来演示新 arena、CID 接口的scx_qmap示例。全都是基础设施。
而拉取请求里有这样一句话。
A follow-up patchset in development will complete enqueue-path support for hierarchical scheduling.
开发中的后续补丁集将会完成层级调度的 enqueue 路径支持。注意这是将来时。7.2 里同样没有 enqueue 路径。而且截至今天,7.2 本身还是 rc3,还没发布。
那现在该做什么
老实说 — 大部分什么都不做。
现在做不到的事
- 给每个 cgroup 挂不同的 BPF 调度器。这是标题所写的,但现在还做不到。
- 升级到 7.1 内核来享受这个功能的好处。光有 dispatch 路径这层骨架,工作负载不会有任何变化。
- 以这个功能为前提做容量规划或架构决策。enqueue 路径什么时候完成、完成后的形态是否和现在的设计一样,没有人给出确切答案。附信里甚至提到过一个临时的 BPF 绕行方案,用来等 upstream 拿出正式的解决办法。
现在能做的事
- sched_ext 本身从 6.12 起就能用了。它是给整台机器挂一个调度器的方式,有监视机制兜底,实验成本不高。
- 如果需要基于 cgroup 权重的 CPU 控制,
scx_flatcg已经存在。它把层级压平,所以只能有一种策略,但很多场景下这样已经够用。 - 如果多租户隔离现在就很紧迫,cpuset、cgroup CPU 控制器依然是答案。代价是承受附信指出的那个局限(不够动态),换来的是今天就能用。
什么时候再回来看
等到听说 enqueue 路径已经合入的那一天。在那之前,这个功能是路线图,不是工具。而且即便合入之后,sched_ext 自己的历史也说明了一点 — 6.11 的目标推到了 6.12,子调度器的 RFC 走了 9 个月还只到一半 — 时间表最好留得宽松一些。
结语
归纳一下:sched_ext 子调度器是个好主意。问题提得准(cpuset 硬分区不够动态),论点一贯(策略应该由懂领域知识的一方来写),设计也合理(把调度器挂到 cgroup 层级上、继承最近的祖先、限制在 4 层以内、归属由内核判定)。
而 7.1 里落地的,只是其中的 dispatch 路径。仅此而已。没有 enqueue 路径,没有select_cpu(),bypass 模式还等着重新设计,内核文档提到这个功能也只有一行事件计数器的说明。「每个控制组都能跑一套自己专属的 CPU 调度器」这句话,描述的是意图,不是现在时。
这不是批评。内核的工作方式本来就是这样,附信、LWN、kernelnewbies 都把自己的状态写得很准确 — 「further development」、「not complete」、「eventually」、「in future releases」。不准确的从来不是这些信源,而是把它读成功能发布的这种读法。
我读内核发布说明时用的标准是这个:合入和能用是两回事,功能越大,两者之间的距离就越远。衡量这段距离最便宜的办法,就是在合并窗口总结里找「not complete」这类词。通常,它就写在那里。
参考资料
- Sub-schedulers for sched_ext — Jonathan Corbet, LWN, 2026-01-29
- sched_ext: Implement cgroup sub-scheduler support — Tejun Heo 的补丁集发布(LWN 转载), 2025-09-19
- The rest of the 7.1 merge window — Jonathan Corbet, LWN, 2026-04-27
- The 7.1 kernel has been released — LWN, 2026-06-14
- The first half of the 7.2 merge window — Jonathan Corbet, LWN, 2026-06-18
- Linux 7.1 变更内容 — kernelnewbies
- Sub-Scheduler Support Could Be One Of The Most Exciting Features To Come For Linux 7.1 — Phoronix, 2026-02-25
- Linux 7.2 sched_ext Continues Working Toward Sub-Scheduler Support — Phoronix, 2026-06-22
- Extensible Scheduler Class — 内核官方文档
- Sched_ext Merged For Linux 6.12 — Phoronix
- The Linux Kernel Archives — 当前发布状态
- Linux CPU 调度器的演进 — 从 CFS 到 EEVDF(相关文章)
현재 단락 (1/95)
Linux 7.1 已经[在 2026 年 6 月 14 日发布](https://lwn.net/Articles/1077758/)。调度器方面的一条标题是这样的 — sched_ext 加入了 ...