这篇文章单独记录 Shell Lab。它对应 CSAPP 第 8 章,重点是进程、信号和 job control。

0. 目标

Shell Lab 通常要求实现一个简单 shell,支持:

  • 运行前台任务
  • 运行后台任务
  • jobs
  • bg
  • fg
  • quit
  • 正确处理 SIGCHLD
  • 正确处理 SIGINT
  • 正确处理 SIGTSTP

1. 核心概念

需要先理解:

  • 进程
  • 子进程
  • 进程组
  • 前台任务
  • 后台任务
  • 信号
  • zombie process
  • fork
  • execve
  • waitpid

2. Shell 的基本流程

一个极简 shell 大概是:

1
2
3
4
5
6
7
8
9
10
11
12
while (1) {
read_command();
parse_command();

if (is_builtin_command()) {
run_builtin_command();
} else {
fork_child();
exec_program_in_child();
wait_if_foreground_job();
}
}

3. 信号处理

Shell Lab 的难点不在能不能 fork,而在信号。

重点:

  • 子进程结束时,父进程会收到 SIGCHLD
  • 用户按 Ctrl-C,会产生 SIGINT
  • 用户按 Ctrl-Z,会产生 SIGTSTP
  • shell 自己不应该被随便杀掉
  • 前台 job 应该收到终端信号

4. Job Control

job table 通常记录:

1
2
3
4
job id
process id
state
command line

常见状态:

1
2
3
FG:前台运行
BG:后台运行
ST:停止

5. 嵌入式关联

Shell Lab 对嵌入式 Linux 很有价值:

  • 理解服务进程如何启动子进程
  • 理解信号和进程退出
  • 理解 zombie process
  • 理解 daemon / supervisor / shell 的基本模型
  • 为调试 Linux 用户态程序打基础

6. 复盘重点

学完后要能回答:

  • fork 后为什么会有两个返回值?
  • execve 之后原来的程序去哪了?
  • 为什么需要 waitpid 回收子进程?
  • Ctrl-C 到底发给谁?
  • 前台任务和后台任务有什么区别?
  • 为什么信号处理里要注意竞态条件?