目录
进程通过系统调用发出中断指令以递交CPU控制权,所以中断(interrupt)是实现进程切换的主要机制,也是驱动操作系统运作的"发动机"。但在正式介绍中断机制前,需要先介绍中断机制出现的原因及适用环境。
CPU双模式(Dual-mode)
进程的指令都是直接运行在CPU上的,但进程运行过程难免向操作系统请求I/O等系统资源,有时又需要操作系统切换进程,这些都需要到内核空间进行操作。操作系统内核如何满足进程需求的同时避免执行进程的危险操作(比如擅自修改操作系统内核)?
首先,我们可以在地址空间中单独划分一块区域存放系统内核代码数据及进程控制块等关键内容,剩余区域用于用户程序的运行,分别称为内核空间和用户空间。然后为内核空间及用户空间运行的指令赋予不同的级别,内核空间的指令级别高,称为特权指令,可以访问一切内存和寄存器,而用户空间运行的指令级别低,称为非特权指令。最后,根据CPU运行的指令级别高低,对CPU运行模式进行区分。当CPU运行特权指令时,CPU处于内核模式,反之则处于用户模式。
Intel的x86处理器是通过Ring级别来进行访问控制的,从Ring0到Ring3共分4级。R0层拥有最高的权限,R3层拥有最低的权限。Intel原有的构想是应用程序工作在R3层,只能访问R3层的数据;操作系统工作在R0层,可以访问所有层的数据;而其他驱动程序位于R1、R2层。实际上,Linux和Windows操作系统均只选取R0层和R3层,分别来运行操作系统指令和应用程序指令——双模式(dual-mode)。
1.内核模式(kernel mode):操作系统运行程序,可以使用特权指令,访问所有内存和寄存器。
2.用户模式(user mode):应用程序运行程序,只能使用一般指令,访问特定的内存和寄存器。
x86体系的CPU模式主要由当前程序特权级CPL和程序状态字PSW共同决定的。CPL代表当前执行的代码特权等级,存储在CS(代码寄存器)和SS(段寄存器)的第0位和第1位上,程序状态字PSW第12和13位IOPL是输入输出特权级位(两位表示0~3四个等级)。当CPL>IOPL时,可以执行像IN、OUT等特权指令,相当于CPU处于内核模式。从概念上,我们可以简单地理解为CPU存在一个模式位寄存器(mode bit register),当模式位为0时,CPU处于用户模式,当模式位为1时,CPU处于内核模式,模式位的修改由中断决定。
安全控制转移(Safe control transfer)
进程切换将CPU控制权交还给操作系统的过程需要从用户模式切换到内核模式,操作系统分配CPU给进程使用又需要从内核模式切换回用户模式。因此,区分完用户模式和内核模式的含义,我们还需要了解如何安全地实现模式切换及控制器转移。
1. 触发事件
目前主要有两种事件触发从用户模式切换到内核模式:异常(exception)、中断(interrupt)。
异常(exception):异常是由当前正在执行的用户进程产生。异常出现的情况包括算数溢出、除零、访问地址时越界、试图使用特权指令或执行"陷入指令(trap)"等情况,这时硬件暂停当前运行进程,切换CPU运行模式,并转到异常处理程序(exception handler)或错误处理程序(debugger)。
中断(interrupt):中断是外部事件发生并传达给进程的内部信号,是为了支持CPU和设备之间的并行操作而产生的。CPU以外发生事件,事件发生后会产生一条int中断指令,使CPU暂停正在执行的进程并保留现场(context),转到中断处理程序(interrupt handler),处理完成后发出iret中断返回指令返回断点,继续执行被打断的进程。除此之外,进程间的中断是另一常见中断。
异常和中断的来源不同,发生过程类似,为方便理解故合并讨论。
2. 中断处理
中断/异常机制是操作系统的核心机制之一,需要硬件和软件相互配合。硬件通过中断隐指令捕获中断源发出的中断/异常请求,响应中断并将CPU控制权转交给中断/异常处理程序,而处理程序需要识别中断/异常的类型并进行相应处理。
中断捕获
硬件保存现场
查询中断向量表的同时,CPU需要为软件处理中断做准备。首先,CPU需要将原进程的程序状态字PSW、程序计数器PC保存在系统内核栈,以便中断处理结束后能够准确地返回到原进程中断点。其次,CPU根据中断指令修改模式位寄存器,将CPU切换至内核模式。
程序计数器(Program Counter)是用于存放下一条指令所在单元的地址的地方。
程序状态字(Program Status Word, PSW)又称状态寄存器,主要用于反映处理器的状态及某些计算结果以及控制指令的执行。用一个专门的寄存器来指示处理器状态。
查询中断向量表
中断向量:一个内存单元,存放中断处理程序入口地址PC和程序运行所需的程序状态字PSW。
CPU根据中断码查询中断向量表,获得对应的中断处理程序的入口地址,并将PC设置成该地址,新的指令周期开始,CPU控制转移至中断处理程序。
处理中断
中断处理程序(interrupt handler):响应一个特定中断后,操作系统会执行的特定函数。Linux系统的中断处理程序是按照特定类型声明的C函数。
首先,中断处理程序保存原进程剩余的寄存器信息至内核栈;然后分析中断/异常的原因,执行对应的功能。比如进程切换产生的中断,则根据进程调度策略选择要执行的新进程,并将PC置为新进程的地址。最后,执行完中断后发出中断返回指令,CPU检测到该指令,继而运行原进程或新进程。
进程切换(Switching Between Processes)
进程切换主要有两种方法:一是等待正进程使用系统调用发出中断协作方法,二是设置计时器强制中断的非协作方法。下面分别以两种方法为例,分别总结中断处理的过程。
1. 协作方法:等待系统调用(A Cooperative Approach: Wait For System Calls)
操作系统启动
启动时,CPU位于内核模式运行操作系统。系统初始化中断向量表(trap table),并将表内中断处理程序(interrupt handler)的地址告知硬件。
执行系统调用
- 系统调用是操作系统提供给用户进程的唯一接口,该方法会在每个进程结束的位置设置一个显示的系统调用,该系统调用的功能就发出中断指令;
- 硬件捕获中断指令,将寄存器内容保存至内核栈,修改模式位使CPU切换至内核模式,并根据中断码查表引出中断处理程序;
- 中断处理程序使用进程调度策略选择进程运行,执行完使用中断返回指令
return-from-trap
回到用户模式。
除此之外,当进程做一些非法的事情时,则会产生一个异常指令,强制剥夺进程的CPU控制权。
但如果正在运行的进程是包含死循环,那么协作方法便失效,这时需要操作系统获取CPU控制权。
2. 非协作方法:操作系统控制(A Non-Cooperative Approach: The OS Takes Control)
非协作方法是在部署一个计时器统计当前进程运行时间,当运行时间超过一定限度(e.g Xms),计时器便主动发出中断指令,强制停止当前进程的运行,并把CPU控制权移交给操作系统。
参考资料
《Operating System: three easy pieces》
《Operating Systems: Principles and Practice》
《Operating System Concepts》
操作系统原理 北京大学 陈向群