第57期 | 死了却走不了:僵尸进程
一、死了,却走不了:Unix 的丧葬礼仪
Unix 1974 年那篇论文里,fork、exec、wait、exit 四个原语是一家人¹。wait 是干什么的?收尸。
进程死了,内核并不立刻把它擦掉。Linux 0.01 的 do_exit(kernel/exit.c 第 64 行)²:先释放页表、关掉文件,然后——只要它还有爹,就把自己的状态设成 TASK_ZOMBIE,再给爹发一封 SIGCHLD:"你儿子死了,来认领。"退出码就写在 task_struct 里,等爹调用 waitpid 来取。取走了,内核才真正 release,格子腾出来。
僵尸进程是最干净的鬼:不占内存、不跑代码,只占进程表里的一个格子,ps 里 STAT 列一个 Z,名字后面跟着 <defunct>。代码走了,影子还钉在墙上,等你来看最后一眼。
更妙的是 0.01 的粗糙:do_exit 里还有一句——爹先死的孩子,father 直接置 0。下次轮到自己死,连僵尸都当不成,直接 release。没有收养,没有过继。1991 年的善后工程,就这么糙。这恰是"发展性研究"的注脚:原理(Unix 1974 的 wait)早熟了,工程(孤儿归谁)是后来一层层补上的。
二、AI 的孤儿院:谁来收智能体的尸体
如果 AI 的操作系统先出现,"僵尸"这个概念会被设计成什么样?
Linux 的僵尸是"退出码没人取",AI 时代的僵尸是"结果没人认领"。一个智能体跑完一轮任务,它的"退出码"不在寄存器里,在它留下的轨迹里:中间推理、工具调用记录、没写完的草稿。调度器只管生死(kill、restart),没人管"它死前想说什么"。
原生 AI OS 里该有一座"孤儿院":所有结束的智能体任务,输出先被收养——结果摘要、退出原因、资源账单,进一个统一的结果收养站。调用方可以晚一点来取,也可以永远不来,但系统永远知道"它是怎么死的"。这不是日志,这是把丧葬礼仪制度化:死亡是信息,不是垃圾。
更进一步:Linux 的僵尸是被动的(等爹来取),AI OS 里的可以是主动的——一个常驻的"收殓者"智能体,专职读死亡现场:这次失败和上次失败像不像?退出码里有没有可复用的经验?Unix 把死亡外包给父进程,AI OS 该把死亡变成学习。
init 收养孤儿,不问来处、不问去处,像极了那句"应无所住而生其心"⁴。而范式之眼也在这里:task_struct 是线性时代的"存在证明",一个结构体钉住一个进程;AI 时代智能体的存在是分布式的——权重、上下文、轨迹。收养的单位变了,从"一个 struct"变成"一段可检索的经验"。
三、今晚就数一遍僵尸:defunct 长什么样
别光看,亲手造一个僵尸出来,再亲手超度它。
先找:ps -eo pid,ppid,stat,comm | awk '$3 ~ /Z/'——STAT 列带 Z 的就是,名字后面常跟着 <defunct>。服务器上偶尔能捡到一两个,多半是某个监控脚本的爹没写 wait。
再造一个(C,十行):fork 之后,子进程直接 _exit(0),父进程 sleep(60),就是不调用 wait。另开一个终端 ps 一看:子进程赫然在列,STAT=Z。这就是"死了却走不了"——它在等你认领。60 秒后父进程退出,PID 1(现在多半是 systemd)把它收养、收尸,格子腾空。整个过程,就是 Unix 丧葬礼仪的现场直播。
还有个经典手法:daemon 的双 fork³。第一次 fork 后父进程退出,子进程被 init 收养;第二次 fork 让孙子进程脱离会话、永远拿不到控制终端——中间那个"一次性父亲"体面退场,不留僵尸。
想偷懒?把 SIGCHLD 设成忽略,现代 Linux 会替你自动收尸,子进程直接消失,连僵尸都当不成——这是后来补上的体贴,0.01 年代没有。
映射一下:手边跑本地模型的机器、常年在线的边缘设备上跑的推理服务,凡是"起子进程干活"的地方,爹都得记得 wait。训练脚本里堆一堆 defunct 不致命,但进程表格子是有限的(0.01 时代 NR_TASKS 才 64 个),积多了,新进程就生不出来——死人不下葬,活人没床位。
觉 然 小 注
僵尸教的不是死亡,是善始善终。系统如此,人亦如此:事情做完,记得收个尾,把退出码交代清楚。
引 用 与 出 处
- [1] Ritchie, D. M., & Thompson, K. The UNIX Time-Sharing System. Communications of the ACM, 1974. https://doi.org/10.1145/361011.361061
- [2] Linux 0.01 源码,kernel/exit.c(do_exit 第 64 行、TASK_ZOMBIE 第 85 行、sys_waitpid 第 99 行)。kernel.org 历史存档. https://www.kernel.org/pub/linux/kernel/Historic/linux-0.01.tar.gz
- [3] Stevens, W. R. Advanced Programming in the UNIX Environment. Addison-Wesley, 1992.(daemon 双 fork 脱离终端的经典做法)
- [4] 鸠摩罗什 译. 《金刚般若波罗蜜经》第四品"妙行无住分". 后秦弘始年间(约402年)译本. ("应无所住而生其心",思想化用)
· · ·