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 后仍存活(使用 nohupdisown),重新登录检查。
3. 通过 Ctrl+Z 暂停前台任务,再用 jobs -l 验证状态变化。