php cli脚本真正脱离终端需分场景:nohup可解决断网退出问题,但须配合循环逻辑、set_time_limit(0)、正确重定向;标准守护进程需pcntl_fork+setsid五步剥离,并安全管理pid与信号;生产环境应优先使用supervisor或systemd。

PHP CLI 脚本怎么真正脱离终端运行
nohup 不是守护进程,但它能解决 90% 的“断网就挂”问题。关键在于:它让进程忽略 SIGHUP 信号,配合 & 放入后台,不依赖 shell 生命周期。但仅靠它不够——脚本自己得能“活下来”:
- 必须有循环逻辑(比如
while (true) { ... sleep(5); }),否则执行完就退出,nohup也救不了 -
set_time_limit(0)要显式调用,CLI 模式默认超时 30 秒,超时后 PHP 自行终止进程 -
ignore_user_abort(true)在 CLI 下无效,别写,纯属干扰 - 重定向路径必须可写,且磁盘不能满;否则
nohup php script.php > log.txt 2>&1 &看似成功,实际日志全丢、错误无声湮灭
用 pcntl_fork + posix_setsid 写标准守护进程要注意什么
标准守护进程要完成五步剥离:fork → setsid → fork → 关闭 stdio → chdir + umask。最容易翻车的是前两次 fork 的顺序和判断:- 第一次
pcntl_fork()后,父进程必须exit(),不能return或继续执行;否则子进程不是“孤儿”,后续posix_setsid()会失败并返回 -1 - 第二次
pcntl_fork()是为了确保最终进程**绝不可能成为会话首进程**,从而彻底杜绝获取控制终端的可能;跳过这步,某些内核或终端环境下仍可能意外绑定 TTY -
fclose(STDIN)、fclose(STDOUT)、fclose(STDERR)必须在posix_setsid()之后做;提前关闭会导致setsid()调用失败(因文件描述符 0/1/2 被占用) - 别漏掉
umask(0):否则守护进程创建的日志、临时文件权限受父进程影响,可能写失败
如何安全地管理 PID 文件和响应信号
PID 文件不是摆设,它是你唯一可控的“进程锚点”。但手写容易出错:- 写 PID 要在二次 fork 完成、stdio 关闭**之后**,用
file_put_contents('/var/run/myapp.pid', getmypid(), LOCK_EX),加锁防竞态 - 进程退出前必须
unlink('/var/run/myapp.pid'),否则下次启动时误判“已在运行” - 信号处理要用
pcntl_async_signals(true)开启异步模式,否则pcntl_signal()注册的回调可能延迟甚至丢失 - 只监听
SIGTERM和SIGHUP就够用:SIGTERM做优雅退出(清理资源、刷日志),SIGHUP可用于重载配置,别碰SIGKILL(无法捕获) - 信号回调里避免调用非异步信号安全函数(如
echo、file_put_contents),改用pcntl_signal_dispatch()配合主循环轮询处理
什么时候该放弃手写,换 Supervisor 或 systemd
手写守护进程适合学习原理或极简场景;一旦涉及生产环境,三个硬伤立刻暴露:- 无自动拉起:进程崩溃后不会重启,得靠外部轮询(比如用 shell 脚本
if [ ! -d /proc/$(cat pid) ]; then ...),可靠性低、轮询间隔难平衡 - 无资源隔离:无法限制内存/CPU/文件句柄数,一个失控脚本可能拖垮整台机器
- 无统一日志与状态接口:查日志要翻文件,看状态要
ps+grep,无法集成到监控系统
nohup 不管,pcntl 也不管——得靠外围机制补上。php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











