4.2.1 作业控制基础:前台/后台与会话/作业概念
作业控制是交互式 Shell 提供的一套机制,用于管理多个命令在同一终端中的运行状态。理解前台/后台、会话、作业与进程组的关系,是后续信号处理、守护进程与自动化运维的基础。
-
前台与后台
前台任务占用终端输入输出,键盘信号(如 Ctrl+C、Ctrl+Z)直接作用于前台进程组。后台任务不占用终端,但默认仍与终端关联,可能因终端关闭而被挂起或结束。 -
作业(Job)
作业是 Shell 对一组命令的管理单元,通常对应一个进程组。作业拥有编号(如 %1),用于交互式控制(暂停、继续、切换前后台)。 -
进程组(Process Group)
进程组是内核对多个相关进程的组织,便于信号广播与前后台切换。一个管道命令通常会生成多个进程,属于同一进程组。 -
会话(Session)
会话是更高层级的组织结构,一个会话可包含多个进程组。会话首进程通常是登录 Shell,拥有控制终端。与终端的关联决定了作业控制能力。 -
控制终端与作业控制
只有具备控制终端的会话才支持作业控制。终端关闭会向会话中的进程发送 SIGHUP,导致后台任务被终止或挂起,除非做了隔离处理(如使用 nohup 或 disown)。
原理草图(作业/进程组/会话关系):
关键命令与示例(含预期效果):
1) 前台/后台与作业编号
# 1. 启动一个前台任务
sleep 300
# 2. 通过 Ctrl+Z 暂停前台任务(SIGTSTP)
# 终端输出示例:
# [1]+ Stopped sleep 300
# 3. 查看作业列表
jobs -l
# 预期输出示例:
# [1]+ 12345 Stopped sleep 300
# 4. 后台启动任务
sleep 600 &
# 预期输出示例:
# [2] 12388
2) 进程组与会话观察
# 查看当前Shell的PID、PGID、SID
echo "PID=$$"; ps -o pid,pgid,sid,tty,cmd -p $$
# 启动一个管道命令并查看进程组一致性
sleep 1000 | cat &
jobs -l
# 查看管道子进程的PGID一致
ps -o pid,pgid,cmd | grep -E 'sleep 1000|cat'
3) 终端关闭影响与排错
# 后台任务默认仍挂在终端上
sleep 1000 &
# 查看该任务的TTY
ps -o pid,tty,stat,cmd -p $!
# 预期:TTY 不是 "?",说明仍与终端关联
# 关闭终端或断开 SSH 后,任务可能收到 SIGHUP 结束
# 典型现象:重新登录后 ps 看不到该进程
4) 隔离示例(避免 SIGHUP)
# 使用 nohup 保持任务在会话结束后继续运行
nohup sleep 1000 >/tmp/nohup.out 2>&1 &
disown %1
# 验证:重新登录后,进程仍存在
ps -ef | grep sleep
命令解释(必要参数):
- jobs -l:显示作业编号、PID 与状态
- &:将命令放入后台执行
- Ctrl+Z:发送 SIGTSTP 暂停前台进程组
- ps -o pid,pgid,sid,tty,cmd:查看进程组、会话、终端关联
- nohup:忽略 SIGHUP,输出默认写入 nohup.out
- disown %1:从 Shell 作业表移除,避免 SIGHUP 影响
常见问题与排错清单:
- 后台任务“莫名消失”:检查是否因终端断开收到 SIGHUP
bash
ps -o pid,tty,stat,cmd -p <PID>
# 若 TTY 非 ? 且终端关闭,可能已被 SIGHUP 终止
- 管道命令只杀掉部分进程:因为同一进程组未整体处理
bash
# 查看同PGID的进程
ps -o pid,pgid,cmd | awk '$2==<PGID>{print}'
练习:
1. 启动 sleep 200 | cat 并放入后台,验证两个进程的 PGID 是否一致。
2. 将一个后台任务保持到断开 SSH 后仍存活(使用 nohup 与 disown),重新登录检查。
3. 通过 Ctrl+Z 暂停前台任务,再用 jobs -l 验证状态变化。