指令1,指令性2,指令3……,这样的控制转移序列就是处理器的控制流(control flow)。

现代系统通过使控制流发生突变,来应对系统状态的变化,称为异常控制流(ECF),它发生在计算机系统的各个层次。

ECF 的重要性:

  • ECF 是操作系统实现 I/O、进程和虚拟内存的基本机制。
  • 应用程序通过使用系统调用的 ECF 形式,来向操作系统请求服务。
  • 操作系统为应用程序提供了强大的 ECF 机制,用来创建新进程、等待进程终止、通知其他进程中的异常事件。
  • ECF 是计算机系统中实现并发的基本机制,比如中断应用程序执行的异常处理程序。
  • 非本地跳转是一种应用层 ECF,软件异常允许程序进行非本地跳转来响应错误情况。

1. 异常

异常就是控制流中的突变,用来响应处理器状态中的某些变化。它一部分由硬件实现,一部分由操作系统实现。

异常的剖析

在任何情况下,当处理器检测到有事件发生时,就会通过一张叫做「异常表」的跳转表,进行一个间接过程调用,交给异常处理程序去处理。
当处理完成后,根据引起异常事件的类型,发生以下 3 种情况中的一种:

  • 处理程序将控制返回给当前指令。
  • 处理程序将控制返回给下一条指令。
  • 处理程序终止被中断的程序。

异常可以分为四类:

  • 中断:来自外部 I/O 设备信号的结果,不是由专门的指令造成的。
  • 陷阱:最重要的用途是在用户程序和内核之间提供一个接口——系统调用。
  • 故障:由错误情况引起,可能被故障处理程序修正,比如缺页异常。
  • 终止:不可恢复的致命错误造成的结果,通常是一些硬件错误。
异常的类别

2. 进程

进程就是一个正在执行的程序实例,系统中的每个程序都运行在某个进程的上下文(context)中。

进程提供给应用程序的关键抽象:

  • 一个独立的逻辑控制流,它提供一个假象,好像程序独占地使用处理器。
  • 一个私有的地址空间,它提供一个假象,好像程序独占地使用内存系统。
进程控制流

进程是轮流使用处理器的,每个进程执行它的流的一部分,然后被抢占(暂时挂起),然后轮到其他进程。

进程为每个程序提供它自己私有地址空间。一般而言,和某个地址关联的内存字节是不能被其他进程读或写的。每个私有地址空间都有相同的通用结构。

x86-64 linux 进程地址空间

处理器通常是用某个控制寄存器中的一个模式位,来实现限制应用可以执行的指令以及可以访问的地址空间。

当设置了模式位时,进程就运行在内核模式中,它可以执行指令集中的任何指令,并且可以访问系统中的任何内存位置。没有设置模式位时,进行就运行在用户模式中,不允许执行特权指令,也不允许直接引用地址空间中内核区的代码和数据。

运行应用程序代码的进程起初是在用户模式中的,进程从用户模式变为内核模式的唯一方法就是通过中断、故障或者陷入系统调用这样的异常。

操作系统内核使用一种称为「上下文切换」的异常控制流来实现多任务。内核为每个进程维持一个上下文。上下文就是内核重新启动一个被抢占的进程所需的状态。这些状态包括:通用目的寄存器、程序计数器、用户栈、环境变量等。

上下文切换的过程:

  • 保存当前进程的上下文
  • 恢复某个先前被抢占的进程保存的上下文
  • 将控制传递给这个刚恢复的进程

当执行系统调用或者中断时,都可能会发生上下文切换。

进程上下文切换