操作系统 CPU 虚拟化

本文将介绍操作系统对 CPU 的虚拟化。

一、进程说明

1. 什么是进程?

简单来说,进程就是运行中的程序。

2. 进程如何创建?

  • 加载:将代码和所有静态数据(例如初始化变量)加载到内存中

    在早期操作系统中,”加载” 操作会在程序运行前尽早完成;

    在现代操作系统中,会进行惰性 “加载”,只 “加载” 程序执行期间需要使用到的代码和数据

  • 分配栈:分配内存,以便作为程序的栈

    使用栈存放局部变量、函数参数、返回地址

    若变量是对象,只存储引用

  • 分配堆:分配内存,以便作为程序的堆

    使用堆存放对象本身

  • I/O 初始化:操作系统还将执行一些 I/O 初始化任务

    在 UNIX 系统中,默认情况下,每个进程都有三个打开的文件描述符,用于标准输入、输出、错误

3. 进程状态

进程可以处于以下三种状态之一:

  • 运行(running):进程正在处理器上运行
  • 就绪(ready):进程已经准备好运行
  • 阻塞(blocked):进程执行了某种操作进入阻塞,知道某种时间发生时才会准备运行

二、进程权限

1. 主要问题

为了保证操作系统的安全性,不应该完全信任进程,因此应该对进程能做的事情做限制。

2. 用户模式与内核模式

  • 用户模式:

    • 进程将以这种模式运行

    • 执行的操作将会受到限制

      例如用户模式下无法发起 I/O 请求

  • 内核模式

    • 操作系统以这种模式运行
    • 此模式下拥有所有权限,可以执行任意操作

3. 系统调用

如果进程希望执行某些受限操作,操作系统会为它们提供渠道,即系统调用。

凡是与系统态级别的资源有关的操作(如文件管理、进程控制、内存管理等),都必须通过系统调用方式向操作系统提出服务请求,并由操作系统代为完成。

通常来说,程序会执行特殊的陷阱(trap)指令,该指令将控制转移到预先指定的陷阱处理程序(操作系统),并将权限级别提升为内核模式,自此,操作系统可以帮助进程完成其所需工作。完成后,操作系统调用一个从陷阱中返回(return-from-trap)指令,返回到用户程序中,同时将权限级别降低为用户模式。

三、进程协作方式

1. 主要问题

为了虚拟化 CPU,操作系统需要以某种方式让许多任务共享物理 CPU,让它们看起来像是在同时运行。其基本思想很简单:运行一个进程一段时间,然后运行另一个进程,如此循环往复。

如果要这么做,首先要解决的问题是控制权,操作系统要保留对 CPU 的控制权,以便将 CPU 运算能力分配给不同进程。

2. 协作方式

(1) 协作方式:被动等待

早期系统采用协作的方式,操作系统相信进程都是善意的,认为它们会定期放弃 CPU 控制权,以便操作系统重新获取。

“协作方式:被动等待” 过于被动,如果有 “恶意 / 缺陷” 进程(不放弃 CPU 控制权)持续运行,将导致操作系统永远无法接管 CPU。

(2) 非协作方式:操作系统主导

这种做法的实现核心是时钟中断:时钟设备可以编程为每隔几毫秒产生一次中断。产生中断时,正在运行的进程停止,操作系统中预先配置的中断处理程序会运行。操作系统可以因此重新获得 CPU 的控制权,从而把控进程的调度。

四、上下文切换

在进行进程切换时,操作系统应该做上下文切换。

  • 在进行进程切换时,应该将上下文保存
  • 在进程切换回来时,应该将上下文恢复

五、进程调度

1. 主要问题

操作系统需要考虑进程调度问题:假如同时有数个任务,应该如何分配 CPU 的使用权。

2. 常用概念

(1) 周转时间

周转时间:任务创建时间 -> 任务完成时间

(2) 响应时间

响应时间:任务创建时间 -> 任务首次执行时间

3. 调度方式 - 先进先出

一个最简单的做法是先进先出,将所有任务按照先来后到的顺序依次执行。

先进先出的做法在实际表现中不佳,一个执行所需时间较长的任务将会影响后续任务的 “完成时间”,因为后续任务都需要等待它执行完成。这种问题被称为 “护航效应”,一些耗时较少的资源消费者被排在重量级资源消费者之后,导致它们需要进行较长的等待。

4. 调度方式 - 最短完成时间优先

将任务按 “完成时间” 排序,从小到大依次完成。

5. 调度方式 - 最短剩余时间优先

将任务按照 “剩余时间” 排序,从小到大依次完成,如果新增了 “剩余时间” 更短的新任务,则执行新任务。

6. 调度方式 - 轮转

以时间片为单位,每个时间片轮流运行队列中的每一个任务。

这种做法的好处是响应时间较短。

7. 调度方式 - 结合 I/O

操作系统可以在某个进程等待 I/O 操作时,切换运行其它进程,以避免 CPU 闲置。

8. 调度方式 - 多级反馈队列

(1) 基本概念

  • 多级反馈队列中有许多独立的队列
  • 在任一时刻,一个工作只能存在于一个队列中
  • 每个队列有不同的优先级,较高优先级的队列将会被优先执行
  • 队列中的任务将会被轮转调度以执行

(2) 优先级计算

  • 任务刚创建,首先作为最高优先级

  • 一旦任务使用完时间配额后,降低优先级

    时间的计算通过累加的方式,即累加每次任务获取到 CPU 的时间

  • 每经过一段时间,将所有任务都重新加入最高优先级队列

    主要考虑这样两个问题:

    • 低优先级的任务可能会长时间无法获取到 CPU
    • 同一个任务,可能在不同时间表现不同,一个 CPU 密集的进程可能在某个时间段表现为 I/O 密集,应该在它表现为 I/O 密集时给予更多的优先级

9. 调度方式 - 比例份额

(1) 基本概念

核心目标:以确保每个任务获得一定比例的 CPU 时间优先,而不以优化周转时间、响应时间优先。

(2) 实现 - 彩票调度

整个系统有若干张彩票,每个进程可以拥有一些彩票,进程的彩票数就代表着进程应该占有 CPU 的多少。

操作系统会在每个时间片随机抽取彩票号码,并让 CPU 运行拥有这个彩票的任务。

通过这种方式,可以让每个任务都 “近似” 拥有对应份额的 CPU。

(3) 实现 - 步长调度

略。

(4) 说明

比例份额并没有被广泛使用,原因是:

  • 彩票调度和步长调度两种实现方式都不能很好地应对 I/O 密集型工作

  • 比例的分配并没有一个较好的方案

    无法通过某种方式确定一个进程应该占据多少比例

参考

  • 操作系统导论