第76期 | 调度器眼里没有进程
第六章第十一期。前面十期,调度器算过时间、算过公平、算过电费,账本越算越细。这一期退后一步,问一个更根本的问题:排队的东西,到底是谁?答案有点出人意料——调度器眼里,根本没有"进程"这个东西。
一、调度器眼里没有进程
内核只认 task_struct。ps 里看到的一行"进程",到了内核,是一份 task_struct:一段可执行流的全部档案。开一个进程,得一份;开一个线程,也得一份——区别只在共享多少东西:线程共享同一份内存、同一批打开的文件、同一套信号处理,进程则各过各的。
调度器真正排队、真正算账的,是嵌在 task_struct 里的一小捆调度状态,叫 sched_entity,vruntime 就记在里面¹。组调度再往上包一层:先按组分账,再在组内按线程分账——第 74 期 cgroup 的那本账,最终还是落到一个个线程头上²。
这就解释了一个日常错觉。一个程序开一百个线程,和一百个单线程程序,在调度器眼里是同一回事:排队处站着一百个可排实体。线程池为什么要限流,原因就在这里——线程不是免费的占位符,每一个都要在调度器的账本上占一行。反过来也一样:一个"进程"跑得慢,未必是进程的问题,可能是它手下某一个线程一直在排队。
二、Agent 先排意图,不排进程
如果 AI 的操作系统先出现,它的排队单位会再换一次。Agent 派出的二十个子任务、一次工具调用、一段正在生成的回答,都是可排实体;它们共享的不再是内存页,而是同一个目标和同一份上下文。调度器排的不再是"谁先上 CPU",而是"哪一份意图先拿到算力"。
预算的单位跟着换。时间片之外,多出三本账:token 预算、工具调用次数、截止期。一个意图进场时就该声明:这份活值多少 token、等多久会过期、失败了能不能重排。没有这三本账,调度器只能把二十个子任务当二十个互不相干的排队者——一个用户,不声不响占了二十个号。
Brooks 在 1987 年那篇泼冷水的文章里,把困难分成两类:本质困难与偶然困难³。调度单位这件事,恰好卡在分界线上:把"进程"当成调度单位,是旧硬件留下的偶然形状;真正本质的,是"一份工作需要多少算力、值多少代价"。换单位,不是换名词,是把账算到本质那一侧。
范式之眼再看一层:旧抽象按指令流切时间,新硬件按向量阵列的占用算账。调度单位从"执行流"变成"算力占用的一次申请",线程只是这种申请在旧世界里的形状。
三、数一数,一台机器几个活
今晚就能试的一步。手边任何一台 Linux 机器,先数两行:
ps -e | wc -l
ps -eLf | wc -l
第一行是进程数,第二行是线程数。后者通常是前者的好几倍——多出来的,就是调度器眼里真正的排队人数。再挑一个你正在用的程序,看它手下有几个线程:
ls /proc/<进程号>/task
cat /proc/<进程号>/status | grep Threads
每个 task 目录,就是一个排队者。找一个线程数最多的程序,再想想:它开这么多线程,每一个都在干什么?有没有只占号不干活的?
落到今天的机器上,同一把尺子直接能套。一台跑本地模型的机器上,推理线程、分词线程、I/O 线程各占一个号,卡顿的时候先数线程,再谈优化;边缘小盒子上,控制环的线程和推理线程抢同一条队,优先级要按线程设,taskset -pc <核号> <线程号> 能把关键线程钉在指定核上;视频 AI 平台里一路视频的推理任务再多,也只花这一路的预算;企业私有智能中心里一个 agent 的二十个子任务,先在意图这一层分好账,再往线程层落。先数清排队者,再谈公平。
觉 然 小 注
孙过庭《书谱》里讲点画与形质:"真以点画为形质,使转为情性。"⁴(思想化用)进程是字,线程是点画。调度器不读字,只数点画——公平的第一步,是先看清排队的东西到底是什么。
引 用 与 出 处
- [1] Linux 内核调度文档. Load tracking in the scheduler. LWN. https://lwn.net/Articles/639543/
- [2] Linux 手册页. sched(7):nice 值与组调度. https://manpages.debian.org/unstable/manpages/sched.7.en.html
- [3] Frederick P. Brooks Jr. No Silver Bullet — Essence and Accidents of Software Engineering. IEEE Computer, 1987. https://doi.org/10.1109/MC.1987.1663532
- [4] 孙过庭. 《书谱》. 垂拱三年(687)撰并书(思想化用)
· · ·