操作系统导论学习笔记(三)

只有少量的物理 CPU,操作系统如何提供有几乎无限 CPU 可用的假象?

操作系统通过虚拟化(Virutalization) CPU 来提供这种假象。通过让每个程序只运行一个时间片,然后切换到其他进程,操作系统提供了存在多个虚拟 CPU 的假象。这就是时分共享(time sharing) CPU 共享技术。

潜在的开销就是性能降低,因为如果 CPU 必须共享,每个进程的运行就会变慢。

要实现 CPU 的虚拟化,操作系统需要提供底层 机制(mechanism) 和高层 策略(policy) 的支持:

  • 机制是一些低级方法或协议,比如上下文切换(context switch),让操作系统可以停止运行当前程序,并在给定的 CPU 上运行另一个程序。
  • 策略是操作系统作出某种决定的算法,比如调度策略(scheduling policy)决定当前 CPU 运行一组待运行程序中的哪一个。

分离机制和策略:机制为 how 提供答案,策略为 which 提供答案,将两者分开可以轻松地替换策略,而不必重新考虑机制。这是一种通用的软件设计原则:模块化

关键问题:如何高效、可控地虚拟化 CPU

操作系统必须以高性能的方式虚拟化 CPU,同时保持对系统的控制。为此,操作系统会巧妙地利用硬件的支持。

受限的直接执行

基本技巧:受限的直接执行 LDE Limited Direct Execution

直接执行」是指程序直接运行在 CPU 上

受限」的体现之一,是 CPU 执行模式区分「用户模式 user mode」 和 「内核模式 kernel mode」,用户模式下,程序执行是受限制的,比如不能执行特权操作的(比如访问磁盘 I/O)。

LDE 协议的具体实现方式:

  1. 操作系统启动时,内核使用特权指令设置陷阱表 (trap table),告知硬件接收到特定指令时,到哪里寻找需要执行的程序。
  2. 用户程序运行在用户模式下,发出系统调用后,硬件检测到变化,保存当前程序的状态(比如寄存器入栈),转为内核模式,查询陷阱表,然后跳转到对应的内核程序处执行。内核执行完程序后,调用返回用户模式指令,硬件恢复寄存器,转为用户模式,回到应用程序中继续执行。

系统调用类似过程调用,但隐藏在系统接口里的实现,是著名的陷阱指令。为了仔细遵循与内核一致的调用约定(例如将参数放在知名位置),库函数的系统调用部分是用汇编语言手动实现的。

LDE 协议的两阶段实现时间线

受限直接执行时间序列图

受限」的体现之二,在于用户程序必须以某种方式,主动或被动地让出 CPU 的控制权,好让操作系统可以执行程序切换,或处理其他事情。

  • 合作式 (cooperative) :等待系统调用

操作系统会假设用户程序会合理的使用资源,并在结束执行后通过系统调用(比如 exit() )将 CPU 控制权交还给操作系统,以便其他程序也可以执行。

当程序运行出错时,比如除 0 或访问非法内存,程序执行也会陷入操作系统中。

  • 非合作式:操作系统进行控制

如果没有硬件帮助,在合作式的风格中,如果用户程序不做退出的系统调用,那么操作系统没有办法中止这个程序,无法执行其他程序。

对于程序不肯让出 CPU 控制权的问题,解决答案很简单:时钟中断 (timer interrupt),时钟设备编程为每隔一段时间发生一次中断,中断发生后,当前进程停止运行,转而执行操作系统中预制的中断处理程序 (interrupt handler),于是,操作系统重新获得 CPU 的控制权,于是可以做它想做的事情:比如停止不肯退出的程序。

时钟中断是操作系统获得 CPU 控制权的重要机制,因此操作系统启动时,要启动时钟设备,告知硬件时钟中断发生时处理程序所在位置。但是时钟设备也是可以关闭的,详见并行部分内容。

上下文切换

切换应用程序时,为了保证程序的正常执行,需要保存和恢复上下文,即 上下文切换 context switch

为了保存当前正在运行的进程的上下文,操作系统会执行一些底层汇编代码,来保存通用寄存器、程序计数器,以及当前正在运行的进程的内核栈指针,然后恢复通用寄存器、程序计数器,并切换内核栈,供即将运行的进程使用。

时钟中断切换进程时序图

时钟中断切换进程时序图,在过程中出现了两种类型的寄存器保存/恢复:

  1. 第一种发生在时钟中断的时候。在这种情况下,由硬件隐式地操作,将寄存器内容保存在内核栈中
  2. 第二种发生在进程上下文切换的时候。在这种情况下,由软件(即操作系统)显式调用,将寄存器内容保存在进程结构的内存中

小结

受限直接执行 LDE 协议是实现 CPU 虚拟化的关键底层机制。基本思路很简单:允许你想运行的程序在 CPU 上直接运行,但首先确保设置好硬件,以便在操作系统无法运行的情况下限制进程可以执行的操作。

重启是优雅而有用的,因为

  • 将软件重置到经过很多测试的、最为预期的状态
  • 可以回收旧的或泄漏的资源(如内存)
  • 重启容易自动化