Workerman中僵尸进程典型表现为ps显示STAT为Z、COMMAND含[php]或[workerman],且top显示zombie数上升;主因是Master进程未及时调用pcntl_waitpid回收已退出Worker子进程。

Workerman 里僵尸进程的典型表现
Workerman 本身是 PHP 进程管理框架,子进程(如 Worker 实例)退出后若父进程(通常是 Master 进程)没及时调用 waitpid(),就会留下 Z 状态进程。你不会在 ps aux | grep php 里看到明显异常,但 top 底部显示 zombie: N,或 ps aux | grep ' Z ' 能扫出一堆带 [php] 或 [workerman] 的僵尸行——它们 PID 存在、STAT 列为 Z、COMMAND 显示 <defunct></defunct>。
确认 Workerman Master 进程是否在正常回收
Workerman 的 Master 进程负责监听 SIGCHLD 并调用 pcntl_waitpid(-1, $status, WNOHANG)。常见失效场景包括:
-
Master进程被意外中断(比如被kill -9或 OOM killer 杀掉),残留子进程变成孤儿,被init(PID 1)接管;此时ps -p Z_PID -o ppid=返回1,但init只会在该子进程真正退出后才清理——而它早已退出,所以卡住 - Workerman 版本过低(如 Master 的信号处理函数未设
WNOHANG,导致一次waitpid()后阻塞,后续子进程退出无法被感知 - 业务代码中使用了
pcntl_signal()但未调用pcntl_signal_dispatch(),导致SIGCHLD积压未分发,Master不触发回收逻辑 - Worker 进程内发生致命错误(如段错误、PHP 扩展崩溃),绕过正常退出流程,
Master收不到预期的子进程终止通知
快速定位问题 Worker 进程来源
Workerman 僵尸通常集中出现在某类 Worker 实例下,比如 TextProtocol 或自定义的 MyWorker。执行以下命令链缩小范围:
ps -eo pid,ppid,stat,comm,args | awk '$3 ~ /Z/ {print $1,$2,$4,$5}' | head -n5
拿到几个僵尸 PID 后,查其父进程:
-
ps -p Z_PID -o ppid=→ 得到 PPID -
ps -p PPID -o pid,ppid,comm,args | grep -E '(workerman|php)'→ 确认是否为php start.php start -d启动的Master进程 - 若 PPID 是 1,说明
Master已死;若 PPID 是某个php进程但STAT为T(stopped)或<defunct></defunct>,说明父进程自身已异常
再检查 Workerman 日志(默认 ./Workerman.log),搜索 exit、kill、segmentation 关键词,确认是否有 Worker 非正常退出记录。
Workerman 场景下最有效的清理与预防
别碰 kill -9 僵尸进程——它无效。关键动作在父层:
- 重启整个 Workerman 服务:
php start.php restart。新Master启动后,旧僵尸若 PPID 是 1,会被init清理;若 PPID 是旧Master(已不可见),重启会强制释放所有子进程资源 - 升级 Workerman 至
v4.1.0+,该版本修复了多线程模式下SIGCHLD处理竞争问题,并默认启用WNOHANG - 在
start.php开头显式注册信号处理:加入pcntl_async_signals(true);(PHP 7.1+),并确保pcntl_signal_dispatch()在主循环中被周期调用 - 避免在 Worker 中调用
pcntl_fork()或其他可能干扰信号链的系统调用;如有必要,自行实现子进程 wait 回收逻辑,不依赖Master
真正麻烦的不是单个僵尸,而是持续产生——这往往指向某个 Worker 实例在反复崩溃又重启。盯住 ppid 相同的一组僵尸,结合日志时间戳,比对对应 Worker 的启动频率和错误模式,才能根治。

















