首页 > Sysadmin > 正文

Linux 后台运行程序:nohup 的正确用法、日志重定向与退出终端不断线的原理

huyuanwen 2026-11-06 Sysadmin

为什么 & 挂后台还不够

很多同学在 SSH 里跑长任务,习惯在命令末尾加一个 & 把进程丢到后台。但只要这个 SSH 终端一关,任务往往跟着一起没了。原因是:终端关闭时,内核会向该终端的前台进程组发送 SIGHUP(挂断信号),会话内的进程收到后默认动作就是退出。& 只是把进程从”前台”挪到”后台”,并没有让它脱离这个会话,自然躲不过 SIGHUP。

nohup 到底做了什么

nohup 的全称是 no hang up,它做了一件很克制的事:让被它启动的进程忽略 SIGHUP 信号。这样即使终端关闭、会话被挂断,进程也不会被这个信号杀掉。需要注意的是,nohup 本身并不负责”后台运行”,它只负责”免疫挂断”,所以真正稳妥的写法是 nohup 与 & 搭配使用。

标准用法

最常用的一行命令:

nohup python3 train.py > train.log 2>&1 &

拆开看每一段的作用:

  • nohup:让 train.py 忽略 SIGHUP,终端关闭不受影响。
  • > train.log:把标准输出重定向到文件。若不重定向,nohup 会默认写到当前目录的 nohup.out,日志一多很难定位。
  • 2>&1:把标准错误也并入标准输出,同一个文件里既能看正常日志也能看报错。
  • 结尾的 &:把整条命令放进后台,SSH 终端立刻恢复可用。

启动后终端会打印一个进程号(PID),建议顺手记下来,后续停止任务要用:

[1] 28641

找回与查看后台任务

如果换了个终端、或者忘了 PID,可以用进程名反查:

ps -ef | grep train.py

确认进程还在,再实时跟踪日志:

tail -f train.log

这里有个常见误区:nohup 启动的任务,在新的 SSH 会话里用 jobs 是看不到的,因为 jobs 只显示当前 shell 自己管理的作业。跨会话查找只能靠 ps 或按 PID。

停止后台任务

拿到 PID 后正常终止:

kill 28641

如果进程卡住不响应,再用强制信号兜底:

kill -9 28641

能优雅退出就别急着上 -9,强杀可能让正在写数据库或写文件的任务留下半成品。

nohup、setsid、tmux 怎么选

  • nohup + &:一次性长任务(跑批、训练、大文件处理),启动后不需要交互,最简单。
  • setsid:让进程直接开一个全新会话、彻底脱离当前控制终端,比 nohup 更”干净”,适合脚本里想彻底脱离的场景。
  • tmux / screen:任务需要你随时接回去继续操作(比如交互式调试、边跑边看),断线后能重新 attach。nohup 做不到”接回交互界面”,这点要分清楚。

三个容易踩的坑

  1. 以为加了 nohup 就不用 &:只 nohup 不加 &,进程仍在前台占着终端,你还会以为它卡住了。两者职责不同,长任务建议一起用。
  2. 忘了重定向日志:默认写 nohup.out,多个任务混在一个目录时日志会互相覆盖或难以区分,务必显式指定日志文件名。
  3. 指望 nohup 保证开机自启:nohup 只解决”当前这次运行不被终端关闭杀掉”,服务器重启后并不会自动拉起。要开机自启,应该写成 systemd 服务。

小结

记住一句话:nohup 管”不被挂断杀死”,& 管”放到后台”,重定向管”日志去哪”。三者组合起来,才是 SSH 里跑长任务的完整姿势;需要断线重连继续交互,就换 tmux;需要开机自启,就交给 systemd。