Shell 后台任务管理
在终端里起了个开发服务器,比如 npm run dev,突然想装个包或者查个日志,结果窗口被霸占了。多数人的做法是 Ctrl+C 杀掉,开个新标签页重来。其实大可不必。
Shell 自带一套后台任务管理机制,用好了,不用杀进程也不用开新窗口。
先说三个基础命令
在命令末尾加 &,Shell 会把这个任务丢到后台,立刻把提示符还给你:
hermes dashboard &回车后返回 [1] 23456,[1] 是作业号,23456 是 PID。这时候你可以继续在同一窗口敲别的命令。
jobs 列出当前终端后台挂着的所有任务:
❯ jobs
[1] + running hermes dashboard
[2] - running python heavy_script.pyfg %1 把 1 号作业拉回前台,bg %1 让暂停的作业在后台继续跑。就这么简单。
Ctrl+Z 的坑
很多人以为 Ctrl+Z 等于后台运行。不是。
Ctrl+Z 发的是 SIGTSTP 信号,本质是暂停,不是后台。进程进入 Stopped 状态后,代码完全不执行了,CPU 占用归零。它不会在后台悄悄干活,它只是被冻住了。
如果这时候你直接关掉终端窗口,Shell 会给所有子进程发 SIGHUP。一个冻住的进程收到 SIGHUP,就直接被杀掉了,没有余地。
忘记加 & 怎么办
你起了个大编译任务,跑了 10 分钟才发现忘加 &。别按 Ctrl+C,三步搞定:
- Ctrl+Z,冻住进程,拿回提示符。
- 输入
bg,进程在后台恢复运行。 - 输入
disown,让进程脱离当前终端。
disown 是关键。执行后,即使你关掉终端,进程也会被 init 或 systemd 接管,安安稳稳把活干完。
disown 之后怎么管
一旦执行了 disown,这个进程就从当前 Shell 的家谱里被除名了。它不再是一个作业(Job),所以 jobs、fg、bg 对它全部失效。
但它没有失控。它现在是一个普通的系统级进程,你需要切到底层视角,用管进程的方式去调教它。
找到它:拿到 PID
disown 之后你手里没有作业号了,只有 PID。如果启动时没记住那串数字,用 pgrep 通过名字查:
pgrep -a hermes-a 参数会把进程启动时的完整命令也打印出来,防止你认错人。传统流派用 ps aux | grep hermes,效果一样,多打几个字而已。
杀掉它:发信号
拿到 PID 后,想让它停下来不能靠 Ctrl+C 了,得用 kill 发信号:
kill 23456 # SIGTERM,允许进程保存数据后体面退出
kill -9 23456 # SIGKILL,不给反应时间,瞬间断电懒得查 PID 的话,pkill hermes 直接按名字杀,前提是系统里只有一个同名的进程在跑。
看它吃多少资源
想确认这个脱管的进程是不是在后台疯狂吃 CPU 和内存,打开 htop 或 btop,按 F4 或 / 过滤进程名,就能实时盯着。
看它的实时输出
这是 disown 最让人抓狂的地方。如果当初没做日志重定向,进程的标准输出要么还往你终端屏幕上刷,要么随终端关闭彻底看不到了。
Linux 一切皆文件,进程的输出在 /proc 目录下有记录。查到 PID 后(假设是 23456):
sudo tail -f /proc/23456/fd/1fd/1 就是标准输出的文件描述符。这招能临时看一眼,但不够稳定,进程重启后 PID 就变了。
操作对照表
| 操作 | 内核信号 | 进程状态 | 关闭终端后 |
|---|---|---|---|
command & | 无 | Running | 死(收到 SIGHUP) |
| Ctrl+Z | SIGTSTP | Stopped | 死 |
bg | SIGCONT | Running | 死(仍绑定终端) |
command & + disown | 无 | Running | 活 |
| Ctrl+C | SIGINT | Terminated | 已经死了 |
长期服务别用这套
& 加 disown 适合临时救急。如果你跑的是需要长期常驻的服务,比如 NAS 上的爬虫或者自建的 API 网关,老老实实用 systemd 或者 tmux。Shell 快捷键管的是临时任务,守护进程管的是长期服务。
如果一个任务你频繁需要查日志、频繁需要手动暂停恢复,也别用 & + disown。开个 tmux 虚拟窗口,随时分离随时重连,体验好得多。