Linux nohup 和 & 的区别后台运行程序
bash sleep 1000 &
输出示例:[1] 12345
- `[1]` 表示这是当前终端的第 1 个后台作业。
- `12345` 就是这个 `sleep` 进程的 PID。
你可以用 `jobs` 命令查看当前终端所有后台作业:
```bash
jobs
# [1]+ Running sleep 1000 &
1.2 & 的致命弱点:受终端挂断信号影响
& 只是把进程放到后台,进程仍然是当前终端会话的子进程。当你退出终端(如直接点击关闭按钮,或执行 exit)时,系统会向该终端的所有子进程发送 SIGHUP(挂断信号),默认行为是终止这些进程。
SIGHUP 是 Linux 中一种信号,原本用于通知进程终端连接已断开。大多数前台或后台进程在收到此信号后会直接退出。
实用测试:
- 打开一个终端,执行
sleep 500 &。 - 查看进程存在:
ps aux | grep sleep。 - 直接关闭终端窗口。
- 在新终端中再次
ps aux | grep sleep,你会发现刚才的sleep进程已经消失了。
这就是只用 & 的风险——你的后台任务会在登出或断网时意外死亡。
2. nohup 命令:让进程忽略挂断信号
2.1 nohup 的作用
nohup 的全称是 "no hang up",它会启动一个进程,并使其忽略 SIGHUP 信号。这样,即使你退出了启动它的终端,进程仍然会继续运行。
默认情况下,nohup 会将程序的输出重定向到当前目录下的 nohup.out 文件中(若无法写入则尝试 $HOME/nohup.out)。
2.2 单独使用 nohup(没有 &)
如果你只执行:
nohup sleep 1000
终端会被这个前台进程占用,你无法输入其他命令,但即使你按 Ctrl+C 退出终端,进程也不会终止(不过你可以用 Ctrl+Z 挂起再用 bg 补救,但这不优雅)。单独使用 nohup 会丢失后台便利性。
2.3 推荐用法:nohup + &
这才是真正稳定且不占用终端的方式:
nohup sleep 1000 &
# 输出示例:nohup: ignoring input and appending output to 'nohup.out'
运行后,你可以安全地关闭终端,进程会变成孤儿进程,被系统的 init 或 systemd 收养,继续在后台存活。
3. 深度对比:nohup 和 & 的三重差异
| 特性 | & |
nohup |
nohup & |
|---|---|---|---|
| 是否后台运行 | ✅ 是 | ❌ 否(前台) | ✅ 是 |
| 终端关闭后进程存活 | ❌ 会收到 SIGHUP 并终止 | ✅ 忽略 SIGHUP,进程存活 | ✅ 忽略 SIGHUP,进程存活 |
| 输出重定向 | 默认输出到终端屏幕 | 默认输出到 nohup.out |
默认输出到 nohup.out |
| 适用场景 | 临时后台任务,不需要保持 | 无意义 | 长时间守护、远程训练、爬虫等 |
3.1 信号处理的不同
&启动的进程不改变信号处理方式,SIGHUP 仍会送达并终止它。nohup启动的进程在开始时就设置了对 SIGHUP 的忽略,因此无论终端是否挂断,进程都不受影响。
3.2 输入输出的区别
& 运行的进程仍然连接着终端,如果它试图从标准输入读取数据,可能会被暂停或出错。而 nohup 则会自动将标准输入重定向到 /dev/null,防止进程因等待输入而挂起。同时标准输出和标准错误都被追加写入 nohup.out。
你可以用 jobs -l 查看进程状态,并使用 fg 将后台作业调回前台,但 nohup 启动的进程如果脱离了终端,jobs 就看不到了(因为不再是当前 shell 的子进程)。
4. 实战进阶:更可靠的后台管理方案
4.1 自定义输出文件
不想要默认的 nohup.out?用重定向自行控制:
nohup python train_model.py > output.log 2>&1 &
> output.log重定向标准输出。2>&1将标准错误也合并到标准输出,一同写入output.log。- 最后的
&把整个任务送入后台。
这样你就能随时用 tail -f output.log 观察程序运行状况。
4.2 使用 disown 拯救未加 nohup 的进程
假设你一开始只用了 & 启动了一个长时间任务,忘了用 nohup,想退出终端时才发现。别慌,disown 可以补救:
# 假设你的后台作业编号为 1
jobs
# [1]+ Running long_task &
# 将其从 shell 的作业列表中移除,但进程不受影响
disown %1
执行 disown 后,该作业不再属于当前 shell 的作业表,退出终端时 shell 不会向它发送 SIGHUP。但注意:进程本身的 SIGHUP 处理方式并未改变。实际上,当你退出终端,终端设备关闭,如果进程没有对 SIGHUP 做特殊处理,它仍然可能收到并终止。更安全的方式是使用 disown -h:
disown -h %1
-h 选项会让 shell 在退出时不对该作业发送 SIGHUP,相当于手动让它“忽略”挂断,与 nohup 效果类似。
4.3 更现代的方案:tmux 或 screen
虽然 nohup & 能解决绝大多数后台运行需求,但对于需要实时查看输出、随时恢复交互的任务,推荐使用终端复用器:
# 新建一个 tmux 会话
tmux new -s mywork
# 在会话内正常启动程序
python train.py
# 然后按 Ctrl+B 再按 D 脱离会话
即使关闭终端,任务仍在 tmux 后台运行。重新连接时只需 tmux attach -t mywork 即可回到原环境。这种方法比 nohup 更灵活,且不产生 nohup.out 文件。
5. 常见问题与排错
Q1: 为什么我用了 nohup ... &,程序还是退出了?
检查以下几点:
- 程序内部是否主动捕获了 SIGHUP?
nohup只是让进程忽略 SIGHUP,如果程序自身注册了信号处理并主动退出,则无效。 - 是否因为资源限制被系统
OOM Killer杀死?用dmesg | grep -i kill查看。 - 确认命令正确,例如
nohup和&是否在同一条命令中。
Q2: nohup.out 文件一直增大怎么办?
可以定期轮转或直接重定向到 /dev/null 丢弃输出:
nohup my_verbose_app > /dev/null 2>&1 &
Q3: 怎么找到之前用 nohup 启动的进程?
使用 ps 配合 grep:
ps -ef | grep your_program_name
# 或者利用 nohup.out 文件找到 PID