Skip to content

06 Virtual Machines

虚拟机(virtual machines, VM)最早在 1960 年代中开发,一段时间在大型主机是重要部分。1980 年代和 1990 年代是个人用户 PC 时代,虚拟机受到了忽视,不过最近又流行了起来,因为:

  1. 现代系统中隔离和安全的重要性增加了
  2. 标准操作系统的安全和可靠性故障
  3. 很多不相关用户共享单个计算机,特别是云计算
  4. 处理器性能大增,弥补了虚拟机的额外开销

最广义的虚拟机定义包括所有提供了标准软件接口的模拟,比如 Java VM。这一节,我们讨论的是在二进制指令集(instruction set architecture, ISA)层级提供完整的系统环境。虽然有的虚拟机可以在虚拟机中运行与硬件不同的指令集,但这里假设指令集是一致的。这样的 VM 称为系统虚拟机(System Virtual Machines),比如 IBM VM/730,VirtualBox,VMware ESX,Xen 等。

系统虚拟机让用户感觉自己在使用整个计算机。一个计算机上运行多个虚拟机可以同时支持不同的操作系统。在传统平台,单个操作系统拥有所有硬件资源,而有多个虚拟机的话,它们共享硬件资源。

支持虚拟机的软件称为 VMM(virtual machine monitor)或 hypervisor,VMM 是虚拟机技术的核心。硬件平台称为宿主机(host),其资源由多个虚拟机共享。VMM 决定如何将将虚拟机资源映射到物理资源:时分复用;分段;甚至软件模拟。VMM 比传统操作系统小很多,VMM 隔离部分的代码通常只有上万行。

尽管这里感兴趣的点是安全防护,不过虚拟机也有两个其他商业优势。

  1. 管理软件。虚拟机提供了一种抽象能力,能够完整的运行软件栈,包括像 DOS 这样古老的操作系统。一种典型的部署是部署一些旧的操作系统,大部分部署当前稳定的操作系统,一小部分部署下一代测试操作系统。
  2. 管理软件。使用多个服务器的原因之一是可以运行与操作系统兼容的软件版本,这样隔离的话可以提高可靠性。虚拟机提供完整的、独立的运行环境,可以节省物理服务器的数量。一些 VMM 支持移动运行的虚拟机到不同机器,这对于负载均衡和故障迁移有助益。

一般来说,处理器的虚拟化依赖于工作负载。用户态处理器密集型程序零虚拟化开销,因为操作系统很少被调用到。IO 密集型往往是操作系统密集型负载,执行很多系统调用和特权执行,这些会导致高虚拟化开销。另一方面,如果 IO 密集型也是 IO 限制型的话,处理器虚拟化的开销会被掩盖,因为处理器往往闲置在等待 IO。

开销依赖于 VMM 模拟指令的数量和这些指令需要的时长。当虚拟机和 ISA 指令集相同时,架构和 VMM 的目标是直接在本机硬件直接运行所有指令。

Requirements of a Virtual Machine Monitor

VMM 是虚拟机软件接口,需要隔离多个虚拟机上的软件,还需要将自身与这些软件(包含虚拟机上的操作系统)相隔离。定性需求有:

  1. 虚拟机上的软件行为应该和直接运行在本地硬件上的一样,例外是性能和一些与多个虚拟机共享的有限资源相关的行为。
  2. 虚拟机上的软件不能直接修改真实的系统资源。

为了虚拟化处理器,VMM 必须能够控制一切——对特权状态、IO,异常、中断的访问,即使是当前运行的虚拟机和操作系统正在访问它们。

比如对于计时器中断,VMM 应该将正在运行的虚拟机挂起,保存状态,处理中断,决定哪一个虚拟机可以继续执行,然后加载状态。由 VMM 给依赖于计时器中断的虚拟机提供虚拟计时器和模拟中断。

为了达到这个目的,VMM 必须拥有比虚拟机更好的特权级别,而虚拟机通常运行在用户态,同时确保任何特权指令的执行由 VMM 处理。为了支持 VMM,系统需要

  1. 至少两个处理器模态——系统和用户。
  2. 特权指令集(指令集的子集)必须只能在系统态下运行,在用户态下执行会进入陷阱,同时所有的系统资源都必须由这些特权指令控制。

(Lack of) Instruction Set Architecture Support for Virtual Machines

如果在设计指令集的时候就考虑到了虚拟机,那么很容易可以减少 VMM 执行的指令数量并且能够模拟速度。允许虚拟机直接在硬件上执行的架构称为可虚拟化(virtualizable),IBM 370 和 RISC-V 就是这样的架构。

由于虚拟机最近才被用于 PC 和服务器,大多数指令集设计的时候都没有考虑虚拟化这个问题,包括 x86 和大部分 RISC 架构(ARMv7 和 MIPS)。

VMM 必须确保虚拟机系统只能和虚拟资源交互,那么虚拟机上的操作系统运行在 VMM 之上的用户态。如果虚拟机操作系统试图通过特权指令访问或修改硬件信息,比如读写开启中断的指令,会陷入 VMM 中,接着 VMM 修改相应的真实资源。

如果任意试图读写这些敏感信息的指令运行在用户态,VMM 中断这些指令返回一个操作系统期望的虚拟的敏感信息。

如果没有这些支持,就必须采取其他措施。VMM 必须小心的处理这些指令确保在虚拟机操作系统上执行的时候行为正确,这会增加 VMM 的复杂性,降低运行的虚拟机的性能。

Protection and Instruction Set Architecture

防护(protection)是由架构和操作系统共同努力的结果,不过当虚拟内存越来越流行,架构设计者必须修改现有指令集的一些令人尴尬的细节。

比如 x86 指令 POPF 加载从内存栈顶加载标志寄存器。其中一个标志是中断启用(interrupt enable, IE)标志。如果在用户态运行这个指令,不会进入陷阱而是修改除了 IE 之外的所有标志。如果在系统态运行,那么就是修改 IE。由于虚拟机操作系统运行在用户态,这就是一个问题了,因为期待看到的是 IE 被修改。

IBM 大型机和 VMM 通过下面三步提升虚拟机的性能。

  1. 减少处理器虚拟化的开销。
  2. 减少由于虚拟化导致的中断的额外开销。
  3. 通过将中断转移到正确的虚拟机而不调用 VMM 以减少中断开销。

AMD 和 Intel 从 2006 年开始尝试第一点。不知道需要多久,多个代的迭代,21 世纪的 x86 虚拟机才能和上世纪 70 年代的 IBM 大型机虚拟机一样高效。