
它打破了我们在第 1-7 章建立的“指令一条接一条顺序执行”的简单模型引入了控制流的突变。ECF 是操作系统实现进程、虚拟内存、I/O 和网络通信的基础机制。1. 什么是异常控制流 (ECF)在传统的程序执行中控制流是平滑的PC 递增或跳转。ECF 是指控制流发生**突然变化且不是由程序内部正常逻辑引起的情况。EC2 发生在硬件和软件的所有层次硬件层中断、陷阱、故障、终止。操作系统层进程上下文切换、信号处理。应用层longjmp、非局部跳转。核心思想ECF 是操作系统用来模拟并发Concurrency的基本机制。2. 异常Exceptions异常是 ECF 的最底层形式由硬件事件触发通过异常表Exception Table跳转到内核中的异常处理程序。异常的四种类型类型英文特点返回行为例子中断Interrupt异步来自外部 I/O 设备返回下一条指令键盘输入、网卡数据包到达陷阱Trap同步有意为之的系统调用返回下一条指令syscallfork,read,write故障Fault同步可恢复的错误可能返回当前指令缺页异常Page Fault、除零错误部分架构终止Abort同步不可恢复的错误不返回杀死进程非法指令、硬件严重错误、段错误Segfault某些情况异常处理流程当前指令执行完毕或出错。硬件识别异常号kkk。间接调用异常表中第kkk项指向的处理程序。处理程序执行通常在内核模式。根据异常类型返回到被中断的程序下一条指令或当前指令 a 或终止。关键点系统调用System Call本质上是一种特殊的**陷阱Trap**指令。用户程序通过执行syscall指令陷入内核内核执行相应服务后返回用户态。3. 进程Processes进程是 CSAPP 中最重要的抽象之一。逻辑控制流每个进程都有一个独立的逻辑控制流。多个进程可以并发运行单核交替执行或并行运行多核同时执行。上下文切换Context Switch内核保存当前进程的状态上下文恢复下一个进程的状态。这是开销较大的操作。私有地址空间每个进程拥有独立的虚拟地址空间包含代码段 (.text)数据段 (.data, .bss)堆 (Heap)栈 (Stack)共享库区域Linux 进程结构PID进程 ID。父进程与子进程关系形成树状结构。Init 进程祖先进程。4. 系统调用错误处理在 Unix/Linux中许多系统调用出错时返回 -1 并设置全局变量errno。Unix 风格检查返回值。Wrapper 函数CSAPP 书中提倡使用包装函数如Popen,Fork如果出错直接打印错误信息并退出简化代码逻辑。5. 进程控制Process Control这是 Shell Lab 的基础。创建进程pid_t fork(void)调用一次返回两次。在父进程中返回子进程的 PID (0)。在子进程中返回 0。出错返回 -1。关键特性子进程获得父进程数据空间、堆、栈的副本写时复制 Copy-on-Write但共享代码段。回收进程pid_t waitpid(pid_t pid, int *statusp, int options)挂起当前进程直到指定的子进程终止。防止僵尸进程Zombie Process已终止但未被父进程回收的进程。WNOHANG选项允许非阻塞等待。加载并运行程序execve(const char *filename, char *argv[], char *envp[])在当前进程上下文中加载并运行新程序。不创建新进程而是覆盖当前进程的代码、数据和栈。只有执行失败才返回。Shell 的工作原理Shell就是一个循环打印提示符。读取命令行。fork()创建子进程。子进程execve()运行命令。父进程waitpid()等待子进程结束。6. 信号Signals信号是软件层面的异常用于通知进程发生了某种事件。基本概念发送信号内核检测到事件如 CtrlC - SIGINT或进程调用kill()。接收信号目标进程更新 pending 位向量。处理信号当进程从内核模式返回用户模式时检查 pending 信号。如果有未屏蔽的信号则跳转到信号处理程序Signal Handler。重要特性pending 位向量每个信号对应一位。blocked 位向量进程可以阻塞某些信号。默认行为终止、忽略、或停止Stop。自定义处理通过signal()或sigaction()注册处理函数。信号处理的陷阱难点处理程序与主程序并发处理程序可能在主程序任何指令间中断执行。共享全局数据结构如果处理程序和主程序都访问全局变量必须小心竞态条件。非重入函数信号处理程序中只能调用异步信号安全函数如write,_exit不能调用printf,malloc等。阻塞信号在处理某个信号时同类型的信号默认会被阻塞防止递归调用导致栈溢出。常用函数kill(pid, sig)发送信号。alarm(secs)设置定时器到期发送 SIGALRM。sigprocmask()阻塞/解除阻塞信号集。sigsuspend()原子性地解除阻塞并挂起进程等待信号。这是编写健壮信号处理代码的关键。7. 非本地跳转Nonlocal JumpsC 语言提供的用户级 ECF 机制类似于异常但完全在用户空间完成。setjmp(jmp_buf env)保存当前上下文栈指针、PC 等到env返回 0。longjmp(jmp_buf env, int retval)恢复env中的上下文使程序看起来像是从setjmp返回但返回值为retval。用途深层嵌套函数中的错误恢复类似 try-catch 的早期实现避免层层返回错误码。风险可能导致资源泄漏因为跳过了中间的清理代码。8. 操作进程的工具/proc文件系统查看进程状态。ps列出进程。top/htop动态监控进程。strace跟踪系统调用。gdb调试进程查看堆栈。本章核心实验Shell Lab你需要编写一个简易 Shelltsh支持前台/后台作业管理。内置命令quit, jobs, fg, bg。正确处理 SIGCHLD子进程终止、SIGINTCtrlC、SIGTSTPCtrlZ。难点竞争条件处理。例如在fork之后、exec之前阻塞 SIGCHLD确保addjob在deletejob之前执行。总结ECF 是连接硬件中断、操作系统内核和用户程序的桥梁。异常是硬件触发的 ECF。进程是操作系统利用 ECF 提供的并发抽象。信号是软件触发的 ECF用于进程间通信和异步事件处理。理解 ECF 对于编写健壮的并发程序、理解操作系统原理以及进行底层系统编程至关重要。