虚拟化技术——cpu虚拟化

一、背景

1.cpu虚拟化是什么

物理机是CPU,内存和I/O设备等一组资源构成的实体。虚拟机也一样,它由虚拟CPU,虚拟内存和虚拟I/O设备等组成。VMM(VM Monitor)按照与传统OS并发执行用户进程的相似方式,仲裁对所有共享资源的访问。

在虚拟化的平台上,虚拟机(guest VM)所使用的多个虚拟CPU(简称vCPU)可能是共享同一个物理CPU(简称pCPU)的。VMM负责vCPU的调度,当一个vCPU获得pCPU的使用权后,基于该vCPU运行的guest OS就可以调度OS中的各个线程/进程。也就是说,guest OS中的各个线程/进程分时复用了vCPU,而各个vCPU又分时复用了pCPU。

如上图所示,通过CPU虚拟化技术可以在一台物理计算机上创建和运行多个虚拟机(Virtual Machine, VM)。具体而言,CPU虚拟化技术通过VMM来实现。VMM是一种软件或硬件实体,它负责在物理计算机上创建和管理虚拟机。它在物理计算机的处理器上运行,拦截并模拟虚拟机对处理器的访问,以便提供虚拟机所需的计算资源。 当虚拟机运行时,CPU虚拟化技术通过虚拟化处理器指令集、内存管理单元(Memory Management Unit, MMU)以及设备访问等方面的技术,将虚拟机对处理器、内存和其他硬件资源的请求转化为对物理计算机上相应资源的访问。

2.cpu虚拟化有什么优势

CPU虚拟化技术具有一系列优点和缺点,以下是具体的内容:

优点:

  1. 提高资源利用率:CPU虚拟化技术可以将物理CPU资源虚拟化成多个虚拟CPU,供多个虚拟机同时使用,从而提高了CPU的利用率。
  2. 简化管理:通过虚拟化技术,可以将多个虚拟机集中管理,实现资源的统一分配和调度,简化了IT基础设施的管理和维护工作。
  3. 提高灵活性:虚拟化技术允许在同一台物理服务器上运行多个不同操作系统和应用程序的虚拟机,从而提高了系统的灵活性和可扩展性。
  4. 节省成本:通过虚拟化技术,可以充分利用现有硬件资源,减少物理服务器的数量,从而降低IT基础设施的采购成本和维护成本。
  5. 提高安全性:每个虚拟机都运行在一个独立的虚拟环境中,相互之间是隔离的,这有助于提高系统的安全性。即使一个虚拟机受到攻击或崩溃,也不会影响其他虚拟机的正常运行。

缺点:

  1. 性能开销:虚拟化技术需要额外的软件层(如虚拟机监视器)来管理虚拟机,这可能会引入一定的性能开销。特别是在处理大量并发请求或运行资源密集型任务时,虚拟化技术的性能可能会受到影响。
  2. 资源争用:当多个虚拟机同时请求CPU资源时,可能会出现资源争用的情况。这可能导致某些虚拟机的性能下降或响应时间延长。
  3. 复杂性增加:虚拟化技术增加了系统的复杂性,包括虚拟机配置、网络设置、存储管理等方面。这可能需要额外的专业知识和技能来维护和管理虚拟化环境。
  4. 安全风险:虽然虚拟化技术可以提高系统的安全性,但也带来了新的安全风险。例如,虚拟机之间的隔离可能不是完美的,可能存在潜在的安全漏洞。此外,虚拟化环境中的网络安全和数据保护也需要特别注意。
  5. 依赖硬件支持:CPU虚拟化技术需要硬件的支持才能实现最佳性能。如果硬件不支持虚拟化技术或支持程度有限,可能会影响虚拟机的性能和稳定性。

CPU虚拟化技术具有许多优点,但也存在一些缺点。在选择是否使用虚拟化技术时,需要根据实际需求和情况进行权衡和考虑。总的来说,CPU的虚拟化技术是一种非常重要的技术,它在提高计算机工作效率、推动云计算发展和提高安全性等方面发挥了重要作用。然而,也需要注意到它的挑战和限制,以便在实际应用中做出明智的选择和决策。

二、CPU的三种虚拟化机制

虚拟化基本类型有三类,分别是:全虚拟化(Full Virtualization)、半虚拟化(Paravirtualization)和基于硬件辅助的全虚拟化(Hardware-assisted virtualization)。对应的CPU虚拟化机制也就有三种,参考下图:

三种类型对比如下表:

虚拟化类型全虚拟化半虚拟化硬件辅助虚拟化
技术说明模拟了完整的底层硬件,包括处理器、内存、时钟、外设等,使得原始的操作系统或其他系统软件可以不做任何修改就可以运行。一种通过修改Guest OS部分访问特权状态的代码以便能直接与VMM交互的技术。在半虚拟化中,部分硬件接口以软件的形式提供给Guest OS,Guest OS可以通过Hypecall(Hypervisor提供给Guest OS直接调用的API接口)的方式来使用。就是在CPU、芯片组及I/O设备等硬件中加入专门针对虚拟化的支持
技术原理二进制翻译和直接执行Hypercall根据特权指令退出到 root 模式
Guest是否需要修改/兼容性Guest不需要修改,且具有优良的兼容性Guest需要修改,兼容性差;不适用windows osGuest不需要修改,且具有优良的兼容性
性能在某些情况下更好目前性能落后,但是未来将会得到提高
使用案例Vmware,Microsoft,ParallelsVmware,Microsoft,Parallels,XenVmware,Xen
优点Guest OS无需修改,速度和功能都非常不错,最重要的是使用非常简单架构更精简,而且与全虚拟化相比在整体速度上有一定的优势通过引入硬件技术,将使虚拟化技术更接近物理机的速度
缺点基于Hosted模式(和Hypervisor一样,是一种模拟硬件的程序)的全虚拟产品性能方面不是特别优异,特别是I/O方面需要对Guest OS进行修改,所以在用户体验方面比较麻烦,增加了产品开发过程的很多工作量现有的硬件实现不够优化或者当前支持的硬件不多,还有进一步提高的空间。
前景由于这种模式不仅使Guest OS免于修改,而且将通过引入硬件辅助虚拟化技术来提高其性能,在未来几年内全虚拟化应该还是主流在公有云(比如Amazon EC2)平台上占有一席之地,但是很难在其他方面和类似VMware vSphere这样的全虚拟化产品竞争,同时它也将会利用硬件辅助虚拟化技术来提高速度,并简化架构。因为通过使用硬件技术不仅能提高速度,而且能简化虚拟化技术的架构,所以硬件技术将会被大多数虚拟化产品所采用

接下来一个一个来分析下它们的原理和实现。

1.CPU的完全虚拟化

2001年,Fabrice Bellard 发布了目前最流行的,采用了动态二进制翻译(Binary Translation)技术的开源虚拟化软件 QEMU(Quick EMUlator)的第一个版本。在那个时候,虚拟化技术的共同目标就是将 x86 架构转变为通用的共享硬件基础架构,使应用程序运行环境在隔离性、移动性和操作系统类型方面都有选择的空间。

CPU 为了保证程序代码执行的安全性,多用户的独立性以及保证操作系统的稳定性,提出了CPU 执行状态的概念。它有效的限制了不同程序之间的数据访问能力,避免了非法的内存数据操作,同时也避免了应用程序错误操作计算机的物理设备。一般的,CPU 都会划分为用户态和内核态,而 x86 CPU更是细分为了Ring 0~3四种执行状态。 

  • Ring0 核心态(Kernel Mode):是操作系统内核的执行状态(运行模式),运行在核心态的代码,可以无限制的对系统内存、设备驱动程序、网卡接口、显卡接口等外部设备进行访问。

这里我们需要知道只有操作系统能够无限制的访问内存、磁盘、鼠键等外围硬件设备的数据,因为操作系统就是作为计算机硬件资源管理器而存在的,操作系统就是为了让多个普通应用程序可以更简单、安全的运行在同一台计算机上而存在的 “特殊的应用程序”。   

  • Ring3 用户态(User Mode):运行在用户态的程序代码需要受到CPU的检查,用户态程序代码只能访问内存页表项中规定能被用户态程序代码访问的页面虚拟地址(受限的内存访问),而且还只能访问I/O Permission Bitmap中规定的能被用户态程序代码访问的端口,不能直接访问外围硬件设备、不能抢占CPU。

也很显然,所有的应用程序都应该运行在用户态中。当应用程序需要访问外围硬件设备时,CPU 会通过特别的接口去调用核心态的代码,以这种方式来处理应用程序对硬件设备的调用。如果用户态的应用程序直接调用硬件设备的话,就会被操作系统捕捉到并触发异常,弹出警告窗口。

可见,x86 架构与大型机不同,当时的x86体系结构缺乏必要的针对虚拟化的硬件支持,难以直接满足虚拟化需求,所以x86架构天然不是一个可虚拟化的架构。x86架构的CPU中有17条指令成为了虚拟化最大的障碍,错误执行这些指令会导致操作系统显示警告、终止应用程序甚至完全崩溃。

当时 VMware 提出了解决这个问题的思路:在虚拟机生成这些特殊的指令时将它们 “困住”,然后将它们转换成可虚拟化的安全指令,同时保证其他所有的指令不受到干扰地执行。这样就产生了一种与主机硬件匹配并保持软件完全兼容性的高性能虚拟机。

这就是 全虚拟化(Full virtualization) 技术诞生的背景,即必须使用纯软件实现的方式构造 VMM。

全虚拟化是指虚拟机模拟了完整的底层硬件,包括处理器、物理内存、时钟、外设等,使得为原始硬件设计的操作系统或其它系统软件完全不做任何修改就可以在虚拟机中运行。

客户机操作系统(Guest OS)与真实硬件之间的交互可以看成是通过一个预先规定的硬件接口进行的。全虚拟化 VMM 以完整模拟硬件的方式提供全部接口(同时还必须模拟特权指令的执行过程)。

不支持硬件辅助虚拟化技术的X86架构下的CPU有4个特权级(ring0~ring3),操作系统是处于最高级别的ring0,应用程序处于最低级别的ring3。

image

在这种架构下实现CPU的全虚拟化是极其困难的,为什么困难?

  • 原先的OS运行在ring0层,拥有对所有硬件的全部特权级;
  • 虚拟化之后将OS运行在ring1层,OS就没有权限执行一些特权指令,怎么保证这些特权指令执行;
  • 在保证该OS虚拟机的特权指令执行的情况下,保证其他运行的OS虚拟机的安全;

1.1 模拟仿真技术

最先实现这种CPU全虚拟化技术的是Trap-and-emulation技术,即陷入模式和模拟仿真技术。这种技术通过将Guest OS需求的特权指令通过VMM自动捕获的方式运行后返回去OS。当Guest OS有特权指令产生时,VMM将其自动捕获,将Guest OS所请求的特权指令进行截获,然后通过VMM运行之后将结果返回给Guest OS层。VMM会使用模拟仿真将特权指令模拟仿真的方式执行一遍。

image

在虚拟化模式下,就存在着2中特殊的指令:特权指令和敏感指令。那么什么是特权指令?什么是敏感指令?

  • 特权指令:系统中有一些操作和管理关键系统资源的指令,这些指令只有在最高特权级上能够正确运行。如果在非最高特权级上运行,特权指令会引发一个异常,处理器会陷入到最高特权级,交由系统软件处理了。

  • 敏感指令:操作特权资源的指令,包括修改虚拟机的运行模式或者物理机的状态;读写时钟、中断等寄存器;访问存储保护系统、地址重定位系统及所有的I/O指令。

根据Popek和Goldberg的定义,指令集支持虚拟化的前提是:所有敏感指令都是特权指令。很可惜x86指令集不能满足这个要求。

虚拟化场景下,要求将GuestOS内核的特权解除,从原来的0降低到1或者3。这部分特权指令在Guest OS中发生的时候,就会产生Trap,被VMM捕获,从而由VMM完成。这就是虚拟的本质方法,特权解除和陷入模拟(Privilege deprivileging/Trap-and-Emulation)。虚拟化场景中敏感指令必须被VMM捕获并完成。对于一般 RISC 处理器,如 MIPS,PowerPC 以及SPARC,敏感指令肯定是特权指令,但是x86 例外,x86绝大多数的敏感指令是特权指令,但是由于部分敏感指令不是特权指令,执行这些指令的时候不会自动trap被VMM捕获。

image

1.2 二进制翻译技术

采用模拟仿真的方式模拟和虚拟化x86架构的CPU,但是由于x86架构的CPU中,不是所有的敏感指令都是特权指令,所以并不能完全的解决掉那些不是特权指令的敏感指令的模拟仿真问题。例如SGDT, SLDT, SIDT …

由于模拟仿真技术固有的缺陷,导致对CPU的虚拟化并不完整。所以也导致了基于x86的虚拟化难以和其他CPU架构一样实现虚拟化。比如IBM的Power CPU架构就很早具备了虚拟化的技术并使用于实践。

这个现象在1999年得到改善,VMware通过二进制翻译技术完成了对x86 CPU架构的完全虚拟化。

其主要采用优先级压缩技术(Ring Compression)和二进制代码翻译技术(Binary Translation)。优先级压缩技术让VMM和Guest运行在不同的特权级下。对x86架构而言,即VMM运行在最高特权级别Ring 0下,Guest OS运行在Ring 1下,用户应用运行在Ring 3下。因此,Guest OS的核心指令无法直接下达到计算机系统硬件执行,而是需要经过VMM的捕获和模拟执行(部分难以虚拟化的指令需要通过二进制翻译【Binary Translation】技术进行转换)。如下图所示。

image

二进制翻译技术简称BT,是一种直接翻译可执行二进制程序的技术,能够把一种处理器上的二进制程序翻译到另外一种处理器上执行。二进制翻译技术将机器代码从源机器平台映射(翻译)至目标机器平台,包括指令语义与硬件资源的映射,使源机器平台上的代码“适应”目标平台。因此翻译后的代码更适应目标机器,具有更高的运行时效率。二进制翻译系统是位于应用程序和计算机硬件之间的一个软件层,它很好地降低了应用程序和底层硬件之间的耦合度,使得二者可以相对独立地发展和变化。二进制翻译也是一种编译技术,它与传统编译的差别在于其编译处理对象不同。传统编译处理的对象是某一种高级语言,经过编译处理生成某种机器的目标代码;二进制翻译处理的对象是某种机器的二进制代码,该二进制代码是通过传统编译过程生成的,经过二进制翻译处理后生成另一种机器的二进制代码。

根据不同的实现方式,二进制翻译技术可分为三大类:解释执行,静态翻译和动态翻译。

三种二进制翻译技术的比较

类别说明优点缺点
代码解释执行解释执行(Interpretation)过程对源机器代码中的每条指令实时解释执行,系统不保存且不缓存解释过的指令,不需要用户干涉,也不进行任何优化。相对容易开发,比较容易与老的体系结构高度兼容效率很差
静态翻译代码在运行之前被离线翻译,根据目标机器的指令结构生成一个新的程序,然后直接执行这个翻译后生成的程序。离线翻译不会给运行带来额外开销,可以进行更好的优化,运行效率较高依赖编译器、运行环境的支持,需要终端用户参与
动态翻译在程序运行时对执行到的代码片段进行翻译,克服了静态翻译所无法解决的一些困难,如运行时动态信息收集,代码挖掘,自修改代码和精确中断问题。无需解释器和运行环境的支持,无需用户参与,利用动态信息来发现优化的机会翻译代码效率不高,对目标机器有额外的开销

解释执行是最易实现的一种翻译技术,但是其繁琐的实现方式大大降低了翻译系统的执行效率。静态翻译虽然能提供高效的运行时性能,但由于无法在静态环境下覆盖所有代码,无法脱离对解释器的依赖。与上述两种相比,动态翻译很好的解决了代码覆盖、自修改代码和精确中断等诸多问题,同时也能提供可接受的执行效率。因此VMware基于动态二进制翻译技术实现了x86架构的CPU的虚拟化。

典型动态二进制翻译系统结构所示,被翻译的代码称为源机器代码,在宿主机上运行的代码称为目标机器代码,一个典型的动态二进制翻译器主要包括两个模块:翻译引擎和执行引擎。其中翻译器引擎负责将源机器代码翻译代码翻译成目标机器代码;执行引擎负责准备目标机器代码运行的上下文环境(Execution Context)然后从目标机器代码缓存中找到源机器代码对应的目标代码并执行。

1.3 总结

在没有CPU硬件辅助虚拟化技术之前,对于X86架构的CPU就采用模拟和二进制翻译的技术对CPU进行虚拟化实现,但是模拟的方式存在固有缺陷,并不完全虚拟化了x86的CPU架构。而二进制翻译技术则采用完全不同的思路实现了x86架构的CPU虚拟化。其实对于x86的CPU虚拟化,其难点就在于对其特权指令和敏感指令的虚拟化实现,当然,在实现了CPU的指令这一难题之后,还有一个难题在等着我们!那就是x86架构的CPU调度问题?

在虚拟化环境下,x86架构的CPU有什么调度问题?

  1. 虚拟CPU和物理CPU之间的对应关系?
  2. 虚拟CPU和物理CPU之间的资源分配?
  3. 虚拟CPU和虚拟CPU之间的优先级?
  4. 多核虚拟CPU架构vSMP和vNUMA与物理多核CPU架构SMP和NUMA之间的调度和负载均衡?

全虚拟化 VMM 必须要克服许多难以解决的问题。例如:    

  1. 确保 VMM 控制所有的系统资源:x86 处理器有 4 个特权级别,Ring 0 ~ Ring 3,只有运行在 Ring 0 ~ 2 时,处理器才可以访问特权资源或执行特权指令;运行在 Ring 0 级时,处理器可以访问所有的特权状态。x86 平台上的操作系统一般只使用 Ring 0 和 Ring 3 这两个级别,操作系统运行在 Ring 0 级,用户进程运行在 Ring 3 级。为了满足 资源控制(Resource Control) 虚拟化需求条件,VMM 就必须运行在 Ring 0 级,同时为了避免 Guest OS 控制系统资源,Guest OS 不得不降低自身的运行级别,运行在 Ring 1 或 Ring 3 级(Ring 2 不使用)。

  2. 特权级压缩(Ring Compression):VMM 使用分页或段限制的方式来保护物理内存的访问,但是 64 位模式下段限制不起作用,而分页又不区分 Ring 0, 1, 2。为了统一和简化 VMM的设计,Guest OS 只能和 Guest 进程一样运行在 Ring 3 级。VMM 必须监视 Guest OS 对 GDT、IDT 等特权资源的设置,防止 Guest OS 运行在 Ring 0级,同时又要保护降级后的 Guest OS 不受 Guest 进程的主动攻击或无意破坏。

  3. 特权级别名(Ring Alias):特权级别名是指 Guest OS 在虚拟机中运行的级别并不是它所期望的。VMM 必须保证 Guest OS 不能获知正在虚拟机中运行这一事实,否则可能打破 等价性(Equivalence) 虚拟化需求条件。例如,x86 处理器的特权级别存放在 CS 代码段寄存器内,Guest OS 可以使用非特权 push 指令将 CS 寄存器压栈,然后 pop 出来检查该值。又如,Guest OS 在低特权级别时读取特权寄存器 GDT、LDT、IDT 和 TR,并不发生异常,从而可能发现这些值与自己期望的不一样。

    为了解决这个挑战,VMM 可以使用动态二进制翻译(Binary Translation)的技术,例如预先把 push %%cs 指令替换,在栈上存放一个影子 CS 寄存器值;又如,可以把读取 GDT 寄存器的操作 sgdt dest 改为 movl fake_gdt, dest。

  4. 地址空间压缩(Address Space Compression):地址空间压缩是指 VMM 必须在 Guest OS 的地址空间中保留一部分供其使用。例如,中断描述表寄存器(IDT Register)中存放的是中断描述表的线性地址,如果 Guest OS 运行过程中来了外部中断或触发处理器异常,必须保证运行权马上转移到 VMM 中,因此 VMM 需要将 Guest OS 的一部分线性地址空间映射成自己的中断描述表的主机物理地址。

    VMM 可以完全运行在 Guest OS 的地址空间中,也可以拥有独立的地址空间,后者的话,VMM 只占用 Guest OS 很少的地址空间,用于存放中断描述表和全局描述符表(GDT)等重要的特权状态。无论如何哪种情况,VMM 应该防止 Guest OS 直接读取和修改这部分地址空间。

  5. 处理 Guest OS 的缺页异常:内存是一种非常重要的系统资源,VMM 必须全权管理,Guest OS 理解的物理地址只是客户机物理地址(Guest Physical Address),并不是最终的主机物理地址(Host Physical Address)。当 Guest OS 发生缺页异常时,VMM 需要知道缺页异常的原因,是 Guest 进程试图访问没有权限的地址,或是客户机线性地址(Guest Linear Address)尚未翻译成客户机物理地址,还是客户机物理地址尚未翻译成主机物理地址。一种可行的解决方法是 VMM 为 Guest OS 的每个进程的页表构造一个影子页表(Shadow Page Table),维护 Guest Linear Address 到 Host Physical Address 的映射,主机 CR3 寄存器存放这个影子页表的物理内存地址。

    VMM 同时维护一个 Guest OS 全局的 Guest Physical Address 到 Host Physical Address 的映射表。发生缺页异常的地址总是 Guest Linear Address,VMM 先去 Guest OS 中的页表检查原因,如果页表项已经建立,即对应的 Guest Physical Address 存在,说明尚未建立到 Host Physical Address 的映射,那么 VMM 分配一页物理内存,将影子页表和映射表更新;否则,VMM 返回到 Guest OS,由 Guest OS 自己处理该异常。

  6. 处理 Guest OS 中的系统调用(System Call):系统调用是操作系统提供给用户的服务例程,使用非常频繁。最新的操作系统一般使用 SYSENTER/SYSEXIT 指令对来实现快速系统调用。SYSENTER 指令通过 IA32_SYSENTER_CS,IA32_SYSENTER_EIP 和 IA32_SYSENTER_ESP 这 3 个 MSR(Model Specific Register)寄存器直接转到 Ring 0 级;而 SYSEXIT 指令不在 Ring 0 级执行的话将触发异常。因此,如果 VMM 只能采取 Trap-And-Emulate 的方式处理这 2 条指令的话,整体性能将会受到极大损害。

  7. 转发虚拟的中断和异常:所有的外部中断和 pCPU 的异常直接由 VMM 接管,VMM 构造必需的虚拟中断和异常,然后转发给 Guest OS。VMM 需要模拟硬件和操作系统对中断和异常的完整处理流程,例如 VMM 先要在 Guest OS 当前的内核栈上压入一些信息,然后找到 Guest OS 相应处理例程的地址,并跳转过去。

    VMM 必须对不同的 Guest OS 的内部工作流程比较清楚,这增加了 VMM 的实现难度。同时,Guest OS 可能频繁地屏蔽中断和启用中断,这两个操作访问特权寄存器 EFLAGS,必须由 VMM 模拟完成,性能因此会受到损害。Guest OS 重新启用中断时,VMM 需要及时地获知这一情况,并将积累的虚拟中断转发。

  8. Guest OS 频繁访问特权资源:Guest OS 对特权资源的每次访问都会触发 CPU 异常,然后由 VMM 模拟执行,如果访问过于频繁,则系统整体性能将会受到极大损害。比如对中断的屏蔽和启用,cli(Clear Interrupts)指令在 Pentium 4 处理器上需要花费 60 个时钟周期(cycle)。

    又比如,处理器本地高级可编程中断处理器(Local APIC)上有一个操作系统可修改的任务优先级寄存器(Task-Priority Register),IO-APIC 将外部中断转发到 TPR 值最低的处理器上(期望该处理器正在执行低优先级的线程),从而优化中断的处理。TPR 是一个特权寄存器,某些操作系统会频繁设置(Linux Kernel 只在初始化阶段为每个处理器的 TPR 设置相同的值)。

显然,基于 Trap-And-Emulate 处理方式的全虚拟化虽能够以纯软件的方式完成虚拟化并解决了许多问题,但同时也带来了极大的设计复杂性和性能下降。而对于这两个问题,半虚拟化(Partial virtualization) 想到了一个好办法:改造 Guest OS,将 Guest OS 原来所有需要被 VMM 截获、模拟的指令和操作全部改造成与 VMM 协同工作的指令(hypercall)和操作。VMM 不再隐瞒了,因为你(Guest OS)已经知道自己就是个虚拟机了。其核心思想是:动态或静态地改变 Guest OS 对特权状态访问的操作,尽量减少产生不必要的硬件异常,同时简化 VMM 的设计。   

2.CPU的半虚拟化

半虚拟化是一种通过修改 Guest OS 部分访问特权状态的代码以便直接与 VMM 交互的技术。在半虚拟化虚拟机中,部分硬件接口以软件的形式提供给 Guest OS,这可以通过 Hypercall(VMM 提供给 Guest OS 的直接调用,与系统调用类似)的方式来提供。而Hypercall支持的批处理和异步这两种优化方式,使得通过Hypercall能得到近似于物理机的速度。

例如,Guest OS 把切换页表的代码修改为调用 Hypercall 来直接完成修改影子 CR3 寄存器和翻译地址的工作。由于不需要产生额外的异常和模拟部分硬件执行流程,半虚拟化可以大幅度提高性能,比较著名的 VMM 有 Denali、Xen。

2.1 Hypercall技术

对于x86体系结构CPU,Xen使用超级调用来替换被监控的操作,其中包括x86架构下的敏感指令。Xen所采用的超级替换的方法是一种全新的设计理念:它将问题的中心,由VMM移向Guest OS自身,通过主动的方式由Guest OS去处理这些指令,而不是被移交给VMM做处理,在这种设计理念下,修改Guest OS内核。

能修改Guest OS是半虚拟化的一个技术核心。通过修改Guest OS的内核。使Guest OS明确知道自己是运行在1环上,而不是通常OS的0环,有效的避免了虚拟化的执行冲突问题。Guest OS也清楚VMM给自己提供了一个虚拟的寄存器组,并能通过其他方式去访问他们,避免了访问冲突的问题。

解决了敏感指令问题只是解决了x86架构下的半虚拟化的第一步。运行在1环的操作系统没有权限执行的指令,交给0环的VMM来处理,这个很大程度上与应用程序的系统调用很类似:系统调用的作用是把应用程序无权执行的指令交给操作系统完成。因此,Xen向Guest OS提供了一套“系统调用”。以方便Guest OS调用,这部分”系统调用“就是超级调用Hypercall。

超级调用Hypercall的机制使用,不仅使x86架构的指令虚拟化得以实现,也为后面的内存虚拟化和I/O虚拟化提供了新的思路和方法,超级调用和事件通道是整个半虚拟化的基础。

半虚拟化整体的访问流程图,如图所示。

image

上图明确的显示了Hypercall的调用位置,在Xen中,各组件通信方式如下所示,Hypercall的调用性质是同步的。其他Xen的通信方式几乎都是异步的。

image

其中,在虚拟机和Xen的通信过程中,如果虚拟机需要调用敏感指令,会主动向虚拟机监控器发起Hypercall调用。Hypercall就如同传统操作系统下的系统调用,监管程序通过它向其上各虚拟机提供各种服务,如MMU更新、Domain0操作请求和虚拟处理器状态等。

下图显示了半虚拟化模式下的特权模式:

image

在x86架构下,原生系统和半虚拟化环境下存在差异。原生环境下,CPU有4个特权级(ring0--ring3),操作系统是处于最高级别的ring0,应用程序处于最低级别的ring3。而在半虚拟化环境下,虚拟机监视器是处于最高级别的ring0,操作系统是处于中级级别的ring1,应用程序处于最低级别的ring3。

image

只有ring1的代码(准虚拟化Guest VM的内核)才能向Xen发送Hypercall请求,以防止应用程序(ring3)的错误调用导致对系统可能的破坏。因此,只有运行在ring1的虚拟机操作系统内核才能申请Hypercall。但是,一些Xen专用的特别程序,如xend 或xe也需要有Hypervisor的服务来完成特殊的操作,如生成一个新的Guest VM等,这在Xen Linux中是通过一个称为privcmd的内核驱动程序实现。应用程序通过ioctl向该驱动程序提出服务请求,运行在虚拟机内核(ring1)的privcmd 驱动程序再将服务请求以Hypercall 形式转向Hypervisor,并由后者真正完成生成新Guest VM 的动作。

image

上图中显示了Hypercall所在的位置,Hypercall位于图中右上方,内核向Hypervisor发起调用的哪里。Xen启用130号中断向量端口(十六进制的82H)作为超级调用的中断号。这一个中断向量的DPL被设置为类型为1,类型为中断门。这样,超级调用能够由处于特权级1的客户机操作系统发起,而不能从用户态发起。

另外,在x86指令集的指令中,有17条指令不能有效的在ring 1特权级上运行,Hypercall 的存在解决了这些指令不能正常执行的问题。

image

Hypercall 机制中,在32位x86架构下,Hypercall通过int0x82陷阱(Trap)指令实现,因为传统操作系统本身并不使用int0x82 (Linux 使用int 0x80作为系统调用指令,int 0x82并未使用)。

int0x82包括:

  • 超级调用号:xen/include/public/xen.h中定义了45个超级调用,其中有7个是平台相关调用。
  • 超级调用表:xen/arch/x86/x86_32/entry.S中定义了超级调用表,通过超级调用号索引就可以方便的找到对应的处理函数。
  • 超级调用页:超级调用页是Xen为Guest OS准备的一个页,可以做到不同Guest OS有不同的超级调用页内容。

Hypercall的具体功能识别号由eax表明,而其他参数则在ebx, ecx, edx, esi 和edi 中。为了减少虚拟机和Hypervisor之间的特权级别(Ring)切换次数,Xen提供对Hypercall的批处理,即将几个Hypercall功能请求放在一个列表中由专门的Hypercall 批处理请求完成。在Xen中,系统调用表与Hypercall表都在entry.S文件中被定义。

2.2 X86架构特权级

x86 硬件支持 4 个特权级 (Ring),一般内核运行在 Ring 0, 用户应用运行在 Ring 3, 更小的 Ring 有比更高的 Ring 能访问更多的系统全局资源,即更高的特权。有些指令只能在 Ring 0 才能正确执行,如 LGDT、LMSW 指令,我们称之为特权指令;另外有些指令可以在 Ring 3 正确执行,如 SGDT、 SMSW、PUSHF/POPF,我们称之为非特权指令。

正常模式和虚拟化两种情况叙述下,特权模式说明如下:

正常模式:特权级别是针对段来讲的,段描述符的最后两位标识了该段所位于的特权级别,比如,中断处理程序运行于ring0,此时的内核程序是具有特权的,即ring0。位于ring3用户程序可以通过系统调用的方式,int80,后特权翻转入ring0,然后就可以顺利执行中断处理程序(好像是用户程序调用内核程序的唯一途径)。

虚拟化情况下:

特权解除:是指解除正常情况下运行于ring0的段,比如中断处理程序,为了虚拟化需要,此时解除其特权,将其运行于ring1。当用户程序通过系统调用时,其跳转到的中断处理程序运行于ring1。但是,在中断处理程序中,有部分指令是必须在ring0才能执行的,此时,便会自动陷入,然后模拟。也就是说,用户程序运行特权指令,会有两次特权下降,一次是通过系统调用进入ring1,第二次是通过特权指令陷入进入ring0。这说明,中断发生时的中断处理程序还是以前的位于内核的代码,但是其运行级别为ring1,部分指令还需要再次陷入,才能执行。另外,还有一个重要问题,就是部分敏感非特权指令无法陷入的问题:存在二进制翻译、超级调用等方式,强迫其陷入,然后模拟。

在传统的 X86 平台上支持虚拟化上存在如下问题 :

X86 指令集中存在 17 条敏感的非特权指令,“非特权指令”表明这些指令可以在 x86 的 ring 3 执行, 而“敏感性”说明 VMM 是不可以轻易让客操作系统执行这些指令的。 这 17 条指令在客操作系统上的执行或者会导致系统全局状态的破坏,如 POPF 指令,或者会导致客操作系统逻辑上的问题,如 SMSW 等读系统状态或控制寄存器的指令。传统的 X86 没法捕获这些敏感的非特权指令。

除了那 17 条敏感的非特权指令,其他敏感的指令都是敏感的特权指令。在 x86 虚拟化环境,VMM 需要对系统资源进行统一的控制,所以其必然要占据最高的特权级,即 Ring 0, 所以为了捕获特权指令,在传统x86上一个直接可行的方法是 “Ring deprivileging”, 如将客操作系统内核的特权级从Ring 0改为Ring 1或Ring 3, 即 “消除” 客内核的特权,以低于VMM所在的Ring 0, 从而让 VMM捕获敏感的特权指令。

2.3 总结

相较于全虚拟化,半虚拟化VMM只需要模拟部分底层硬件,因此Guest OS不做修改是无法在虚拟机中运行的,甚至运行在虚拟机中的其它程序也需要进行修改,如此代价,换来的就是接近于物理机的虚拟机性能。 

半虚拟化的思想就是,让Guest OS操作系统知道自己是在虚拟机上跑的,工作在非ring0状态,那么它原先在物理机上执行的一些特权指令,就会修改成其他方式,这种方式是可以和VMM约定好的,这就相当于,通过修改代码把操作系统移植到一种新的架构上来,就像是定制化。所以XEN这种半虚拟化技术,客户机操作系统都是有一个专门的定制内核版本,和x86、mips、arm这些内核版本。这样以来,就不会有捕获异常、翻译、模拟的过程了,性能损耗非常低。这就是XEN这种半虚拟化架构的优势。这也是为什么XEN半虚拟化只支持虚拟化Linux,无法虚拟化windows原因,微软不修改代码无法实现半虚拟化。    

既然全虚拟化性能低的主要原因是花费了太多的精力去捕获CPU异常并模拟CPU行为,那么如果CPU本身就为VMM提供了便利,那岂不是从根本上解决了这个问题?这就是硬件辅助虚拟化(Hardware-assisted virtualization) 。  

3.硬件辅助虚拟化

硬件辅助虚拟化的核心思想是通过引入新的指令和运行模式,使VMM和Guest OS分别运行在不同模式(ROOT模式和非ROOT模式)下,且Guest OS运行在Ring 0下。通常情况下,Guest OS的核心指令可以直接下达到计算机系统硬件执行,而不需要经过VMM。当Guest OS执行到特殊指令的时候,系统会切换到VMM,让VMM来处理特殊指令。

在硬件虚拟化中,CPU指令环多了一个ring -1环,把ring 0环给Guest OS的内核了,当进行系统调用的时候,Guest OS会调用ring 0环的特权指令,ring 0环上的特权指令是假的,它会被Host的内核捕获,进而转换调用ring -1环的特权指令,只不过这些过程都是由硬件来进行的。

Intel-VT(Intel Virtualization Technology)和 AMD-V 是目前 x86 平台上可用的两种硬件辅助虚拟化技术。VT-x 为 IA 32 处理器增加了两种操作模式:VMX root operation 和 VMX non-root operation。VMM 自己运行在 VMX root operation 模式,VMX non-root operation 模式则由 Guest OS 使用。两种操作模式都支持 Ring 0~3 这4个特权级,因此VMM和Guest OS都可以自由选择它们所期望的运行级别。

3.1 Intel VT-x技术

为弥补x86处理器的虚拟化缺陷,市场的驱动催生了VT-x,Intel推出了基于x86架构的硬件辅助虚拟化技术Intel VT(Intel Virtualization Technology)。

目前,Intel VT技术包含CPU、内存和I/O三方面的虚拟化技术。

  • CPU硬件辅助虚拟化技术,分为对应安腾架构的VT-i(Intel Virtualization Technology for ltanium)和对应x86架构的VT-x(Intel Virtualization Technology for x86)。
  • 内存硬件辅助虚拟化技术包括EPT(Extended Page Table)技术。
  • I/O硬件辅助虚拟化技术的代表VT-d(Intel Virtualization Technology for Directed I/O)。
  • Intel VT-x技术解决了早期x86架构在虚拟化方面存在的缺陷,可使未经修改的Guest OS运行在特权级0,同时减少VMM对Guest OS的干预。Intel VT-d技术通过使VMM将特定I/O设备直接分配给特定的Guest OS,减少VMM对I/O处理的管理,不但加速数据传输,且消除了大部分性能开销。如下图所示。CPU硬件辅助虚拟化技术简要说明流程图:

Intel VT-x技术解决了早期x86架构在虚拟化方面存在的缺陷,可使未经修改的Guest OS运行在特权级0,同时减少VMM对Guest OS的干预。Intel VT-d技术通过使VMM将特定I/O设备直接分配给特定的Guest OS,减少VMM对I/O处理的管理,不但加速数据传输,且消除了大部分性能开销。如下图所示。CPU硬件辅助虚拟化技术简要说明流程图: 

image

效法IBM大型机,VT-x提供了2个运行环境:根(Root)环境和非根(Non-root)环境。根环境专门为VMM准备,很像原来没有VT-x 的x86,只是多了对VT-x 支持的几条指令。非根环境作为一个受限环境用来运行多个虚拟机。

如上图所示,根操作模式与非根操作模式都有相应的特权级0至特权级3。VMM运行在根模式的特权级0,GuestOS的内核运行在非根模式的特权级0,GuestOS的应用程序运行在非根模式的特权级3。运行环境之间相互转化,从根环境到非根环境叫VMEntry;从非根环境到根环境叫VMExit。VT-x定义了VMEntry操作,使CPU由根模式切换到非根模式,运行客户机操作系统指令。若在非根模式执行了敏感指令或发生了中断等,会执行VMExit操作,切换回根模式运行VMM。

根模式与非根模式之问的相互转换是通过VMX操作实现的。VMM 可以通过VMXON 和VMXOFF打开或关闭VT-x。如下图所示: 

image

VMX操作模式流程:

  • VMM执行VMXON指令进入VMX操作模式。
  • VMM可执行VMLAUNCH指令或VMRESUME指令产生VM Entry操作,进入到Guest OS,此时CPU处于非根模式。
  • Guest OS执行特权指令等情况导致VMExit的发生,此时将陷入VMM,CPU切换为根模式。VMM根据VMExit的原因作出相应处理,处理完成后将转到2),继续运行GuestOS。
  • VMM可决定是否退出VMX操作模式,通过执行VMXOFF指令来完成。

为更好地支持CPU虚拟化,VMX新定义了虚拟机控制结构VMCS(Virtual Machine ControlStructure)。VMCS是保存在内存中的数据结构,其包括虚拟CPU的相关寄存器的内容及相关的控制信息。CPU在发生VM Entry或VMExit时,都会查询和更新VMCS。VMM也可通过指令来配置VMCS,达到对虚拟处理器的管理。VMCS架构图如下图所示:

image

每个虚拟处理器都需将VMCS与内存中的一块区域联合起来,此区域称为VMCS区域。对VMCS区域的操纵是通过VMCS指针来实现的,这个指针是一个指向VMCS的64位的地址值。VMCS区域是一个最大不超过4KB的内存块,且需4KB对齐。

VMCS区域分为三个部分:

  • 偏移0起是VMCS版本标识,通过不同的版本号,CPU可维护不同的VMCS数据格式;
  • 偏移4起是VMX中止指示器,在VMX中止发生时,CPU会在此处存入中止的原因;
  • 偏移8起是VMCS数据区,这一部分控制VMX非根操作及VMX切换。
  • VMCS 的数据区包含了VMX配置信息:VMM在启动虚拟机前配置其哪些操作会触发VMExit。VMExit 产生后,处理器把执行权交给VMM 以完成控制,然后VMM 通过指令触发VMEntry 返回原来的虚拟机或调度到另一个虚拟机。

VMCS 的数据结构中,每个虚拟机一个,加上虚拟机的各种状态信息,共由3个部分组成,如之前的VMCS架构图所示:

  • 1) Gueststate:该区域保存了虚拟机运行时的状态,在VMEntry 时由处理器装载;在VMExit时由处理器保存。它又由两部分组成:
    • Guest OS寄存器状态。它包括控制寄存器、调试寄存器、段寄存器等各类寄存器的值。
    • Guest OS非寄存器状态。用它可以记录当前处理器所处状态,是活跃、停机(HLT)、关机(Shutdown)还是等待启动处理器间中断(Startup-IPI)。
  • 2) Hoststate:该区域保存了VMM 运行时的状态,主要是一些寄存器值,在VMExit 时由处理器装载。
  • 3) Control data:该区域包含几部分数据信息,分别是:

虚拟机执行控制域(VM-Execution control fields)。VMM 主要通过配置该区域来控制虚拟机在非根环境中的执行行为。基于针脚的虚拟机执行控制。它决定在发生外部中断或不可屏蔽中断(NMI)要不要发生VMExit。基于处理器的虚拟机执行控制。它决定虚拟机执行RDTSC、HLT、INVLPG 等指令时要不要发生VMExit。 VMExit 控制域(VMExit control fields)。该区域控制VMExit 时的行为。当VMExit 发生后处理器是否处于64 位模式;当因为外部中断发生VMExit 时,处理器是否响应中断控制器并且获得中断向量号。VMM 可以用它来定制当VMExit 发生时要保存哪些MSR 并且装载哪些MSR。MSR是CPU的模式寄存器,设置CPU的工作环境和标识cpu的工作状态。 VMEntry 控制域(VMEntry control fields)。该区域控制VMEntry 时的行为。它决定处理器VMEntry 后是否处于IA-32e 模式。与VMExit 的MSR控制类似,VMM 用它来定制当VMEntry 发生时要装载哪些MSR。VMM 可以配置VMEntry 时通过虚拟机的IDT向其发送一个事件。在此可以配置将使用IDT 的向量、中断类型(硬件或软件中断)、错误码等。 VMExit 信息域(VMExit information fields)。该只读区域包括最近一次发生的VMExit 信息。试图对该区域执行写操作将产生错误。。此处存放VMExit 的原因以及针对不同原因的更多描述信息、中断或异常向量号、中断类型和错误码、通过 IDT 发送事件时产生的VMExit 信息、指令执行时产生的 VMExit 信息。

有了VMCS结构后,对虚拟机的控制就是读写VMCS结构。后面对vCPU设置中断,检查状态实际上都是在读写VMCS数据结构。

3.2 AMD-V技术

我们在上面小节介绍了 Intel 的硬件辅助虚拟化技术,那么 AMD 的硬件辅助虚拟化技术又有什么特点呢?AMD从2006 年便开始致力于硬件辅助虚拟化技术的研究,AMD-V全称是AMD Virtualization,AMD-V从代码的角度分别称为AMD和SVM,AMD开发这项虚拟化技术时的内部项目代码为Pacifica,是AMD推出的一种硬件辅助虚拟化技术。

image

Intel VT-x 和 AMD-V 提供的特征大多功能类似,但名称可能不一样,如 Intel VT-x 将用于存放虚拟机状态和控制信息的数据结构称为 VMCS, 而 AMD-V 称之为VMCB; Intel VT-x 将 TLB 记录中用于标记 VM 地址空间的字段为 VPID, 而AMD-V 称之为 ASID; Intel VT-x 将二级地址翻译称之为 EPT, AMD 则称为 NPT,等等一些区别。尽管其相似性,Intel VT-x 和 AMD-V 在实现上对 VMM 而言是不兼容的。

AMD-V在AMD传统的x86-64 基础上引入了“guest”操作模式。“guest”操作模式就是CPU在进入客操作系统运行时所处的模式。 “guest”操作模式为客操作系统设定了一个不同于 VMM 的运行环境而不需要改变客操作系统已有的4个特权级机制,也就是说在“guest”模式下,guest操作系统的内核仍然运行在Ring 0, guest OS用户程序仍然在Ring 3。 host上的操作系统和VMM所在的操作模式依然和传统的x86中一样,且称之为“host”操作模式。VMM通过执行VMRUN指令使CPU进入“guest”操作模式而执行客操作系统的代码; 客操作系统在运行时,遇到敏感指令或事件,硬件就执行VMEXIT行为,使CPU回到“host”模式而执行 VMM 的代码。VMRUN指令运行的参数是一个物理地址指针,其指向一个Virtual Machine Control Block (VMCB) 的内存数据结构, 该数据结构包含了启动和控制一个虚拟机的全部信息。

image

“guest”模式的意义在于其让客操作系统处于完全不同的运行环境,而不需要改变客操作系统的代码。“guest”模式的设立在系统中建立了一个比Ring 0更强的特权控制,即客操作系统的Ring 0特权必须让位于VMM的Ring 0特权。客操作系统上运行的那些特权指令,即便是在Ring 0上也变的可以被VMM截取的了,“Ring Deprivileging”由硬件自动搞定。此外,VMM还可以通过VMCB中的各种截取控制字段选择性的对指令和事情进行截取,或设置有条件的截取,所有的敏感的特权或非特权指令都在其控制之中。

VMCB数据结构主要包含如下内容 :

  1. 用于描述需要截取的指令或事件的字段列表。其中 :

2 个 16 位的字段用于控制对 CR 类控制寄存器读写的截取 2 个 16 位的字段用于控制对 DR 类调试寄存器的读写的截取 一个 32 位的字段用于控制 exceptions 的截取 一个 64 位的字段用于控制各种引起系统状态变化的事件或指令的截取,如 INTR, NMI, SMI 等事 件, HLT, CPUID,INVD/WBINVD,INVLPG/INVLPGA,MWAIT 等指令, 还包括两位分别标志是否对 IO 指令和 MSR 寄存器的读写进行控制 指向IO端口访问控制位图和MSR读写控制位图的物理地址指针字段。该位图用于差别性地控制虚拟机对不同的 IO 端口和 MSR 寄存器进行读写访问。 描述虚拟机CPU状态的信息。包含除通用寄存器外的大部分控制寄存器,段寄存器,描述符表寄存器,代码指针等。 RAX 寄存器也在其中,因为 RAX 在 VMM 执行 VMRUN 时是用来存放VMCB 物理地址的。 对于段寄存器,该信息中还包含段寄存器对应的段描述符,也就那些传统 x86 上对软件隐藏的信息。 对虚拟机的执行进行控制的字段。主要是控制虚拟机中断和 NPT 的字段。 指示虚拟机进入“guest”模式后要执行的行动的字段。包括用来描述 VMM 向虚拟机注入的中断或异常的信息的字段。 注入的中断或异常在 VMRUN 进入“guest”模式后立即执行,就象完全发生在虚拟机内一样。 提供VMEXIT信息的字段。包括导致 VMEXIT 的事件的代码,异常或中断的号码,page fault 的线性地址,被截获的指令的编码等。

image

VMCB 以及其涉及的控制位图,完全通过物理地址进行指向,这就避免了“guest”和“host”模式切换的过程依赖于“guest”空间的线性地址 ( 传统操作系统内用户空间到内核的切换确实依赖于 IDT 中提供的目标的线性地址 ),使得 VMM 可以采用和客操作系统完全不同的地址空间。

VMCB 的内容在物理上被分成了俩部分,其中用于保存虚拟机 CPU 状态的信息占据 2048 字节的后半部分,我们可称之为 VMCB.SAVE; 其他信息,占据前 1024 字节范围,我们可称之为 VMCB.CONTROL。

VMRUN 命令以 VMCB 为参数,使CPU 进入“guest”状态, 按 VMCB.SAVE 的内容恢复虚拟机的 CPU 寄存器状态,并按 VMCB.SAVE 中 CS:RIP 字段指示的地址开始执行虚拟机 的代码, 并将之前 VMM 的 CPU 状态保存在MSR_VM_HSAVE_PA 寄存器所指向的物理内存区域中。VMRUN 所保存的 VMM 的 CPU状态的 CS:RIP 实际上就是 VMM 的代码中 VMCB 的下一个指令,当虚拟机因某种原因而导致 #VMEXIT 时,VMM 会从 VMRUN 后的一条指令开始执行。CPU 执行 #VMEXIT 行为时,会自动将虚拟机的状态保存到 VMCB.SAVE 区,并从 MSR_VM_HSAVE_PA 指定的区域加载 VMM 的 CPU 状态。

VMLOAD 和 VMSAVE 指令是对 VMRUN 的补充,他们用来加载和恢复一些并不需要经常使用的 CPU 状态,如 FS, GS, TR, LDTR 寄存器以及其相关的隐含的描述符寄存器的内容,VMLOAD 和 VMSAVE 可以让 VMM 的实现对“guest”进入和退出的过程进行优化,让多数情况下只使用 VMRUN 进行最少的状态保存和恢复。

VMMCALL 指令是 AMD-V 为客操作系统内核提供的明确的功能调用接口,类似于 syscall 指令 ( 从 Ring3 到 Ring 0), VMMCALL 让客操作系统直接执行 #VMEXIT 而进入 VMM,请求VMM 的服务。

3.3 总结

根模式(root operation)和非根模式(none-root operation)这两种操作模式可以互相转换。运行在 VMX root operation 模式下的VMM通过显式调用VMLAUNCH或VMRESUME指令切换到VMX non-root operation 模式,硬件自动加载Guest OS的上下文,于是Guest OS获得运行,这种转换称为VM entry。

Guest OS运行过程中遇到需要VMM处理的事件,例如外部中断或缺页异常,或者主动调用 VMCALL指令调用VMM的服务的时候(与系统调用类似),硬件自动挂起Guest OS,切换到 VMX root operation模式,恢复VMM的运行,这种转换称为VM exit。

VMX root operation模式下软件的行为与在没有 VT-x 技术的处理器上的行为基本一致;而VMX non-root operation模式则有很大不同,最主要的区别是此时运行某些指令或遇到某些事件时,发生 VM exit。    

例如:在上面的例子中,Guest OS 能够执行修改页表的汇编指令,再无需 VMM 进行捕获、模拟。从而减少了相关的性能开销,也极大简化了 VMM 设计,进而使 VMM 能够按通用标准进行编写,性能更加强大。  

又因为 VMM 和 Guest OS 共享底层的处理器资源,所以硬件需要一个物理内存区域来自动保存或恢复彼此执行的上下文。这个区域称为虚拟机控制块(VMCS),包括客户机状态区(Guest State Area),主机状态区(Host State Area)和执行控制区。

VM entry 时,硬件自动从客户机状态区加载 Guest OS 的上下文。并不需要保存 VMM 的上下文,原因与中断处理程序类似,因为 VMM 如果开始运行,就不会受到 Guest OS的干扰,只有 VMM 将工作彻底处理完毕才可能自行切换到 Guest OS。

而 VMM 的下次运行必然是处理一个新的事件,因此每次 VMM entry 时, VMM 都从一个通用事件处理函数开始执行;VM exit 时,硬件自动将 Guest OS 的上下文保存在客户机状态区,从主机状态区中加载 VMM 的通用事件处理函数的地址,VMM 开始执行。而执行控制区存放的则是可以操控 VM entry 和 exit 的标志位,例如标记哪些事件可以导致 VM exit,VM entry 时准备自动给 Guest OS “塞” 入哪种中断等等。  

客户机状态区和主机状态区都应该包含部分物理寄存器的信息,例如控制寄存器 CR0,CR3,CR4;ESP 和 EIP(如果处理器支持 64 位扩展,则为 RSP,RIP);CS,SS,DS,ES,FS,GS 等段寄存器及其描述项;TR,GDTR,IDTR 寄存器;IA32_SYSENTER_CS,IA32_SYSENTER_ESP,IA32_SYSENTER_EIP 和 IA32_PERF_GLOBAL_CTRL 等 MSR 寄存器。

客户机状态区并不包括通用寄存器的内容,VMM 自行决定是否在 VM exit 的时候保存它们,从而提高了系统性能。客户机状态区还包括非物理寄存器的内容,比如一个 32 位的 Active State 值表明 Guest OS 执行时处理器所处的活跃状态,如果正常执行指令就是处于 Active 状态,如果触发了三重故障(Triple Fault)或其它严重错误就处于 Shutdown 状态,等等。    

执行控制区用于存放可以操控 VM entry 和 VM exit 的标志位,包括:    

  • External-interrupt exiting:用于设置是否外部中断可以触发 VM exit,而不论 Guest OS 是否屏蔽了中断。

  • Interrupt-window exiting:如果设置,当 Guest OS 解除中断屏蔽时,触发 VM exit。

  • Use TPR shadow:通过 CR8 访问 Task Priority Register(TPR)的时候,使用 VMCS 中的影子 TPR,可以避免触发 VM exit。同时执行控制区还有一个 TPR 阈值的设置,只有当 Guest OS 设置的 TR 值小于该阈值时,才触发 VM exit。

  • CR masks and shadows:每个控制寄存器的每一位都有对应的掩码,控制 Guest OS 是否可以直接写相应的位,或是触发 VM exit。同时 VMCS 中包括影子控制寄存器,Guest OS 读取控制寄存器时,硬件将影子控制寄存器的值返回给 Guest OS。

VMCS 还包括一组位图以提供更好的适应性:    

  • Exception bitmap:选择哪些异常可以触发 VM exit,

  • I/O bitmap:对哪些 16 位的 I/O 端口的访问触发 VM exit。

  • MSR bitmaps:与控制寄存器掩码相似,每个 MSR 寄存器都有一组“读”的位图掩码和一组“写”的位图掩码。

每次发生 VM exi t时,硬件自动在 VMCS 中存入丰富的信息,方便 VMM 甄别事件的种类和原因。VM entry 时,VMM 可以方便地为 Guest OS 注入事件(中断和异常),因为 VMCS 中存有 Guest OS 的中断描述表(IDT)的地址,因此硬件能够自动地调用 Guest OS 的处理程序。    

传统的全虚拟化实现在有了硬件的辅助之后,由于 CPU 引入了新的操作模式,VMM 和 Guest OS 的执行由硬件自动隔离开来,任何关键的事件都可以将系统控制权自动转移到 VMM,因此 VMM 能够完全控制系统的全部资源。Guest OS 也可以运行在它所期望的最高特权级别,因此特权级压缩和特权级别名的问题迎刃而解,而且 Guest OS 中的系统调用也不会触发 VM exit。    

硬件使用物理地址访问虚拟机控制块(VMCS),而 VMCS 保存了 VMM 和 Guest OS 各自的 IDTR 和 CR3 寄存器,因此 VMM 可以拥有独立的地址空间,Guest OS 能够完全控制自己的地址空间,地址空间压缩的问题也不存在了。中断和异常虚拟化的问题也得到了很好的解决。VMM 只用简单地设置需要转发的虚拟中断或异常,在 VM entry 时,硬件自动调用 Guest OS 的中断和异常处理程序,大大简化 VMM 的设计。

同时,Guest OS 对中断的屏蔽及解除可以不触发 VM exit,从而提高了性能。而且 VMM 还可以设置当 Guest OS 解除中断屏蔽时触发 VM exit,因此能够及时地转发积累的虚拟中断和异常。另外,纯软件实现的 VMM 目前缺少对 64 位客户操作系统的支持,而 CPU 的虚拟化技术除支持广泛的传统操作系统类型之外,还支持 64 位客户操作系统。硬件辅助虚拟技术提高了虚拟机的性能以及兼容性。   

需要注意的是,上文中我们提到了全虚拟化、半虚拟化和硬件辅助的全虚拟化,但这种分类实际上并不绝对,一个优秀的 VMM 往往融合了多项技术。例如 VMware Workstation 是一个著名的全虚拟化的 VMM,但是它使用了一种被称为动态二进制翻译(Binary Translation)的技术把对特权状态的访问转换成对影子状态的操作,从而避免了低效的 Trap-And-Emulate 的处理方式,这与半虚拟化相似,只不过半虚拟化是静态地修改程序代码。    

看得出硬件辅助虚拟化技术必然是未来的方向,Intel-VT 的处理器级虚拟化技术还需要进行以下优化:    

  • 提高操作模式间的转换速度:两种操作模式间的转换发生之如此频繁,如果不能有效减少其转换速度,即使充分利用硬件特性,虚拟机的整体性能也会大打折扣。早期的支持硬件辅助虚拟化技术的 Pentium 4 处理器需要花费 2409 个时钟周期处理 VM entry,花费 508 个时钟周期处理由缺页异常触发的 VM exit,代价相当高。随着 Intel 技术的不断完善,在新的 Core 架构上,相应时间已经减少到 937 和 446 个时钟周期。未来硬件厂商还需要进一步提高模式的转换速度,并提供更多的硬件特性来减少不必要的转换。

  • 优化翻译后援缓冲器(TLB)的性能:每次 VM entry 和 VM exit 发生时,由于需要重新加载 CR3 寄存器,因此 TLB(Translation Lookaside Buffer)被完全清空。虚拟化系统中操作模式的转换发生频率相当高,因此系统的整体性能受到明显损害。一种可行的方案是为 VMM 和每个虚拟机分配一个全局唯一 ID,TLB 的每一项附加该 ID 信息来索引线性地址的翻译。

  • 提供内存管理单元(MMU)虚拟化的硬件支持:即使使用 Intel-VT 技术,VMM 还是得用老办法来处理 Guest OS 中发生的缺页异常以及Guest OS 的客户机物理地址到主机物理地址的翻译,本质原因是 VMM 完全控制主机物理内存,因此 Guest OS 中的线性地址的翻译同时牵涉到 VMM 和 Guest OS 的地址空间,而硬件只能看到其中的一个。Intel 和 AMD 提出了各自的解决方案,分别叫做 EPT(Extended Page Table)和 Nested Paging。这两种技术的基本思想是,无论何时遇到客户机物理地址,硬件自动搜索 VMM 提供的关于该 Guest OS 的一个页表,翻译成主机物理地址,或产生缺页异常来触发 VM exit。

  • 支持高效的 I/O 虚拟化:I/O 虚拟化需要考虑性能、可用性、可扩展性、可靠性和成本等多种因素。最简单的方式是 VMM 为虚拟机模拟一个常见的 I/O 设备,该设备的功能由 VMM 用软件或复用主机 I/O 设备的方法实现。例如 Virtual PC 虚拟机提供的是一种比较古老的 S3 Trio64显卡。这种方式提高了兼容性,并充分利用 Guest OS 自带的设备驱动程序,但是虚拟的 I/O 设备功能有限且性能低下。

    为了提高性能,VMM 可以直接将主机 I/O 设备分配给虚拟机,这会带来两个主要挑战:1. 如果多个虚拟机可以复用同一个设备,VMM 必须保证它们对设备的访问不会互相干扰。2. 如果 Guest OS 使用 DMA 的方式访问 I/O 设备,由于 Guest OS 给出的地址并不是主机物理地址,VMM 必须保证在启动 DMA 操作前将该地址正确转换。Intel 和 AMD 分别提出了各自的解决方案,分别称为 Direct I/O(VT-d)和 IOMMU,希望用硬件的手段解决这些问题,降低 VMM 实现的难度。

三、参考

虚拟化技术发展编年史-36氪

  • 38
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不会写代码的小可爱&&

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值