PHP 7.2 中 proc_open() 启动子进程后若未显式调用 proc_close() 或 wait 处理,会导致僵尸进程;需通过 proc_close、proc_get_status 配合回收,或在 CLI 中注册 SIGCHLD 信号处理函数兜底。

PHP 7.2 中用 proc_open() 启动子进程后,若子进程已退出但父进程未及时回收,就会留下僵尸进程(状态为 Z)。这不是 PHP 特有现象,而是 Unix/Linux 进程模型决定的——关键在于父进程是否调用 waitpid() 获取子进程退出状态。PHP 的 proc_open() 本身不自动回收,必须显式处理。
确认是否产生僵尸进程
运行以下命令快速筛查:
-
ps aux | awk '$8 ~ /^Z$/ {print $2, $11}'—— 查看 PID 和命令名 -
ps -eo pid,ppid,stat,comm | grep '^ *[0-9]* *[0-9]* Z '—— 精准匹配 Z 状态 - 找到僵尸进程后,用
ps -o ppid= -p [ZOMBIE_PID]查其父进程 PID(即 PHP 主进程或 FPM worker)
PHP 层主动回收:必须调用 proc\_close 或 proc\_get\_status + wait
proc_open() 返回的 resource 不会自动释放子进程资源。正确做法是:
- 无论子进程是否已退出,都应尽快调用
proc_close($process)—— 它内部会阻塞等待并调用waitpid(),完成回收 - 若需非阻塞判断,先用
$status = proc_get_status($process)检查$status['running'] === false,再立即调用proc_close($process) - 避免只调
proc_terminate()就丢弃 resource —— 这会导致僵尸残留;proc_terminate()只发信号,不回收
兜底策略:确保父进程能响应 SIGCHLD(尤其在 CLI 或常驻脚本中)
PHP 默认不处理 SIGCHLD,而 FPM/CLI 主进程若长期运行(如守护进程、队列消费者),可能收不到子进程退出通知。可手动注册:
立即学习“PHP免费学习笔记(深入)”;
- 在脚本开头添加:
pcntl_signal(SIGCHLD, function($sig) { pcntl_waitpid(-1, $status, WNOHANG); }); - 配合
pcntl_signal_dispatch()在主循环中定期分发(如每轮迭代后调用一次) - 注意:此方式仅适用于启用
pcntl扩展的 CLI 环境,FPM 下不可用(信号由 master 进程管理)
运维层面临时清理与预防
当僵尸已堆积,且无法修改代码时:
- 若 PHP 进程是短生命周期(如 Web 请求),重启 PHP-FPM 即可:
sudo systemctl restart php7.2-fpm - 若为常驻 CLI 进程,可 kill 其父进程(如 supervisor 管理的进程),让 init 接管并自动清理所有子僵尸
- 监控建议:用 cron 每分钟执行
ps aux | awk '$8 ~ /^Z$/ {count++} END{if(count>2) print "ALERT: " count " zombies"}',触发告警



















