php多进程编程核心在于子进程必须exit、父进程须用pcntl_waitpid或sigchld及时回收,否则将产生僵尸进程;ipc应选用proc_open管道或unix域套接字,禁用文件轮询与共享内存。

PHP 多进程不是靠 fork 一写就完事的,没做进程回收和通信设计,跑几天就会卡死或产生大量僵尸进程。核心在于:子进程必须 exit、父进程必须及时 pcntl_waitpid 或用 SIGCHLD 捕获、IPC 不能靠全局变量或文件轮询。
pcntl_fork 后不 exit 子进程会怎样
子进程执行完逻辑后若没调用 exit 或 die,它会继续向下执行父进程的后续代码——这会导致子进程重复 fork、重复 wait、甚至递归创建不可控的子树。常见现象是:进程数指数级增长、ps aux | grep php 看到几十个同名进程、系统负载飙升。
- 必须在子进程分支末尾加
exit(哪怕只是exit(0)) - 不要在子进程中再调用
pcntl_fork,除非你明确要“进程树”,且已做好深度控制 - CLI 脚本里用
register_shutdown_function补漏不靠谱,exit是唯一确定退出路径
pcntl_waitpid 非阻塞回收子进程
用 pcntl_wait 会卡住父进程,无法并发管理多个子任务;而 pcntl_waitpid(-1, $status, WNOHANG) 可以轮询检查是否有子进程退出,适合循环派发任务的场景。
-
WNOHANG是关键参数,不加就变阻塞调用 -
$pid = pcntl_waitpid(-1, $status, WNOHANG)返回 0 表示“有子进程在跑但还没退”,返回 >0 是退出的子进程 PID,返回 -1 是出错 - 别在 while(true) 里无休止调用,加
usleep(10000)避免空转吃 CPU - 回收后建议用
pcntl_wifexited($status)和pcntl_wexitstatus($status)判断是否正常退出及退出码
PHP 进程间通信该用什么方式
PHP 原生 IPC 支持有限,别碰 shmop(易冲突)、少用 sysvmsg(扩展需手动启用且跨平台差),推荐组合:proc_open + 管道用于简单父子通信,socket(AF_UNIX)用于多进程稳定通信。
- 父子进程间传少量数据:用
proc_open的 stdin/stdout 管道,比pcntl_fork+ 共享内存更安全 - 多个 worker 进程协同工作:建一个
unix:///tmp/myapp.sock,用stream_socket_server启 server,worker 用stream_socket_client连,走 JSON 协议 - 绝对不要用文件轮询(如反复
fopen("status.txt"))——竞态条件高、I/O 开销大、无法实时感知 - 信号(
SIGUSR1)只适合发简单指令(如“重载配置”),不适合传数据
为什么 SIGCHLD 处理器常失效
写了 pcntl_signal(SIGCHLD, 'sig_handler') 却没触发?大概率是忘了 declare(ticks = 1)。PHP 的信号不是中断式实时投递,而是“在每个 tick 边界检查是否收到信号”,没有 ticks,信号就一直挂起。
- 必须在信号注册前加
declare(ticks = 1),且作用域覆盖整个主流程 -
pcntl_signal_dispatch()可手动触发一次检查,但不能替代 ticks -
SIGCHLD处理器里别做耗时操作(如写日志、连 DB),只做pcntl_waitpid回收,否则可能丢信号 - Linux 下
SIGCHLD默认被忽略,PHP 的pcntl_signal会自动解除忽略,但其他信号(如SIGUSR2)需要显式设置
真正难的不是 fork 出去,而是让所有子进程在任意时刻崩溃、被 kill、或异常退出时,父进程都能干净回收、不残留、不卡死、不丢状态——这需要把 pcntl_waitpid、SIGCHLD、exit 三者绑死在同一个逻辑闭环里,缺一不可。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











