2. 操作系统运行环境与运行机制
1 处理器状态
处理器由运算器,控制器,一系列寄存器以及高速缓存构成。其中的寄存器分为两类:
- 用户可见寄存器:高级语言编译器通过优化算法分配并使用之,以减少程序访问内存次数
- 控制和状态寄存器:用于控制处理器的操作,通常由操作系统代码使用
控制和状态寄存用于控制处理器的操作,在某种特权级别下面可以修改,访问。常见的控制和状态寄存器:
- 程序指令寄存器(PC),记录将要取出的指令地址
- 指令寄存器(IR),记录最近取出的指令
- 程序状态字(PSW),记录处理器的运行状态比如条件码、模式、控制位
2 操作系统在运行时对于环境有什么需求
操作系统的特征包括:并发、共享。因此操作系统需要环境提供保护与控制,对应到硬件系统,需要硬件提供基本的运行机制:
- 处理器具有特权级别,能在不同的特权级别运行不同的指令集
- 硬件机制需要将OS与用户程序隔离
为了满足这些需求,处理器被设计为多种状态
现代的处理器通常将CPU状态设计为两种、三种或四种。在程序状态字寄存器PSW中专门设置一位,根据运行程序对资源和指令的使用权限而设置不同的CPU状态。
对于操作系统而言,它需要两种CPU状态:
- 内核态(kernel Mode):运行操作系统程序
- 用户态(User Mode):运行用户程序
对应着两种指令:
- 特权指令:只能由操作系统使用、用户程序不能使用。例如:启动I/O,内存清零,修改程序状态字,设置时钟,允许/禁止中断,停机
- 非特权指令:用户程序可以使用。例如:控制转移,算术运算,访管指令,取数指令
例子:X86处理器
X86有四个处理器特权级别,通常称做特权环:R0、R1、R2、R3。从R0到R3,特权能力由高到低。R0相当于内核态,R3相当于用户态,R1、R2介于两者之间。不同级别运行的指令不同。
那么CPU的不同状态之间是如何转换的呢?
用户态 → 内核态:唯一途径 → 中断/异常/陷入机制
内核态 → 用户态:设置程序状态字PSW
有一条特殊的指令:陷入指令(又称访管指令),提供给用户程序的接口,用于调用操作系统的功能(服务)
例如:int,trap,syscall,sysenter/sysexit
3 中断与异常机制
中断/异常对于操作系统极其重要。可以说,操作系统是由“中断驱动”或者 “事件驱动”的。
它们的概念如下:
- CPU对系统发生的某个事件作出的一种反应
- CPU暂停正在执行的程序,保留现场后自动转去,执行相应事件的处理程序,处理完成后返回断点,继续执行被打断的程序
中断与异常的差别在于,中断是由外部事件触发的(比如I/O事件),异常则是由CPU执行指令时本身出现的问题触发的(比如算术溢出,除零)。 因此中断也被称为“外中断”,异常也被称为“内中断”。
下图列出了常见的中断以及异常的例子:
下表总结了关于中断异常的一些知识:
4 中断与异常机制的工作原理
硬件:捕获中断源发出的中断/异常请求,以一定方式响应,将处理器控制权交给特定的处理程序
软件:识别中断/异常类型并完成相应的处理
中断响应:发现中断,接收中断的过程,由中断硬件部件完成。处理器控制部件中设有中断寄存器。
在每条指令执行周期的最后时刻扫描中断寄存器,查看是否有中断信号,若有中断,中断硬件将该中断触发器内容按规定编码送入PSW的相应位,称为中断码,通过查中断向量表引出中断处理程序。
中断向量表:
中断向量表记录这不同的中断向量。中断向量是一个内存单元,存放中断处理程序入口地址和程序运行时所需的处理机状态字。执行流程按中断号/异常类型的不同,通过中断向量表转移控制权给中断处理程序。
下面是Linux中断向量表:
一个完整的中断响应流程如下:
第一步,设备发来了中断信号,第二步,中断硬件部件,会去保存现场把这些内容保存到了系统堆栈里头,保存的内容主要的是PSW加PC寄存器的内容。第三步,中断硬件根据中断码来去查中断向量表,通过查中断向量表得到对应的中断处理程序。第四步,把中断处理程序的入口地址等相关信息推送到相应的寄存器,那么下一个指令 周期开始,就是执行中断处理程序。
设计操作系统时,为每一类中断/异常事件编好相应的处理程序,并设置好中断向量表。所以软件部分是提前就设置好的,由硬件部分来捕捉中断,执行程序。
5 系统调用
系统调用是指用户在编程时可以调用的操作系统功能。系统调用是操作系统提供给编程人员的唯一接口,可以使CPU状态从用户态陷入内核态,改变特权级别。
系统调用是基于中断/异常机制来实现的。设计过程如下
- 实现中断/异常机制
- 选择一条特殊的指令:陷入指令(访管指令),引发异常,完成用户态到内核态的切换
- 为每一个系统调用事先编号,即系统调用号
- 把系统调用的服务历程存放在系统调用表中
怎样实现用户程序的参数传递给内核? 常用的3种实现方法:
- 由陷入指令自带参数:陷入指令的长度有限,且还要携带系统调用功能号,只能自带有限的参数
- 通过通用寄存器传递参数:这些寄存器是操作系统和用户程序都能访问的,但寄存器的个数会限制传递参数的数量
- 在内存中开辟专用堆栈区来传递参数
一个完整的系统调用过程如下:
当CPU执行到特殊的陷入指令时:
- 中断/异常机制:硬件保护现场;通过查中断向量表把控制权转给系统调用总入口程序
- 系统调用总入口程序:保存现场;将参数保存在内核堆栈里;通过查系统调用表把控制权转给相应的系统调用处理例程或内核函数
- 执行系统调用例程
- 恢复现场,返回用户程序
系统调用是特殊的异常指令。