swoole\process子进程需接入主服务生命周期管理,否则易成孤儿或僵尸进程;推荐用$server->addprocess()由swoole自动处理sigchld、重启与优雅退出,手动start()则需自行实现wait、signal、重试等逻辑。

直接用 Swoole\Process 创建的子进程,如果不接入主服务生命周期,很容易变成孤儿进程或僵尸进程——这不是配置问题,是管理缺失。
new Swoole\Process() 的三个参数怎么传才不踩坑
构造函数签名是 new Swoole\Process($function, $redirect_stdin_stdout = false, $pipe_type = 2),关键在第三个参数:
-
$pipe_type = 0:不创建管道,适合纯后台守护任务(如日志轮转),但父子间无法通信 -
$pipe_type = 1:创建 SOCK_DGRAM 类型管道(默认),半双工、同步阻塞,read()没数据就卡住 -
$pipe_type = 2:创建 SOCK_STREAM 类型管道,全双工、支持多次读写,推荐用于需要双向通信的场景 - 别传字符串或 null,会静默失败;
false会被转成0,不是“关闭管道”,而是“不创建”
为什么子进程退出后 ps 还能看到僵尸进程
父进程没调用 pcntl_wait() 或没监听 SIGCHLD 信号,系统就无法回收子进程资源。Swoole 提供了两种解法:
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
- 在主进程中调用
Swoole\Process::wait(),但它会阻塞——只适合单个子进程且主逻辑可暂停的场景 - 更常用的是注册信号处理器:
pcntl_signal(SIGCHLD, function() { while (pcntl_waitpid(-1, $status, WNOHANG) > 0); }),配合pcntl_signal_dispatch()在事件循环中触发 - 如果用的是
Swoole\Http\Server或Swoole\WebSocket\Server,直接用$server->addProcess($process),Swoole 内部自动处理 SIGCHLD 和重启逻辑
addProcess() 和独立 start() 的根本区别
核心不在“能不能跑”,而在“谁负责善后”:
- 手动
$process->start()后,父进程需自行维护生命周期:监听退出、捕获异常、重试启动、设置进程名($process->name('my_worker'))便于调试 - 调用
$server->addProcess($process)后,Swoole 主循环接管该进程:异常退出自动拉起、收到kill -15时协同优雅退出、可通过swoole_server->stats()查到进程状态 - 注意:只有 Server 实例(如
Swoole\Http\Server)才有addProcess()方法;纯命令行脚本想获得同等管理能力,得自己封装 wait + signal + restart 逻辑
子进程里不能干的事,比能干的更关键
看似自由的子进程,实际受 Swoole 运行模型严格约束:
- 别在子进程中再调
new Swoole\Http\Server()或go()启协程——容易引发 fork bomb 或资源竞争 - 数据库连接、Redis 客户端等资源必须在子进程内重新初始化,不能复用父进程实例(内存不共享)
-
sleep()可以用,但usleep(100000)比co::sleep(0.1)更安全——后者依赖协程调度器,而子进程默认没启用协程环境 - 若需长期运行,务必在循环里加
if (Swoole\Process::kill($pid, 0) === false) break;检查父进程是否还活着,避免父死子独存
真正难的不是创建进程,是让每个进程知道自己什么时候该停、怎么停、停完谁来收尸。Swoole 的 addProcess() 把这事包圆了,但前提是别绕过它自己手写 pcntl_fork() 那套逻辑。










