控制前台与后台任务

在终端里运行程序时,常会碰到两种情况:一是程序占着前台,没法继续输入下一条命令;二是程序跑到一半,想先暂停或者放到后台继续跑,把终端腾出来。这就要用到 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 + CSIGINT (2)键盘中断信号。通知前台进程用户请求中断,程序可捕获该信号完成清理后退出
Ctrl + ZSIGTSTP (20)终端暂停信号。让前台进程进入 T(Stopped)挂起状态,等待后续 fg 或 bg 唤醒
kill PIDSIGTERM (15)默认终止信号。kill 不带参数时默认发送此信号。程序收到后可以先刷写缓冲区、关闭连接、清理锁文件再安全退出,停止进程时应优先使用
kill -9 PIDSIGKILL (9)强制杀死信号。不经过进程自身处理,直接由内核强行回收资源。进程来不及保存数据,仅在 SIGTERM 无效、进程彻底卡死时使用

确认刚才启动的 sleep 进程已经清理干净后,就可以回到第一章的任务卡提交 S 了。

本章阅读

阅读标记