第58期 | 十三个字符,炸掉一台机器

操作系统 · · 九羲科技

一、十三个字符,炸掉一台机器

1991 年的 Linux,进程表只有 64 个槽位。0.01 的 include/linux/sched.h 里,NR_TASKS=64¹——整个系统的进程,就是一个 task[64] 数组。那时候要是有人敲下这一行:

:(){ :|:& };:

13 个字符,64 个名额眨眼抢光,机器直接僵死。

拆开看,它残忍得优雅:定义一个叫 `:` 的函数,函数体是"调用自己,把输出用管道递给另一个自己,扔后台跑";末尾那个 `:` 点燃引线。一次调用变两次,两次变四次,指数分裂,停不下来。

反常识的是,它不占 CPU,也不吃内存——管道两端的进程大多在空等。它耗的是"名额":进程表槽位、PID 号段、每个进程那份 task_struct。杀死系统的不是干活,是记账。今天的 PID 上限,现代发行版上通常是 4194304(systemd 把内核默认的 32768 调大),名额比 1991 年多了六万倍,可"名额"这个概念本身,三十多年没变过。

Unix 给了 fork——一次调用,两次返回,Ritchie 与 Thompson 在 1974 年那篇论文里确立的模型²——却花了二十多年,才给"创造"装上刹车。早年间真没有防线:进程表满了,fork 直接失败,系统僵在原地,只能硬重启。笼子是一层层后加上去的:ulimit -u,按用户限进程数,超了 fork 返回 EAGAIN;2015 年合入内核 4.3 的 cgroup pids 控制器,pids.max,超了同样 EAGAIN⁴;再往上,systemd 给每个服务配 TasksMax=,底层走的还是 pids.max。

维纳在《控制论》里讲反馈回路³。fork 炸弹是最干净的反例:一条自我复制的正反馈,环路里没有任何"够了"的信号。负反馈缺席,指数增长就替系统做了决定。

二、AI OS 的 fork 炸弹

原生 AI 系统里没有进程,但有"意图"。agent 派生子 agent 去处理子任务,子 agent 再派生——这就是它的 fork。AutoGPT 时代大家已经见过雏形:agent 循环调用工具停不下来,只不过当时烧的是 API 账单,还没烧到系统层。

如果 AI 的操作系统先出现,"派生"会是一等公民操作,而"节制"会是它的孪生兄弟:每个意图自带"派生预算",子意图继承递减,额度归零就不能再派生;整棵派生树全局可见——谁派生了谁,一目了然;熔断器盯着派生速率与深度,异常就冻结整棵子树,根因可审计。

对照 Unix 的三层笼子,刚好对上三层:ulimit -u 管"按主体限额",对应意图预算;pids.max 管"按任务树限额",对应子树熔断;TasksMax 管"按会话限额",对应会话配额。变的是名词,不变的是那句老话:系统必须先数得清,才有资格说"够了"。

往深看一层:fork 炸弹耗的从来不是算力,是记账能力。AI 时代也一样——真正稀缺的不是算力,是"注意力与记账":谁派生了谁、花了多少、什么时候停。派生的人不买单,买单的永远是系统。

三、进笼子,看一次爆炸

手边任何一台 Linux 机器都能试,但千万别在裸 shell 里试。下面这套是关进笼子看爆炸:

systemd-run --scope -p TasksMax=60 bash

进去之后,敲下那 13 个字符。满屏会刷出 `fork: retry: Resource temporarily unavailable`——这就是 pids.max 在静默拒绝。再开一个终端,`systemd-cgls` 看这棵 cgroup 的进程数:顶在 60 上不去。想看计数器,读对应 cgroup 目录下的 pids.events,max 那一行在涨。dmesg 里干干净净——cgroup 动手不见血,不靠崩溃,靠记账。

退出笼子(exit),scope 结束,笼子里残留的进程会被一起收走,不留尾巴。

为什么不用 ulimit -u?它是按用户全局算的,调低会影响同一用户的其他进程。scope 笼子只关这一间房,炸完了推门就走。

映射到普遍场景:今天做多智能体编排的团队,agent 派生子 agent 就是现实版的 fork。动手建议:给每棵任务树设派生预算与深度上限,超限熔断并记审计日志。原理(预算与熔断)都是成熟的,缺的是把它做进编排层的那一层工程。

觉 然 小 注

fork 是 Unix 最慷慨的礼物,一次调用,两次返回。可慷慨若没有边界,就是灾难。操作系统花了二十多年才学会对"创造"说不——先有生,才有节。这是所有复杂系统的成人礼。

引 用 与 出 处

  1. [1] Linux 0.01 源码历史归档. include/linux/sched.h(NR_TASKS=64,task 数组即进程表), 1991. https://mirrors.edge.kernel.org/pub/linux/kernel/Historic/
  2. [2] Ritchie, D. M.; Thompson, K. The UNIX Time-Sharing System. Communications of the ACM, 1974. https://doi.org/10.1145/361011.361061
  3. [3] Wiener, N. Cybernetics: Or Control and Communication in the Animal and the Machine. MIT Press, 1948.(思想化用:失去负反馈的正反馈回路)
  4. [4] cgroups(7). pids 控制器(自 Linux 4.3 起,fork 超限返回 EAGAIN). https://www.mankier.com/7/cgroups

· · ·

← 返回资讯中心