zombie 进程回收失败的 init pid=1 信号处理 bug 排查

16次阅读

init(PID=1)收不掉僵尸进程,因其可能未安装 SIGCHLD 处理函数,或虽安装但未循环调用 waitpid(-1, &status, WNOHANG) 回收所有退出子进程,尤其在 busybox 旧版本或自定义 init 中常见。

zombie 进程回收失败的 init pid=1 信号处理 bug 排查

为什么 init(PID=1)收不掉僵尸进程

Linux 中,子进程退出后若父进程未调用 wait()waitpid() 获取其退出状态,该子进程会变成僵尸进程(Z 状态)。正常情况下,当父进程退出,内核会将孤儿进程的父进程设为 init(PID=1),由它负责回收——但前提是 init 正确响应 SIGCHLD 并调用 wait 类系统调用。

问题在于:如果 init 进程本身没有安装 SIGCHLD 信号处理函数,或安装了但未在 handler 中调用 waitpid(-1, &status, WNOHANG) 循环收割,就会漏掉部分僵尸进程。尤其在使用自定义 init(如 busybox initsystemd 替代品、或容器中精简 init)时,这个逻辑极易出错。

busybox init 的 SIGCHLD 处理缺陷

老版本 busybox(如 1.30.x 之前)的 init 默认不注册 SIGCHLD handler;即使注册了,也只调用一次 waitpid(),无法处理并发退出的多个子进程——导致仅回收第一个,其余滞留为僵尸。

  • 现象:ps aux | grep 'Z' 持续出现,且父 PID 均为 1
  • 验证:运行 strace -p 1 -e trace=signal,waitpid,观察是否收到 SIGCHLD、是否调用 waitpid,以及返回值是否为 -1ECHILD)或正数(成功回收一个)
  • 修复方式取决于 busybox 版本:
    – ≥1.31.0:默认启用循环 wait,需确认编译时开启 CONFIG_FEATURE_INIT_SCTTYCONFIG_FEATURE_INIT_SYSLOG 不影响主逻辑
    – <1.31.0:必须打补丁或升级,无安全 workaround

systemd 作为 init 时的 ReapZombie 行为

systemd 理论上会自动收割僵尸,但它依赖 notify 机制和 cgroup v1/v2 的进程生命周期跟踪。若 systemd 启动参数中禁用了子进程监控(如 systemd.legacy_systemd_cgroup_controller=0),或容器环境未正确挂载 cgroupfs,可能导致僵尸“不可见”于 systemd 的回收路径。

  • 检查点:cat /proc/1/cmdline | tr '' ' ' 确认是 systemd;再查 systemctl show --property=ReapZombie(应为 yes
  • 关键限制:systemd 只对它“知道”的子进程(即通过 fork()+exec 且未脱离 cgroup 的进程)负责;用 clone() 创建的线程、或手动 setpgid(0,0) 脱离会话的进程,可能逃逸回收
  • 临时缓解:echo 1 > /proc/sys/kernel/child_subreaper 可设当前 shell 为 subreaper,但仅对后续 fork 生效,不能清理已有僵尸

如何定位是 init 信号处理 bug 而非应用层泄漏

先排除用户进程自身未 wait 的情况,再聚焦 PID=1。核心判断依据是:僵尸的 PPID 确实为 1,且持续存在超过数秒。

  • 确认僵尸归属:ps -o pid,ppid,stat,comm -C '[""]'| awk'$3 ~ /Z/ && $2 == 1'
  • 检查 init 是否在收:grep -i 'sigchld|wait' /proc/1/status(看 SigBlk/SigCgt 字段);更直接的是 cat /proc/1/status | grep -E '^(SigQ|SigPnd)',若 SigQ 中有未决 SIGCHLD,说明信号被阻塞或未处理
  • 不要依赖 kill -s SIGCHLD 1 测试——POSIX 规定 PID=1 忽略所有未显式捕获的信号,包括 SIGCHLD,除非它真的装了 handler

真正棘手的不是 init 收不掉,而是 init 根本没机会收到信号:比如内核调度延迟、cgroup 事件丢失、或 init 自身卡在不可中断睡眠(D 状态)。这时候得看 /proc/1/stack 和 dmesg 中是否有相关警告。

text=ZqhQzanResources