Watch at Ease看见世界,读懂生活。

科技 · 实用指南

Linux 出现 defunct 僵尸进程?动手前先证明父进程与 wait 边界

僵尸进程已经结束;真正负责清理的是仍存活的父进程、subreaper 或容器 PID 1。先绑定子进程与父进程身份,再只修复一个安全的回收边界。

原创图示:子进程退出成为僵尸状态,直到父进程调用 wait 回收。
原创示意图:诊断对象是存活父进程,而不是已经结束的子进程。 Watch at Ease

僵尸进程不是还能再次被终止的运行任务,而是已结束子进程留下的状态记录,等待父进程或回收者读取。因此安全排查应从父进程身份开始,而不是从更强信号开始。

固定子进程 PID 与启动时间、父进程 PID 与启动时间、服务或容器边界以及 Z 状态数量趋势。然后只修复真正负责 wait 的进程,并通过原启动路径证明回收数量受控。

1. 重启父进程前先冻结记录

保存完整 ps 输出、子 PID、父 PID、命令名、用户、容器或 unit、首次观察时间和 Z 状态数量。只要路径仍存在,就读取 /proc/CHILD/stat 与 /proc/PARENT/stat,并保存各自启动时间。

不要先对 CHILD 执行 kill -9,也不要批量终止所有父进程。子进程已经结束,批量命令却可能中断无关服务。

2. 证明仍存活的父进程与 wait 边界

在同一命名空间视图确认状态 Z 与 PPID,再把父进程映射到可执行文件、启动时间、服务、supervisor 与部署。僵尸记录存在,是因为负责的父进程或 subreaper 尚未调用 wait 系列。

检查父进程是否健康、卡住、过载、配置错误,或只是处于短暂回收间隔。稳定或上升的数量才支持持续缺陷判断。

3. 纳入 PID 命名空间与 subreaper

容器中的 PID 1 可能是应用或小型 init,并不等于宿主机 init。记录容器内进程树与宿主侧容器身份;Docker 文档说明 --init 可提供负责回收的 init。

不要假定添加 init 就能修复应用代码。它能回收被收养的后代,但长期父进程仍必须正确回收自己的子进程。

4. 选择对父进程影响最小的修复

如果程序由你维护,应修复完整子进程生命周期:回收每个已结束子进程,处理突发和错误路径,并用相同并发与失败时序测试。

第三方软件优先使用有文档的服务重启或升级。只有在明确依赖、回滚与重启路径后,才考虑结束父进程。

5. 通过原启动器验证有界回收

用相同 systemd unit、容器命令、supervisor 或 shell wrapper 运行原工作负载,按固定间隔采样父进程身份、Z 数量与子进程完成数。

仅当父进程健康、结束子进程被回收、僵尸数量回到有界基线且无关进程未受影响时,才接受修复。

原创图示:容器 PID 1 接管并回收孤儿子进程。
原创示意图:主机 PID 1 与容器 PID 1 是不同的运行边界。 Watch at Ease