第68期 | 一本欠账,CFS 怎么算公平

操作系统 · · 九羲科技

第六章第三期。上一期我们数了 O(1) 的两套点名册:140 个优先级,active 点空就翻转。点名册的问题不在慢,在"猜"——靠 sleep_avg 猜谁是交互进程,加分、扣分、调优先级。猜,就有空子可钻。2007 年,有人把整套猜法连根拔起,换成了一本账。

一、一本欠账,CFS 怎么算公平

2007 年,Ingo Molnar 把 CFS(Completely Fair Scheduler,完全公平调度器)贴上 LKML¹²;同年 10 月,Linux 2.6.23 发布,CFS 取代 O(1),成为默认调度器²。公告里,Molnar 致谢了一个人:Con Kolivas——他的 Rotating Staircase Deadline(旋转楼梯死线调度),给了 CFS 最初的启发²。

CFS 的设计文档里有两句狠话。第一句:"80% of CFS's design can be summed up in a single sentence: CFS basically models an 'ideal, precise multi-tasking CPU' on real hardware"——在真实硬件上,模拟一台理想的、精确的多任务 CPU¹。这台 CPU 不存在:它能让 n 个任务真正并行,每人恰好分到 1/n 的算力。真实硬件一次只能跑一个任务,于是 CFS 引入"虚拟运行时间"vruntime:真实运行时间,按权重折算¹。

折算公式很直白:vruntime += 真实运行时间 × 1024 ÷ 权重³。权重由 nice 值查表:nice 0 对应 1024,nice 5 对应 335,nice -5 对应 3121;相邻两档 nice,权重差 1.25 倍³。nice 0 的任务跑 1 秒,账上记 1 秒;nice 5 的任务跑同样 1 秒,账上记 1×1024/335 ≈ 3.06 秒——权重小的,账记得更狠。公平的定义,就这样被翻译成一句话:所有任务的 vruntime 相等。

账记在哪?每颗 CPU 一棵红黑树,按 vruntime 排序;调度器永远取最左边的节点——账上最欠的那个⁴。插入是 O(log n),但最左节点被缓存着,取任务这一步是 O(1)³。还有防作弊的细节:内核维护一个单调递增的 min_vruntime,新出生、刚睡醒、刚迁移来的任务,vruntime 都从它起步⁵——睡了一万年的任务醒来,不会凭一纸旧账霸占 CPU。

第二句狠话:"CFS has no heuristics whatsoever"——没有任何启发式;连"时间片"这个概念都扔了¹。O(1) 时代那些专钻启发式空子的攻击程序(fiftyp.c、thud.c……),在 CFS 面前全部失效¹。公平不再靠猜,靠记账。

算盘拨完,两个数字:相邻 nice 差一档,CPU 按 55:45 分³;sched_latency(调度周期,如今的默认值 6ms⁶)保证每个可运行任务在这段时间内至少轮到一次。

CFS 的一本账:红黑树按 vruntime 排序,调度器永远取最左——账上最欠的先跑;权重小的任务,账记得更快。

这里有个从"险绝"到"平正"的回环。O(1) 的 sleep_avg 奖惩、5 档加减、一堆可调参数,是险绝;CFS 把公平翻译成一本账、一棵树、一条"永远取最欠",是复归平正。孙过庭《书谱》里那句,正是写给它的:"初学分布,但求平正;既知平正,务追险绝;既能险绝,复归平正"⁷(思想化用)。

二、货币换了,公平就得重算

如果 AI 的操作系统先出现,vruntime 这套账会怎么记?先看它有个从没被问过的前提:CPU 时间是可通约的货币。向量阵列时代,这条前提在松动——一秒 GPU 时间和一秒 CPU 时间不是一种钱;KV cache 占着的显存、attention 吃掉的内存带宽,都不在这本账上。调度器只给 CPU 记账,等于只给现金记账,不管房子和股票。

原生的做法:换货币。账上记的不再是"跑了多久",而是"占了多少算力阵列、多少显存、多少带宽",再乘一个"边际收益"——多给这个任务 10ms,它能多产出多少价值?Brooks 1987 年的框架正好量这件事⁸:O(1) 到 CFS 消灭的是偶然困难(启发式、调参、攻击面);"公平用什么货币结算",是本质困难。货币不换,公平的定义就还是 2007 年的。

范式之眼再看一层:CFS 的整套记账,量的仍是"时间"——为线性指令流设计的货币。算力主体变成非线性向量阵列之后,时间不再是稀缺物的准确度量:同样一秒,Tensor Core 的矩阵乘和标量指令流,根本不是一种劳动。旧抽象与新硬件的张力,在这里露了馅。

三、绑一颗 CPU,亲眼看它记账

手边任何一台 Linux 机器,把两个忙循环绑在同一颗 CPU 上,一个 nice 0,一个 nice 5:

taskset -c 0 nice -n 0 bash -c 'while true; do :; done' & P1=$!

taskset -c 0 nice -n 5 bash -c 'while true; do :; done' & P2=$!

sleep 8

ps -o pid,ni,%cpu,comm -p $P1,$P2

grep se.vruntime /proc/$P1/sched /proc/$P2/sched

kill $P1 $P2

你会看到 %CPU 接近 75:25——权重 1024 对 335,算出来的,不用猜。更妙的是那两行 se.vruntime:两个任务实际跑的时间差了 3 倍,账上的数字却几乎相等。CFS 的公平,就是这句被跑出来的话:现实按权重分,账上永远拉平。

落到普遍场景:一台跑着本地模型的机器上,推理服务常驻、评测任务来来去去,nice 权重就是现成的定价旋钮——先给时延敏感的任务降 nice(数字变小、权重变大),再看效果;但记住,CFS 只给 CPU 记账,GPU 的显存和算力还没有 OS 级的账本。边缘算力盒上算力更金贵,值得先把各任务的 SLO 列出来,按"出价"配权重,而不是默认一人一半。CPU 的公平 2007 年就解决了,异构算力的公平,是下一个没人写的章节。

觉 然 小 注

「是法平等,无有高下。」⁹(思想化用)

公平不是人人一样多。nice 5 拿 24.7%,nice 0 拿 75.3%——把重量折成同一枚硬币之后,一视同仁。

引 用 与 出 处

  1. [1] I. Molnar 等. CFS Scheduler(Linux 内核设计文档 Documentation/scheduler/sched-design-CFS.txt;CFS 由 Ingo Molnar 实现,2007 年合入 Linux 2.6.23,取代 O(1) 成为默认调度器). 存档:https://git.gitlab.arm.com/linux-arm/linux-rr/-/blob/754e0b0e35608ed5206d6a67a791563c631cec07/Documentation/scheduler/sched-design-CFS.rst
  2. [2] "Completely Fair Scheduler". Wikipedia(存档镜像):Con Kolivas 的 Rotating Staircase Deadline 启发了 CFS 的开发,Molnar 在公告中致谢;CFS 合入 2.6.23(2007 年 10 月). https://gateway.ipfs.cybernode.ai/ipfs/QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco/wiki/Completely_Fair_Scheduler.html
  3. [3] "The Completely Fair Scheduler". blog.qsliu.dev(引 Linux 内核源码 kernel/sched/core.c sched_prio_to_weight 表:nice 0→1024、nice 5→335、nice -5→3121;相邻 nice 级权重比约 1.25;vruntime 计算公式;红黑树最左节点缓存使取任务为 O(1)). https://blog.qsliu.dev/post/the-completely-fair-scheduler/
  4. [4] R. Love. Linux Kernel Development. 3rd ed., Addison-Wesley, 2010(第 4 章"进程调度":vruntime、红黑树按 vruntime 排序、永远取最左节点)
  5. [5] "Implementation of the Linux kernel CFS scheduler". sobyte.net(min_vruntime:新进程、久睡醒来、跨 CPU 迁移三种情形下 vruntime 的起步规则). https://www.sobyte.net/post/2022-01/linux-cfs/
  6. [6] "Relearning Linux Task Scheduling 7: CFS Scheduler Internals". hitzhangjie.medium.com(引内核源码注释:sysctl_sched_latency 默认 6000000ns = 6ms;sysctl_sched_min_granularity 默认 750000ns = 0.75ms). http://hitzhangjie.medium.com/relearning-linux-task-scheduling-7-cfs-scheduler-internals-a5ba0abca436
  7. [7] 孙过庭. 《书谱》. 垂拱三年(687)撰并书("初学分布,但求平正;既知平正,务追险绝;既能险绝,复归平正"为已核实引文,此处化用)
  8. [8] F. P. Brooks. No Silver Bullet: Essence and Accidents of Software Engineering. IEEE Computer, 1987. DOI: 10.1109/MC.1987.1663532(思想化用:本质困难 vs 偶然困难)
  9. [9] 「是法平等,无有高下。」《金刚般若波罗蜜经》,鸠摩罗什译(思想化用)

· · ·

← 返回资讯中心