nohup 之后进程还是死了:GUI 子进程的存活陷阱

有个脚本要跑一个会开窗口的桌面程序。我按常规做法启动:

bash 复制代码
nohup ./cli.cmd publish-article ... >/dev/null 2>&1 &
disown
echo "launched"

命令立刻返回,PID 打印出来了。过一分钟去看输出文件------文件压根没生成。

同一条命令,换个方式跑就好了。这篇记录中间的差别在哪,以及我确证了什么、没确证什么。

先排除一个常见误判

nohup 和 disown 各自只解决一个很窄的问题:

  • nohup 让进程忽略 SIGHUP(终端挂断信号)。它的名字就写着自己能干什么,不多干。
  • disown 把作业从当前 shell 的作业表里移除,这样 shell 退出时不会给它发信号。

两者都不 阻止 SIGTERM。而清理动作发的通常是 SIGTERM。所以「加了 nohup 和 disown 就该活着」这个预期本身是错的------它们防的是挂断,不是终止。

我观察到的现象

同一条命令,两种跑法,结果稳定可复现:

跑法 父进程是否等待 结果
nohup ... & + disown,命令立即返回 否 输出文件从未生成
放进带轮询循环的后台任务,父进程等到结束 是 正常跑完,输出完整

差别只有一条:父进程是不是活着等到了子进程结束。

补充几个观察到的细节:

  • 被杀时没有任何错误输出,表现为「命令成功返回了,但什么都没发生」。
  • 目标程序是会开浏览器窗口的 GUI 程序。纯命令行的子进程在同环境下没有这个问题。
  • 单篇任务稳定耗时约 40 秒,父进程只要撑过这 40 秒就没事。

我没有确证的部分

坦白说,具体是谁发的 SIGTERM、按什么粒度清理,我没有确证。几种可能性都解释得通:

  • 进程组(process group)被整体回收
  • 作业对象(Windows Job Object)在父进程退出时关闭
  • 无控制台会话的 GUI 进程被会话管理器清理

能确证的只有行为和解法,机制层面以上都是推测。 如果你的环境和我不一样,先复现再说,别直接照搬结论。

有效的三件事

一、让父进程活到子进程结束

这是最可靠的一条。不要用「启动后就返回」的写法,改成启动后同步等待:

bash 复制代码
nohup ./cli.cmd publish-article ... >/dev/null 2>&1 &
disown
for i in $(seq 1 40); do
  sleep 5
  [ -f "$OUT" ] && grep -q "执行结束" "$OUT" && break
done

父进程在这里耗着,子进程就活得下来。代价只是父进程多占一会儿。

二、输出必须落到文件,不能靠管道

管道会随着进程一起消失,文件不会。把 stdout/stderr 全部重定向到文件,然后轮询文件内容判断进度,而不是等进程退出。

这一步同时解决了另一个问题:即使中途被杀,已经写进文件的部分输出还在,能从中断的位置看出跑到哪一步了。

三、用哨兵文件区分「没启动」和「启动后被杀」

这两种失败的排查方向完全不同,但表现很像(都是没有产物)。

bash 复制代码
OUT=/tmp/job.log
: > "$OUT"                      # 任务开始前清空
echo "STARTED $(date +%s)" >> "$OUT"
nohup ./cli.cmd ... >> "$OUT" 2>&1 &
# ...轮询...

判据:

  • OUT 完全为空 → 子进程启动即死,或者根本没起来。查命令路径、环境变量。
  • OUT 有 STARTED 但没有完成标记 → 起来了,中途被杀。查父进程生命周期。
  • OUT 有完成标记 → 正常。

我的第一次失败属于第一种,正因为有文件可查,才没把它误判成「程序内部报错」。

顺带:退出码 0 不等于任务完成

这套流程里还有一个独立的坑。有一次命令退出码是 0,看输出里的状态字段也是 true,但产物文件里根本没有完成标记。

原因是:子进程把状态写进日志后,主进程才做收尾;退出码反映的是主进程的收尾结果,不代表业务真的走完了。

所以判据要用产物里的完成标记,不要用退出码,也不要只信状态字段。三个都对上才算数。

一句话

nohup 是免死金牌的印象,来自「终端关掉进程别死」这个原始场景。在会被主动清理的运行环境里,真正决定子进程生死的是父进程还在不在。让父进程等着,比折腾信号处理简单得多。

相关推荐
子兮曰3 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰3 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万3 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝3 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
三十而立洋3 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
大蓝头3 天前
安装包UI美化之路-nsNiuniuSkin全新UI调试与预览方法
ui·electron·安装包·截图控件·截屏控件
卡布鲁3 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
汉堡大王95273 天前
Jev:不是聊天机器人, 而是一个智能 if 语句
前端·人工智能·后端
梦想很大很大3 天前
从运行事实到回归证据:Workrun 的 Telemetry 与 Evaluation 实践
前端·人工智能·后端
计算机魔术师3 天前
Meta Muse agent 接入 Shopify 的 Shop Pay 实现代理式购物
前端