在终端里运行程序时,常会碰到两种情况:一是程序占着前台,没法继续输入下一条命令;二是程序跑到一半,想先暂停或者放到后台继续跑,把终端腾出来。这就要用到 Shell 的前后台作业控制(Job Control)。
这次不加 &,直接在前台启动 sleep:
crow@opsbox:~$ sleep 300
终端提示符不会返回,因为 Shell 正在等待 sleep 执行完。按下快捷键 Ctrl + Z:
^Z
[1]+ Stopped sleep 300
提示符立刻恢复,刚才的 sleep 进程被暂停了。用 jobs -l 查看当前终端托管的作业:
crow@opsbox:~$ jobs -l
[1]+ 2180 Stopped sleep 300
crow@opsbox:~$ ps -p 2180 -o pid,stat,comm
PID STAT COMMAND
2180 T sleep
[1]是当前终端里的作业号(Job Number),后续可以用%1指代它;2180是进程的 PID;ps输出的STAT列变成了T,代表进程处于暂停(Stopped)状态。
切到后台与拉回前台
想让暂停的 1 号作业在后台继续计时,执行 bg(background):
crow@opsbox:~$ bg %1
[1]+ sleep 300 &
crow@opsbox:~$ jobs -l
[1]+ 2180 Running sleep 300 &
crow@opsbox:~$ ps -p 2180 -o pid,stat,comm
PID STAT COMMAND
2180 S sleep
执行 bg %1 后,Shell 向该进程发送了继续运行信号(SIGCONT),jobs -l 显示为 Running,而 ps 中的 STAT 从 T 变回了代表可中断睡眠的 S——这个大写字母 S 就是上一章任务卡要求提交的答案。
如果想把后台作业重新拉回前台,执行 fg(foreground):
crow@opsbox:~$ fg %1
sleep 300
进程回到前台后,按下 Ctrl + C 即可直接终止它。
注意
用 & 或 bg 放到后台的作业,父进程依然是当前 bash 会话。一旦关闭终端窗口或 SSH 断开,内核会向该会话下的进程发送挂断信号 SIGHUP,导致后台作业跟着退出。如果临时跑一个耗时脚本不想因断连中断,可以用 nohup 命令 > out.log 2>&1 & 忽略 SIGHUP;而真正需要开机自启、崩溃自动拉起的后台服务,后面我们会统一交给 systemd 管理。
进程信号
按快捷键或执行 kill 命令,本质上都是通过内核向目标进程发送信号(Signal)。运行 kill -l 可以列出全部 64 种信号,日常运维最常用的是以下五种:
| 操作 / 命令 | 对应的信号 | 行为说明 |
|---|---|---|
| 终端断开 | SIGHUP (1) | 终端挂断信号。默认让关联终端的进程退出;许多守护进程(如 Nginx、sshd)约定收到 SIGHUP 时不退出,而是重新加载配置文件 |
| Ctrl + C | SIGINT (2) | 键盘中断信号。通知前台进程用户请求中断,程序可捕获该信号完成清理后退出 |
| Ctrl + Z | SIGTSTP (20) | 终端暂停信号。让前台进程进入 T(Stopped)挂起状态,等待后续 fg 或 bg 唤醒 |
kill PID | SIGTERM (15) | 默认终止信号。kill 不带参数时默认发送此信号。程序收到后可以先刷写缓冲区、关闭连接、清理锁文件再安全退出,停止进程时应优先使用 |
kill -9 PID | SIGKILL (9) | 强制杀死信号。不经过进程自身处理,直接由内核强行回收资源。进程来不及保存数据,仅在 SIGTERM 无效、进程彻底卡死时使用 |
确认刚才启动的 sleep 进程已经清理干净后,就可以回到第一章的任务卡提交 S 了。