异常控制流是操作系统用来实现I/O、进程和虚拟内存的基本机制。
应用程序利用一个叫做陷进或者系统调用的异常控制流形式来向操作系统请求服务。
异常控制流是实现并发的基本机制。
非本地跳转是一种应用层异常控制流,在C中通过setjump和longjump函数提供的。
之前都是学应用如何和硬件交互,本周介绍应用和操作系统交互。这些交互都围绕异常控制流。异常:硬件和操作系统交界部分(硬件层,硬件检测到的事件会触发控制突然转移到异常处理程序)。系统调用:为应用程序提供到操作系统的入口点。进程和信号:应用和操作系统交界部分(操作系统层,内核通过上下文切换控制一个用户进程转移到另一个进程)。非本地跳转:应用层(一个进程可以发送信号到另一个进程,而接收者会将控制突然转移到它到一个信号处理程序)。
8.1 异常502
异常是控制流中的突然变化,以响应处理器状态的某些变化。
状态变化称为事件。
8.1.1 异常处理503
系统启动时,操作系统分配和初始化一张异常表(异常号一部分是处理器设计者分配,其他是OS内核设计者分配),运行时,处理器检测到发生了一个事件,并且确定了相应的异常号k,随后,处理器触发异常(执行间接过程调用,通过异常表的表目k,转到相应的处理程序)。
8.1.2 异常的类别504
除了中断外,陷阱、故障、终止都(执行当前指令的结果)称为故障指令。
四类:
Class Cause Async/sync Return behavior
Interrupt
Signal from I/O device
Async
Always returns to next instruction
硬件中断的异常处理程序常常称为中断处理程序(interrupt handler)。
Trap
Intentional exception
Sync
Always returns to next instruction
最重要的用途就是系统调用。 例如:读一个文件,创建一个新的进程,加载一个新的程序,终止当前进程。
Fault
Potentially recoverable error
Sync
Might return to current instruction
不能修正错误就返回到内核中的abort例程,该例程会终止引起故障的应用程序。
Abort
Nonrecoverable
Error
Sync
Never returns
通常是硬件错误。处理程序将控制返回给一个abort例程,该例程会终止的应用程序。
异步异常由处理器外部的I/O设备中的事件产生。同步异常是执行一条指令的直接产物。
8.1.3 Linux/x86-64系统中的异常505
0-31 Intel 架构师定义的异常:包括除法错误、一般保护故障、缺页、机器检查等。32-255 对应是操作系统定义的中断和陷阱。
8.2 进程508
进程的经典定义是一个执行中程序的实例。每个程序都运行在某个进程的上下文中。上下文由程序正确运行所需的状态组成。状态包括内存中的代码和数据,栈,通用寄存器的内容,程序计数器,环境变量以及文件描述符的集合。
进程提供给应用程序的关键抽象:一个独立的逻辑控制流,一个私有的地址空间。
8.2.1 逻辑控制流508
逻辑流有许多不同的形式,异常处理程序、进程、信号处理程序、线程、java进程都是逻辑流的例子。
8.2.2 并发流509
一个进程执行它的控制流的一部分的每一时间段叫时间分片。一个进程和其他进程轮流执行的概念称为多任务,也叫时间分片。
并发流和处理器核数或计算机数没关系。并行流是并发流的真子集。若两个流并发运行在不同处理器核或计算机上,我们称之为并行流,他们并行的运行且并行的执行。
8.2.3 私有地址空间509
8.2.4 用户模式和内核模式510
用户模式变为内核模式,只能通过中断、故障或陷入系统调用这样的异常。
Linux提供一种聪明的机制,叫做/proc文件系统,允许用户模式进程访问内核数据结构的内容。比如,你可以在该文件系统中找出一般的系统属性,比如CPU类型/proc/cpuinfo,或者某个特殊进程使用的内存段/proc/
8.2.5 上下文切换511
操作系统内核使用一种称为上下文切换(1.保存当前上下文,2.恢复某个先前被抢占的进程被保存的 上下文,3.将控制传递给这个这个新恢复的进程)的较高层形式的异常控制流来实现多任务。上下文是内核重新启动一个被抢占的进程所需的状态。
切换之前,内核正代表进程A在用户模式下执行指令(没有单独的内核进程),在切换的第一部分中,内核代表进程A在内核模式下执行指令,然后在某一时刻,它开始代表进程B(看图,仍然是内核模式下)执行指令。
8.3 系统调用错误处理512
8.4 进程控制513
8.4.1 获取进程ID 513
8.4.2 创建和终止进程513
8.4.3 回收子进程516
一个终止了但还未被回收的进程称为僵死进程。
若一个父进程终止了,内核会安排init进程称为它的孤儿进程的养父。
option=0时,waitpid挂起调用进程的执行,直到她的等待集合中的一个子进程终止。Waitpid返回导致waitpid返回的已终止的子进程PID。此时子进程已经被回收,内核会从系统中删除掉它的所有痕迹。
waitpid实例
8.4.4 让进程休眠521
8.4.5 加载并运行程序521
8.4.6 利用fork和execve运行程序524
execve函数在当前进程的上下文中加载并运行一个新的程序,它覆盖当前进程的地址空间,但并没有创建一个新的进程,新的程序仍然有相同的PID,并继承了调用execve函数时打开的所有文件。
8.5 信号526
Linux信号:更高层次的软件形式的异常,它允许进程和内核中断其他进程。低层硬件异常是由内核异常处理程序处理的,正常情况下,对用户进程不可见,信号提供一种机制,通知用户进程发生了这些异常。比如一个进程试图除以0,那么内核就发送给他一个SIGFPE(号码8)信号。
8.5.1 信号术语527
一种类型最多只会有一个待处理信号(发出没有被接收的信号)。其他只会被丢弃。一个进程可以有选择阻塞接收某种信号,当阻塞时,它仍可以发送,但是取消阻塞之前它一直都是待处理信号并且不会被接收。
8.5.2 发送信号528
8.5.3 接收信号531
8.5.4 阻塞和解除阻塞信号532
8.5.5 编写信号处理程序533
8.5.6 同步流以避免讨厌的并发错误540
8.5.7 显式地等待信号543
8.6 非本地跳转546
8.7 操作进程的工具550
strace, ps, top, pmap, /proc虚拟文件系统
8.8 小结550