1.1.4 虚拟化的实现
最早实现CPU完全虚拟化的技术是陷入-仿真模型。而对于不支持陷入-仿真模型的CPU ISA(比如X86 32位指令集)来说,一方面可以通过二进制翻译技术和半虚拟化技术实现虚拟化;另一方面也可以对不能虚拟化的CPU ISA进行扩展,引入新的指令和运行模式,来支持陷入-仿真模型,这种方式一般称为硬件虚拟化技术。接下来分别介绍这3种技术。
1.二进制翻译技术
二进制翻译(Binary Translation,BT)技术(见图1-7)由VMware公司于1998年首次应用在面向PC(Persoual Computer,个人计算机)的Hypervisor产品VMware Workstation当中。VMware Workstation首先扫描操作系统内核二进制文件的代码段,以确定基本块。所谓基本块,是指程序顺序执行的语句序列。根据定义,除了最后一条指令,基本块内不会含有其他改变程序计数器(Program Counter,PC)的指令。然后VMware Workstation会检查基本块中是否含有敏感指令。如果有敏感指令,则每条敏感指令都会被替换成VMware Workstation的过程调用。与此同时,基本块的最后一条指令也会被VMware Workstation的过程调用替代。基本块执行结束后,CPU控制权会再次返回VMware Workstation,并扫描下一个基本块的位置。一般一个基本块需要依次进行翻译、缓存、执行等操作。如果基本块已经被翻译完毕并缓存,就可以被立刻执行。待所有基本块都被翻译完成后,系统内大多数程序都将被缓存并且被接近全速执行。如果没有敏感指令,那么基本块和用户应用程序可以直接在硬件上运行。
图1-7 采用二进制翻译技术虚拟化X86平台
如果使用X86 32位处理器(该处理器不支持虚拟化),并且客户操作系统无法获取源码(如Windows系统),那么二进制翻译技术将是唯一的全虚拟化解决方案。为弥补X86处理器的虚拟化缺陷,在市场需求的驱动下,Intel和AMD分别推出了基于X86架构的硬件辅助虚拟化技术Intel VT-x和AMD-V(AMD Virtualization,AMD虚拟化)。自此之后,二进制翻译成为一项过渡技术。随着基于X86架构的硬件虚拟化技术的广泛应用,二进制翻译逐渐退出了历史舞台。VMware公司在2017年发布的VMware vSphere 6.5是基于二进制翻译技术的最后一个Hypervisor版本。
2.硬件虚拟化技术
Intel VT-x和AMD-V的核心思想都是通过引入新的指令和运行模式,使Hypervisor和客户操作系统分别运行在不同模式(root模式和非root模式)下,且客户操作系统运行在Ring 0下。通常情况下,客户操作系统的核心指令可以直接下达给计算机系统硬件执行,而不需要经过Hypervisor处理。当客户操作系统执行到敏感指令时会触发陷阱,自动陷入root模式,让Hypervisor截获并处理这条特权指令。这种对现有指令集进行扩展的虚拟化技术称为HVM(硬件虚拟化技术),如图1-8所示。
图1-8 X86上的硬件虚拟化技术
采用硬件虚拟化技术的Hypervisor,客户操作系统不需要经过修改。但是这类Hyper-visor在性能上会有所降低,因为使用虚拟化技术的硬件采用陷入-仿真的方式,引入了太多的陷入操作。而在现代CPU硬件平台上,陷入的代价是昂贵的,会清空处理器的缓存、TBL和分支预测表。
3.半虚拟化技术
对客户操作系统来说,半虚拟化技术(Para-Virtualization,PV)类似应用程序通过操作系统的系统调用获取操作系统的内核服务。当采用这种方法时,Hypervisor必须定义由过程调用集合组成的API供客户操作系统使用,这类API集合一般称为Hypercall API。
Hypervisor精确仿真复杂指令的语义是一件耗时的工作,而客户操作系统直接调用Hypercall API去完成I/O任务,比精确仿真每条敏感指令更有优势。半虚拟化技术用于X86平台的虚拟化方案如图1-9所示。
二进制翻译技术和硬件虚拟化技术本质上都是模拟完整的计算机敏感指令集,主要原因是操作系统的源码不可获取(比如Windows)或者源码种类多样(比如Linux)。当然理想的情况是Hypercall API可以标准化,而后续的操作系统都调用该Hypercall API接口,而不是执行敏感指令,这样的做法将会使得虚拟化技术更容易被支持和使用。另外,我们可以在支持虚拟化技术的硬件平台上同时采用完全虚拟化技术和半虚拟技术来实现Hypervisor,这样可以充分发挥两种虚拟化技术的优势,如图1-10所示。
图1-9 半虚拟化技术用于X86平台的虚拟化方案
图1-10 支持完全虚拟化和半虚拟化的Hypervisor
在图1-10中,在支持虚拟化技术的硬件平台上,左侧是一个没有经过修改的Windows系统,执行敏感指令时硬件陷入Hypervisor,Hypervisor通过仿真来执行这条敏感指令的精确语义并返回。右边是一个经过半虚拟化修改的Linux内核版本,其中不含有敏感指令。当需要进行I/O操作或者修改重要内部寄存器(比如指向页表的寄存器)时,Linux内核会调用Hypervisor中的程序来完成操作。就像在标准Linux系统中,应用程序通过int指令实现系统调用获取Linux内核服务一样。
Hypervisor可以同时实现HVM和PV。当Windows在支持HVM的Hypervisor上运行时,Hypervisor将解释陷入的敏感指令;当客户Linux系统在支持HVM的Hypervisor上运行时,半虚拟化后的Linux会直接执行Hypercall API,而不需要对敏感指令进行仿真。这时的Hypervisor在某种程度上相当于一个微内核架构的操作系统(只执行最基本的服务,比如内存管理、中断管理等),代码短小精练,以特权模式运行在硬件上,大幅提升了系统的可靠性。
提示:这里介绍的Hypervisor通常是Ⅰ型Hypervisor,Ⅱ型Hypervisor会在1.2节详细讨论。
对客户操作系统进行半虚拟化也存在一些问题。第一,如果所有的敏感指令都被Hypervisor中的程序所替代,硬件不可能理解Hypervisor的内部程序,那么虚拟化的操作系统将无法再次在原生物理机器上运行;第二,市场上的Hypervisor(比如VMware Workstation、Xen、KVM(Kernel-based Virtual Machine,基于内核的虚拟机)等)Hypercall API的标准不同,导致修改后的内核无法在所有的Hypervisor上运行。对此,目前一个可取的解决方案是使用VMI(Virtual Machine Interface,虚拟机接口)来处理内核对敏感操作的执行和对Hypercall API的调用。VMI主要用于底层与硬件、底层与Hypervisor的交互,如图1-11所示。
图1-11 VMI Linux运行在裸机、VMware Hypervisor、Xen Hypervisor上
图1-11所示是一个半虚拟化的Linux版本,称为VMI Linux。VMI Linux直接运行在原生硬件上时,会链接到通过发射敏感指令来完成工作的函数库,如图1-11a所示;当运行在Hypervisor(VMware Workstation或者Xen)上,则会链接到另外一个函数库,该函数库提供对下层Hypervisor的超级调用程序的封装,如图1-11b和图1-11c所示。通过在不同场景下链接不同函数库这一方式,操作系统内核保持了可移植性和高效性,可以适配不同的Hypervisor。