PHP文生图脚本在CLI下易因SIGHUP等信号中断,根本原因是Linux进程会话管理机制;nohup+&可快速规避终端退出问题,但需注意路径、日志、内存控制;Supervisor才是生产环境实用守护方案。

PHP 文生图任务(比如调用 Stable Diffusion API、调用本地 Python subprocess 生成图像、或使用 GD/Imagick 处理大量图片)一旦在 CLI 下直接运行,很容易因 SSH 断开、超时、信号中断或未捕获异常而中途退出——这不是“生成中断”,而是进程被杀掉了。守护化不是万能解药,但能解决“非代码逻辑失败”导致的意外退出。
为什么 PHP 文生图脚本一断网就停?
根本原因不是 PHP 或图像库的问题,而是 Linux 进程生命周期管理机制在起作用:
- 直接执行
php generate.php:进程挂在当前终端 session 下,SSH 断开 → 触发SIGHUP→ 默认终止进程 - 用
php generate.php &:只是转入后台 job,仍属当前 session,终端退出照样收SIGHUP - 即使加了
ignore_user_abort(true)或set_time_limit(0):这些只对 Web SAPI 有效,CLI 模式下完全不生效
文生图常需几秒到几分钟,中间任何一次终端挂起、网络抖动、用户误关窗口,都会让整个生成链路断掉——你看到的“中断”,其实是进程消失了。
nohup + & 是最快上线方案,但有硬伤
适合临时调试、单次批量任务,命令形如:
立即学习“PHP免费学习笔记(深入)”;
nohup php /path/to/generate.php > /var/log/imggen.log 2>&1 & echo $! > /var/run/imggen.pid
关键点:
-
nohup拦截SIGHUP,避免终端退出杀进程 -
> ... 2>&1把 stdout/stderr 合并重定向,否则日志会丢 -
echo $! > ...保存 PID,方便后续kill $(cat /var/run/imggen.pid) - 不要漏掉最后的
&,否则nohup仍前台阻塞
⚠️ 容易踩的坑:
- 日志文件路径不存在 → 写入失败,
nohup.out默认生成在当前目录,容易被清理 - 脚本里用了相对路径(如
require 'vendor/autoload.php')→ 守护后工作目录是启动时的路径,不是脚本所在目录 - 没做内存/超时控制 → 图像批量生成可能 OOM,进程被系统
OOM Killer杀掉,nohup也救不了
pcntl_fork + posix_setsid 实现真守护,但别裸写
手动 fork 两次、posix_setsid()、chdir('/')*、umask(0)、关闭 STDIN/STDOUT/STDERR……这些步骤没错,但生产环境不建议自己拼:
- PHP 8.4+ 已废弃部分
pcntl信号行为,pcntl_signal_dispatch()在某些 SAPI 下不可靠 - 没处理子进程崩溃后的自动拉起 → 一次 GD 内存溢出,整个服务就静默死亡
- 缺少进程健康检查(比如检测子进程是否卡死在
imagick->writeImage())
如果你坚持手写,至少补上这三件事:
- 用
pcntl_signal(SIGTERM, ...)和pcntl_signal_dispatch()响应优雅退出,确保图像写完再 exit - 主循环内加
pcntl_waitpid(-1, $status, WNOHANG)防止僵尸子进程(尤其调用exec('python gen.py')时) - 所有路径用
<strong>DIR</strong>或dirname(<strong>FILE</strong>)显式指定,别信getcwd()
Supervisor 才是文生图守护的实用底线
它不改 PHP 代码,不碰 fork,只做一件事:盯住你的进程,死了就拉起来,还能管日志轮转、资源限制、启动顺序。配置示例:
[program:img-generator] command=php /var/www/app/generate.php directory=/var/www/app user=www-data autostart=true autorestart=true startretries=3 stopwaitsecs=30 redirect_stderr=true stdout_logfile=/var/log/supervisor/imggen.log stdout_logfile_maxbytes=10MB environment=PATH="/usr/local/bin:/usr/bin:/bin"
重点说明:
-
stopwaitsecs=30给 PHP 留足时间完成最后一张图(比如你在register_shutdown_function()里 flush buffer) -
environment补全 PATH,避免调用convert或python时找不到命令 -
stdout_logfile_maxbytes防止日志撑爆磁盘(文生图 debug 输出往往很多) - 不要用
root启动 → Imagick 读写文件权限、临时目录安全都成问题
Supervisor 本身不解决“生成卡死”,但它配合脚本里的超时控制(如 stream_set_timeout($fp, 60)、proc_open 的 timeout 参数)就能覆盖绝大多数中断场景。
真正难搞的从来不是“怎么让它一直跑”,而是“怎么确认它没假死、没卡在系统调用里、没悄悄漏掉某几张图”。守护只是第一层防线,日志埋点 + 进度落库 + 定期 health check 才是闭环。



















