虚拟化技术在概念上与仿真类似。仿真技术可以在一个平台上构建出另外一个平台 (比如在X86平台的Linux系统中通过QEMU仿真ARM64平台),虚拟化技术可以在一个平 台上构建一个或者多个相同结构的平台。现代操作系统通常都包含一个简化的虚拟化系统,用于虚拟化CPU和内存。CPU虚拟化技术使得每个正在运行的进程表现得好像它是唯一正在运行的进程。如果一个进程试图消耗所有的 CPU 资源,操作系统将抢占这个进程的 CPU资源并允许其他进程公平分享。同样,内存虚拟化技术使得正在运行的进程通常有自己的虚拟地址空间,操作系统将虚拟地址空间映射到具体的物理内存,让该进程产生一种错觉,认为它是整个物理内存的唯一用户。
硬件设备通常也会被操作系统虚拟化。进程通常使用伯克利套接字来访问网络设备,而无须担心其他应用程序的干扰。操作系统的图形用户界面,比如GNOME(GNU Network Object Model Environment,GNU网络对象模型环境)、KDE(K Desktop Environment ,K桌面环境)或终端模拟器(比如Linux Terminal)复用屏幕和输入设备,使得用户感觉好像他们在独占整个计算机系统。
坦白地说,我们每天都有可能触及虚拟化技术,享受虚拟化带来的好处。虚拟化所提供的隔离性通常可以防止一个系统的Bug或恶意行为破坏其他系统的正常运行。
虽然系统中的操作系统组件为应用程序提供了一定程度的隔离性,但如果操作系统组 件包含Bug,这种隔离性就会被打破。而Hypervisor可以为应用程序提供完全的隔离,基于Hypervisor 构建的系统也会比原生操作系统具备更强的隔离性。Hypervisor实现强隔离性的关键是虚拟化技术,包含CPU 虚拟化和外围设备I/O虚拟化。
1.1.1 CPU虚拟化
1. 虚拟化技术概述
对直接运行在硬件上的操作系统(见图 1-1a)而言, CPU 的虚拟化就是对CPU资源的分时复用。vCPU本质上是一小段在 pCPU( Physical CPU,物理 CPU)运行的时间片。当一个进程独占CPU 运行一段时间被中断后,操作系统会保存当前进程的上下文,然后恢复另一个进程的上下文,一段时间后重复类似操作。上述过程在主流操作系统(比如Linux)中一般每10ms发生一次。
提示:此时的 vCPU 和 pCPU虽然概念上是类似的,但实现上并不相同。 vCPU 包含虚 拟用户模式和虚拟特权模式,pCPU 包含原生用户模式和原生特权模式。vCPU 的虚拟 特权模式或由 Hypervisor 模拟实现,或由vCPU 和 Hypervisor 协作实现。
当部署Hypervisor 时,如图1-1 b所示, Hypervisor运行在pCPU的特权模式下,原本直接运行在原生硬件上的操作系统改变为运行在Hypervisor 创建的虚拟机中,并且降级到用户模式运行。此时 Hypervisor 需要解决的问题不仅是pCPU的分时复用,还涉及映射到虚拟机中的vCPU特权指令的模拟问题。因为虚拟机中的操作系统仍然会执行它所认为的特权指令,这才是CPU虚拟化所要解决的关键问题。
图 1-1 传统操作系统模式和 Hypervisor 运行模式
为了能更清楚地描述 CPU 的虚拟化问题,计算机科学领域的著名学者杰拉尔德J. 波普克(Gerald J. Popek)和罗伯特 P. 戈德堡(Robert P. Goldberg)在 1974 年发表的论文《第三代可虚拟化架构的正式要求》中引入了特权指令和敏感指令的概念,并将CPU的指令集分为 3 类:特权指令、控制敏感指令和行为敏感指令。其中,控制敏感指令是指试图修改系统中资源配置状态的指令,包括更新虚拟地址到物理地址的映射、修改与设备通信或使用系统全局配置相关的寄存器指令等;行为敏感指令的行为或结果取决于系统的资源配置状态,例如对虚拟内存进行加载和存储操作的指令就属于行为敏感指令。
这些概念的引入对虚拟化和操作系统的设计非常重要,这 3 类指令的拦截和模拟是虚 拟化技术实现的核心。这篇论文为虚拟化技术的发展奠定了基础,并对后续的研究和实践产生了深远影响。
CPU 可虚拟化的充分条件是所有敏感指令(包括控制敏感指令和行为敏感指令)必须 是特权指令集合的子集。Hypervisor利用特权指令实现对敏感指令的拦截和处理,以控制虚 拟机对底层硬件的访问,实现对虚拟机的隔离和资源分配。Hypervisor 负责解释敏感指令或 修改其行为,或将敏感指令传递给底层物理硬件进行处理。这样的机制允许Hypervisor 实 现虚拟机的隔离、资源调度、内存虚拟化和设备模拟等功能。
2. 虚拟化技术的类型
从客户操作系统的角度来说, CPU虚拟化行为可以分成两类。
(1)完全模拟原生
pCPU 的行为完全模拟 pCPU 的行为,客户操作系统将完全不需要修改就可以直接在虚拟机中运行, 就像运行在原生物理设备上一样。因此客户操作系统感知不到Hypervisor 的存在。
(2)非完全模拟原生 pCPU 的行为
非完全模拟原生 pCPU 的行为需要客户操作系统和 Hypervisor相互配合来实现客户操 作系统的服务,因此客户操作系统可以感知到 Hypervisor 的存在。
3. 虚拟化技术的原理
具体采用哪一种虚拟化技术实现 Hypervisor,取决于 CPU ISA(Instruction Set Architecture,指令集架构)和具体的应用需求。
(1)可虚拟化的 ISA
如果所有的敏感指令都是特权指令,根据波普克和戈德堡的理论,该 CPU ISA可通过 陷入-仿真模型进行完全虚拟化,如图 1-2所示。支持陷入-仿真模型的ISA有ARMv8、 RISC-V、MIPS、PowerPC、SPARC指令集。
陷入- 仿真是一种处理虚拟机与 Hypervisor 之间交互的技术,它将敏感指令陷入到 Hypervisor 中,然 后由 Hypervisor 仿真这些指令的执行。
图1-2 基于陷入- 仿真模型的完全虚拟化技术
在虚拟化实现中, Hypervisor 运行在特权模式,具有对硬件资源的完全控制权,因而可以在所有虚拟机间共享硬件平台。所有虚拟机运行在用户模式下,而虚拟机中的 vCPU 期 望能够访问所有硬件资源,因此 Hypervisor 必须提供一种间接的机制来实现 vCPU 期望的 所有功能。在用户模式下执行特权指令会产生一条陷阱异常信息,Hypervisor会利用这个特 征来捕获虚拟机试图执行的特权指令,并精确地模拟该特权指令的原有功能。
如图1-2所示,如果异常触发的原因是vCPU在虚拟特权模式下执行特权指令,则该条特权指令会被 Hypervisor模拟;如果异常是由于 vCPU 处于虚拟用户模式下非法执行特权指令而触发(比如除零异常),则该异常会被 Hypervisor 转发给当前虚拟机,并在 vCP中触发一个可编程异常。也就是说,如果pCPU处于虚拟用户模式,但是vCPU 处于虚拟特权模式,那么需要给vCPU 创造一种幻象,即vCPU运行在自己期望的特权模式中,并且每一条特权指令在 Hypervisor中都会有一个对应的模拟程序。当 Hypervisor需要模拟某条特权指令时,对应的程序将会被调用。总之,无论是 Hypervisor 模拟特权指令还是将异常转发给虚拟机, Hypervisor最终都会恢复vCPU 的上下文,以使当前虚拟机继续执行。
(2)不可虚拟化的 ISA
如果某敏感指令不是特权指令,那么该敏感指令在虚拟用户模式下执行时不会触发陷阱,因而无法被运行在虚拟特权模式下的系统软件所捕获,所以不能通过陷入-仿真模型进行完全虚拟化。例如,在没有引入硬件虚拟化扩展的 X86 32 位指令集中,某些敏感指令在虚拟用户模式下执行时会被忽略,而不会触发陷阱异常。以 X86的POPF 指令为例,该指令用于替换标志寄存器的值,会改变允许/禁止中断的标志位。但是在虚拟用户模式下执行这条指令时,这个标志位不会被改变,也不会触发陷阱。因此,X86 32 位ISA是不能通过陷入-仿真模型进行虚拟化的ISA。但是我们可以通过特殊的工程技术来实现pCPU虚拟化(详情见1.1.4 小节)。
讨论了CPU的虚拟化后,下面来研究一下I/O的虚拟化。
1.1.2 I/O 虚拟化
客户操作系统在启动的时候会检测硬件,以找出当前系统中连接的所有 I/O 设备。这 些检测操作都会陷入Hypervisor中。那么Hypervisor如何处理这些设备的 I/O 请求呢?通常有两种解决方案。
第一种方案:将设备的管理功能集成到 Hypervisor 中,作为 Hypervisor 的一个子系统,由Hypervisor根据这些 I/O 请求操作对应的 I/O 设备,并将结果反馈给虚拟机中的客户操作系统。这一方案的弊端在于使得 Hypervisor 的代码量和复杂度急剧增加,从而很难保证 Hypervisor 的安全性和高效率,故通常只将很少的驱动集成到 Hypervisor中,比如控制台、串口驱动。
第二种方案:将所有 I/O设备分配给一个虚拟机,其他虚拟机的 I/O 请求全部发送给 这个虚拟机处理。这种方案具有极大的灵活性,可以利用现有客户操作系统中的驱动程序。
如果设备本身就是一个独占设备,那么它至多被分配给一个虚拟机。但是如果设备需要在 不同的虚拟机间共享,则需要额外的管理方法。在操作系统的设备驱动中,只要对该设备 用互斥锁则可保证 I/O 操作的原子性。但是在 Hypervisor 的设计中使用互斥锁会破坏不同 虚拟机间的隔离性。
因此 I/O 设备虚拟化最合适的方法就是创建相互隔离的安全虚拟机(见图 1-3),让虚 拟机独占该设备,并为每一个申请访问它的应用虚拟机提供服务。每个应用虚拟机通过这 种模型向 I/O 虚拟机和人机交互虚拟机发送请求来访问真实的设备。而在虚拟设备的底层, Hypervisor 需要提供必要的机制(共享内存通道)来传递这种请求,以保证虚拟机间的隔离性。
图 1-3 I/O 设备虚拟化模型
1.1.3 为什么需要虚拟化技术
虚拟化技术主要有以下几点优势。
1)Hypervisor 可以在同一物理设备上并行运行多种操作系统。例如,在基于Hypervisor 构建的虚拟平台上同时运行提供实时任务的 RTOS(如 VxWorks 、μC/OS-Ⅱ等)和提供非实时任务的GPOS(如Linux或Windows),既解决了GPOS 实时性方面的不足,又解决了RTOS应用不够丰富的劣势,如图1-4所示。
2)通过虚拟化技术可以把不同子系统封装到不同的虚拟机中。比如,驱动程序、网络 协议栈或者文件系统等内核组件可以直接运行在某个虚拟机上,其他子系统可以共享这些 组件,这大大提高了系统的安全性和代码的复用率。即使其中一个子系统崩溃或者被攻击, 也不会影响其他子系统。
图 1-4 典型 Hypervisor 示意图
对于现代嵌入式操作系统来说,代码量越来越庞大,存在的安全隐患也越来越多。比如,缓冲区溢出攻击就是一种常见的网络攻击手段,原理是利用用户程序对缓冲区的超界 访问,访问系统关键数据和程序,进而窃取系统的控制权。在没有使用 Hypervisor 架构的 系统(见图 1-5a)中,缓冲区溢出攻击一旦成功,整个操作系统将暴露在入侵者面前,入侵 者将能完全控制整个系统资源,访问所有的关键模块。而在使用了 Hypervisor 架构的系统 (见图 1-5b)中,即使用户端的交互应用被入侵,导致其所在的操作系统被劫持,也不会致 使运行在 Hypervisor 上的其他客户操作系统被控制,即把系统被入侵的损害降到了最低。
图 1-5 缓冲区溢出攻击
3)Linux作为主流GPOS的优点是使用免费,且有一个庞大的开源社区支持。
Linux 遵照GPL( GNU General Public License,GNU 通 用公共许可协议),要求任何由Linux衍生出来的代 码都要遵照同样的许可发布,也就意味着开源。这对商业开发者来讲是一个两难的选择,既希望使用免费的系统,又需要保护商业机密。通过Hypervisor可以实现许可的隔离,如图1-6 所示。
4)虚拟化技术实现了软件和硬件的松散耦合,使得客户操作系统只需要做极少的改动就可以 移植到一个新的平台。虚拟化技术还可以为客户操 作系统提供稳定的运行环境,特别是维护周期长的系统,可以不受硬件平台限制而得到较长时间的维 护。采用Hypervisor方案,看起来像是把所有的 鸡蛋放在同一个篮子里。如果运行所有虚拟机的Hypervisor崩溃了,其结果可能比单独一台专用服务器的崩溃要严重得多。然而大多数服务器停机的根源不在于硬件故障,而主要在于臃肿、不可靠、有漏洞的软件,特别是操作系统。使用Hypervisor 方案,可以让运行在特权模式下的软件仅有Hypervisor,其代码量比一个完整操作系统的代码量低两个数量级,也就意味着漏洞的数量也低两个数量级,风险系数大大降低。
除了松散耦合带来的强大隔离性,采用Hypervisor方案还有其他的好处。其一是减少 物理机器的数量,占用空间更少,节省了硬件与电源的开支。其二是可以设置备份点,虚 拟机的迁移仅需移动内存映像和主要保存在操作系统中的进程关键状态信息(包括与打开文件、警报、信号处理函数等相关的信息),这使得虚拟机的迁移比在普通操作系统中进行进程的迁移要容易得多。