Loading... > 现在市场上最常见的虚拟化软件有 VMWare workstation(VMWare) 、 VirtualBox(Oracle) 、 Hyper-V(Microsoft) 、 KVM(Redhat) 、 Xen 等,这些软件统称之为 VMM(Virtual Machine Monitor) ,使用不同的虚拟化实现。而这些虚拟化实现的方式可以分为全虚拟化、半虚拟化、硬件虚拟化等,本篇主要是理解这些虚拟化实现的原理。 --- # 虚拟化简介 虚拟化的目的:使用逻辑来表示资源,从而摆脱物理限制的约束,提高物理资源的利用率。 虚拟化的原理:在操作系统中加入一个虚拟化层( Hypervisor 或 VMM ),虚拟化层可以对宿主机硬件资源(物理CPU、内存、磁盘、网卡、显卡等)进行封装、隔离,抽象为另一种形式的逻辑资源,再提供给上层的客户机使用。所以 VMM 其实就是联系客户机和宿主机的一个中间件,当然虚拟化可以将一份资源抽象为多份,也可以将多份资源抽象为一份。 通过虚拟化技术实现的虚拟机一般被称之为 Guest ,而作为 Guest 载体的物理主机称之为 Host 。 # 虚拟化管理器 VMM 虚拟化通过虚拟化管理程序( Hypervisor 或 VMM )将物理服务器的硬件资源与上层应用进行解耦,形成统一的计算资源池,然后可弹性分配给逻辑上隔离的虚拟机共享使用。且 VMM 作为向上层 VM 提供底层硬件抽象的一层轻量级的软件,必须满足以下条件: 1. 等价性(Equivalence) :应用程序在 VMM 上的虚拟机执行,应与物理硬件上的执行行为相同。 2. 资源控制(Resource Control) :物理硬件由 VMM 全权控制, VM 上的应用程序不得直接访问硬件。 3. 有效性(Efficiency) :在虚拟执行环境中应用程序的绝大多数指令能够在 VMM 不干预的情况下,直接在物理硬件上执行。 基于 VMM 所在位置与虚拟化范围可以分三种类型。 ## Type1 裸金属型 VMM 直接运行的物理硬件上,穿过客户机,可直接管理和操作底层硬件,运行效率和性能较好,是当前主流的虚拟化类型,如开源的 Xen 以及VMware ESXi、Microsoft Hyper-V 等。 ## Type2 宿主型 在早期虚拟化产品中, VMM 运行在宿主机的 Host 上(如Windows),对硬件的管理与操作需要经过 Host 处理与限制,系统运行开销、效率与灵活性都不太好。主要的产品有 VMware Workstation , Windows Virtual PC 2004,Xen 3.0之前的版本等。 ## Type3 容器型 容器是一种更加轻量的应用级虚拟化技术,将应用的可执行文件及其所需的运行时环境与依赖库打包,实现一次构建,到处运行的目标。相比虚拟化,容器技术多了容器引擎层,如 Docker ,但上层应用无需与 Guest 绑定,可以实现秒级部署、跨平台迁移,灵活的资源分配,弹性调度管理等优势。容器、微服务与 DevOps 为云原生的三大要素,是推动企业技术中台建设与微服务化转型不可或缺的组件。 # 虚拟化原理 x86 架构的操作系统直接运行在硬件上,分为内核态和用户态。为了保障系统安全性、隔离性与稳定性,指定了 2 种特权级别的运行状态 Ring0 和 Ring3 ,其中 Ring 0 是内核态,权限最高,可执行**特权指令**(对系统资源进行管理与分配等)或**敏感指令**(如读写时钟、控制中断、修改内存页表、访问地址重定位系统,以及所有IO指令等)。 Ring3 是用户态,运行用户应用程序,只能执行非特权指令;需要执行特权或敏感的特权指令时通过系统调用方式从 Ring3 切换到 Ring0 ,内核完成相关操作后再切换返回。 那么问题就来了,没有 Guest 的情况下,ring0 只有 Host 一个是没有什么问题的。但如果存在 Guest ,它的内核也想在需要在 ring0 ,但是事实上是它不能占据 ring0 ,出现了冲突,这里首先对两类指令进行详细的说明。 ## 特权指令 这段摘自 SDM 卷 3第5.9节 以及表2.3,Intel 对特权指令的描述如下:一些系统指令的使用在应用程序中是被保护的,特权指令控制系统函数,只有 CPL 为 0 才能执行,否则会引发一个通用保护异常(#GP)。这些特权指令有: - LGDT 加载 GDTR - LLDT 加载 LDTR - LTR 加载 TR - LIDT 加载 IDTR - MOV 加载控制寄存器 - LMSW 加载机器状态字 - CLTS 清除 CR0 里的任务切换标志 - MOV 加载调试寄存器 - INVD 无效的缓存,不写回 - WBINVD 无效的缓存,同时写回 - INVLPG 无效的 TLB 入口 - HLT 处理器停机 - RDMSR 读取 MSR 寄存器 - WRMSR 写入 MSR 寄存器 - RDPMC 读取性能计数器 - RDTSC 读取时间戳计数器 其中, CR4 的 PCE TSD 标志位是否开启可以决定 RDPMC 和 RDTSC RDTSCP 是否能在任意 CPL执行,也就是可以在用户层执行这两个指令。 *后续新引入的部分指令 SDM 没明确说是不是特权指令。* ## 敏感指令 操作特权资源的指令,包括 - 修改虚拟机的运行模式或者物理机的状态; - 读写时钟、中断等寄存器; - 访问存储保护系统、地址重定位系统及所有的 I/O 指令。 总的来说,敏感指令包括特权指令,特权指令不包括敏感指令。 ## 特权解除-陷入模拟 虚拟化场景下,要求将 GuestOS 的内核特权解除,从原来的 0 降低到1 或者 3 。同时特权指令在 GuestOS 中发生的时候,就会产生 Trap ,被 VMM 捕获,从而由 VMM 完成。对敏感指令来说,这样的指令关乎到系统全局资源的状态读取或者设置。假设敏感指令同时也是一条特权指令,在非特权模式执行的时候会引发硬件陷入特权模式的 ring0 ,那么也就可以通过 VMM 来处理,这就是典型的特权解除和陷入模拟(Privilege deprivileging/Trap-and-Emulation)。我们显然是可以完美通过这种“陷入-模拟”的方法来实现虚拟化的。实际上,大部分敏感指令确实是特权指令,但无论是早期的 X86 ,还是 ARM ,都有些敏感指令不是可以陷入的特权指令,我们称呼它们为临界指令(Critical Instruction),不陷入就无法模拟,是要重点考虑的对象。 特权解除:也称之为翻译,当 GuestOS 需要调用运行在核心态的指令时, VMM 就会动态的将核心态指令捕获并调用若干运行在非核心态的指令来模拟出期望得到的效果,从而将核心态的特权解除。解除了核心态的特权后,就能够在 GuestOS 中执行大部分的核心态指令了。但是,这仍然不能完美的解决问题。因为在一个 OS 的指令集中还存在敏感指令,此时就需要陷入模拟的实现。 陷入模拟:无论是 HostOS 还是 GuestOS ,只要是一个 OS 都必然会存在有敏感指令。如果在 GuestOS 中执行了 reboot 指令,但是却将 HostOS 给重启了,这将会非常糟糕。 VMM 的陷入模拟机制就是为了解决这个问题。在 GuestOS 中执行了敏感指令 reboot 时, VMM 首先会将敏感指令 reboot 捕获、检测并判定其为敏感指令。此时 VMM 就会陷入模拟,将敏感指令 reboot 模拟成一个只针对 GuestOS 进行操作的、非敏感的、并且运行在非核心态上的 reboot 指令,最后 CPU 执行虚拟机的重启操作。 ## 虚拟化实现方案 - 全虚拟化(Full Virtualization) - 半/超虚拟化(Para virtualization) - 硬件辅助虚拟化(Hardware-assisted virtualization) 在虚拟化发展的早期主要以**全虚拟化**和**半虚拟化**两大流派为主,两者各有优缺点。如果能适当的将其应用不同的环境中,就能充分的发挥两者的特性以获得更高的收益。随着这两大流派的分歧和竞争愈演愈烈,由 Intel 领衔的硬件厂商也纷纷加入到虚拟化的浪潮中,开启了(大航海时代)硬件虚拟化的时代,从此全虚拟化和半虚拟化前进的道路逐渐有了靠拢的趋势。 再到后来的第二代的内存虚拟化 EPT 、第三代的 IO 虚拟化 VT-d 的出现和兴起,当下虚拟化市场已经不再以单纯卖卖虚拟化软件为主要盈利手段,而是将虚拟化技术整合在更大、更完善的虚拟化平台解决方案中。 # 全虚拟化(Full Virtualization) ## CPU 虚拟化 GuestOS 可以直接在全虚拟化 VMM 上运行而不需要对 GuestOS 本身的核心代码做任何修改,全虚拟化的 GuestOS 具有完全的物理机特性。既 VMM 会为 GuestOS 抽象模拟出它所需要的包括 CPU 、磁盘、内存、网卡、显卡等抽象硬件资源,所以全虚拟化的 GuestOS 并不会知道自己其实是一台虚拟机。 由于全虚拟化 VMM 会频繁的捕获这些核心态的和敏感的指令,将这些指令进行转换之后,再交给 CPU 执行。所以经过了两重转换,导致其效率会比半虚拟化低,但全虚拟化 VMM 应用程序的好处在于其不需要对 GuestOS 的核心源码做修改,所以全虚拟化的 VMM 可以安装绝大部分的 OS 。典型的全虚拟化软件有 —— VMWare、Hyper-V、KVM-x86(复杂指令集)。 全虚拟化的两种实现方式: 1. 基于二进制翻译的全虚拟化,对于那17条敏感指令,采用二进制代码翻译的技术,不改代码,比如看到 ABC 这样的敏感而非特权指令,提前插入断点来截获,交由 VMM 解释执行,我们就把它强行翻译为别的东西(模拟),所以是不用修改 Guest OS 的,但是动态翻译会带来一些额外的开销。其实这个也有那么一点类似半虚拟化,可以认为半虚拟化的改代码在编译前,而二进制翻译的改代码在运行时。 ## 内存虚拟化 每次对内存的访问都要先从 GVA 到 GPA,再从 GPA 到 HPA ,但这和已有的架构不兼容( x86 的 CR3 、 MMU 都是为一次地址转换设计的)。于是有人提出直接维护 GVA 到 HPA 的映射,即影子页表(Shadow Page Table, SPT)。但是 GVA 到 GPA 的映射是在不断变化的,如何能够让影子页表保持最新的映射呢?解决方法非常巧妙,将 GVA 到 GPA 的页表设为只读,那么每次 Guest OS 要改页表时会触发 page fault ,陷入到 VMM ,然后 VMM 就知道要去更新影子页表了,于是影子页表根据新的 GVA 到 GPA 的映射,修改 GVA 到 HPA 的映射。这样在进行内存访问时只需进行一次地址转换,故 MMU 可以通过影子页表进行寻址,此时 CR3 只须指向 SPT 即可。 但是这样每个进程有一张页表,假设有 10 台 VM ,每台VM上运行 100 个进程,那么就需要维护 10 * 100 = 1000 个影子页表。这耗费了大量的空间,同时更新影子页表的开销也不容小视。实现起来也非常复杂。 ## IO 虚拟化 I/O 虚拟化的目标是保障 VM 的 IO 隔离与正常高效的执行。全虚拟化通过对磁盘和网卡 IO 设备模拟,每次 IO 通过异常陷入 Trap 到 VMM 来执行,如ESXi。 对于 Guest OS 来说,它并不知道设备是虚拟出来的,于是会通过自带的设备驱动去访问 VMM 模拟的 IO 设备。对于端口 I/O ,由于是特权指令,会 trap 到 VMM 中,交给设备模拟器进行模拟;对于 MMIO , VMM 把映射到该 MMIO 的页表设为无效,访问时会触发 page fault 并 trap 到 VMM 中,交给设备模拟器进行模拟;对于中断,设备模拟器在接收到物理中断并需要触发中断时,发送一个虚拟中断给 Guest OS 。 # 半/超虚拟化(Para virtualization) ## CPU 虚拟化 直接在 Guest OS 里面把无法虚拟化的部分代码改掉,把 ABC 指令替换成一个陷入 ring0 的系统调用,既然你不陷入,就强行进行陷入,这种操作转而调用 VMM 提供的特殊 API (hypercall) 来进行模拟 ,这就是半虚拟化。 **可以直接调用硬件,不需要VMM转化,效率高,需要修改内核。** 半虚拟化是需要 GuestOS 协助的虚拟化。因为在半虚拟化 VMM 中运行的 GuestOS ,都需要将其内核源码进行都进过了特别的修改。半虚拟化 VMM 在处理敏感指令和内核态指令的流程上相对更简单一些。主要是修改 GuestOS 指令集中的敏感指令和核心态指令。让HostOS在捕抓到没有经过半虚拟化 VMM 模拟和翻译处理的 GuestOS 内核态指令或敏感指令时, HostOS 也能够准确的判断出该指令是否属于 GuestOS (GuestOS 知道自己是虚拟机)。这样就可以高效的避免了上述问题。典型的半虚拟化软件有—— Xen、KVM-PowerPC(简易指令集)。 ## 内存虚拟化 改 Guest OS ,让其知道自己运行在虚拟化环境中,从而直接去访问“不连续”的 HA 。在这种情况下,PA 到 HPA 的页表保存在 Guest OS 中, Guest OS 进行内存访问时能够自己完成 VA-PA-HPA 的转换,然后 MMU 通过 VA-HPA 进行寻址。为了保证安全性,对页表进行修改需要通过 hypercall 调用 VMM 来进行。VM 运行过程中 VMM 不断管理和维护该页表,确保 VM 能直接访问到合适的地址。 ## IO 虚拟化 半虚拟化中可通过前端 Front-end 和后端 Back-end 驱动的方式实现,比如 Xen 在 Dom U(Guest OS)中安装前端 IO 驱动,同时在管理虚拟机 Dom 0 中安装后端 IO 驱动。由其负责将请求发送到真正的物理设备驱动上,在完成请求后,沿原路将结果传递给 Guest OS 。需要对操作系统进行修改。 # 硬件辅助虚拟化(Hardware-assisted Virtualization) ## CPU虚拟化 2005年 — Intel 提出并开发了由 CPU 直接支持的虚拟化技术。这种虚拟化技术引入新的 CPU 运行模式和新的指令集,使得 VMM 和 GuestOS 运行于不同的模式下( VMM=Root Mode;GuestOS=Non-Root Mode), GuestOS 运行于受控模式,原来的一些敏感指令在受控模式下会全部陷入 VMM ,由 VMM 来实现模拟,这样就解决了部分非内核态敏感指令的陷入——模拟难题,而且模式切换时上下文的保存恢复由硬件来完成,这样就大大提高了陷入——模拟时上下文切换的效率 。该技术的引入使 x86 CPU 可以很容易地实现完全虚拟化。故皆被几乎所有之前分歧的各大流派所采用,包括 KVM-x86,VMWare ESX Server 3,Xen 3.0 。 GuestOS 需要执行特权指令时通过 VMCALL 调用 VMM 的服务,系统自动挂起 GuestOS ,切换到 Root 模式执行,该过程叫 VM Exit 。 VMM 也可以调用 VMLaunch 或 VMResume 指令切换到 Non-root 模式,系统自动加载运行 GuestOS ,该过程叫 VM Entry 。 特点: 1. 允许在没有修改Kernel的OS上运行 2. 提供完整的裸机,能够将所有的设备都进行虚拟化(虚拟硬件+虚拟硬件驱动) 3. 虚拟机的所有硬件都被 Hpyervisor 管理 4. 每个虚拟机都是独立的,拥有虚拟的整套设备环境 5. 除了特权指令,一般指令均由物理机 CPU 解析,特权指令会被 Hypervisor 捕获,并替换成安全指令后再交由物理机 CPU 处理执行。 6. 所有的特权指令均运行在 Kernel 的特权级中。 ![[vmx.jpg]] ROOT 模式和 NON-ROOT 模式切换。 ![[vmx2.jpg]] ## 内存虚拟化 硬件辅助内存虚拟化有Intel扩展页表EPT(Extend Page Table)和AMD 嵌入页表NPT(Nested Page Table)。EPT/NPT 是内存管理单元 MMU 的扩展, CPU 硬件一个特性,通过硬件方式实现 GuestOS GVA 到 HA 的转换,比影子页表的系统开销更低,性能更高。 EPT 引入了 EPT 和 EPT base pointer , EPT 中存储着 GPA 到 HPA 的映射,而 EPT base pointer 负责指向 EPT 页表。当进行地址转换时,根据 CR3 找到 VA-PA 的页表进行 GVA 到 GPA 的转换,然后根据 EPT base pointer 找到 EPT ,进行 GPA 到 HPA 的转换。 ## IO 虚拟化 硬件辅助 IO 虚拟化有 Intel VT-d 与 AMD IOMMU 。 VMM 将 IO 设备分配给特定 GuestOS ,数据直接写入设备,减少 VMM 参与 IO 处理,提升了性能。在北桥中内置提供 DMA 虚拟化和 IRQ 虚拟化硬件,通过硬件层的映射使得虚拟机内的 IO 请求直接映射到实际硬件上,让 VM 以独占的方式直接使用设备。同时每个设备在系统内存中都有一个专用区域,只有对应的 GuestOS 才能访问,增强了系统安全性与可用性。 # 虚拟化技术总结 虚拟化主要技术对比 ![[compare.jpg]] ESXi 虚拟化的核心任务都是通过ESXi内核完成。Xen的CPU和内存虚拟化通过Xen内核完成,磁盘与网络IO虚拟化,以及虚拟化管理与调度由主机上最先启动的管理VM Dom0完成;Dom U就是用户VM,其中HVM Guest(Windows)是全虚拟化,PV Guest(Linux)是半虚拟化。Hyper-V架构Xen的架构非常相似,Xen中的Dom0角色在Hyper-V 叫做Parent Partition。Hyper-V采用微内核,VMM运行在ring -1(根模式),设备驱动和GuestOS运行在Ring0,所以Hyper-V内核比较轻量,安全性和性能上有一定优势。作为Linux的一个内核模块,KVM 架构更加不同,CPU和内存虚拟化通过KVM内核完成,磁盘与网络IO虚拟化通过QEMU完成,每个VM是一个QEMU进程,由Linux进程调度器管理。 ![[compare2.jpg]] 主流虚拟化对比 ![[compare3.jpg]] 全虚拟化采用模拟的方式来实现虚拟化,努力提供一个native的环境供虚拟机运行,无需Guest OS进行修改。 半虚拟化采用修改Guest OS的方式来实现虚拟化,让Guest OS知道自己运行在虚拟环境下,然后与VMM协同工作。 而硬件辅助虚拟化通过硬件解决了全虚拟化中的大部分问题。使得全虚拟化的性能大大提升,于是越来越成为主流。 # 参考文章 [x86虚拟化概述](https://www.binss.me/blog/An-overview-of-the-virtualization-of-x86/) [KVM最初的2小时——KVM从入门到放弃](https://blog.csdn.net/juS3Ve/article/details/101572315) [计算虚拟化详解](https://zhuanlan.zhihu.com/p/100526650) [虚拟化的发展历程和实现方式](https://www.cnblogs.com/jmilkfan-fanguiju/p/7533719.html) [Linux_RHEV虚拟化_基础理论&KVM](https://www.cnblogs.com/jmilkfan-fanguiju/p/11825195.html) ``` ``` Last modification:January 16th, 2021 at 01:29 pm © 允许规范转载 Support 确定不打赏一下支持博主吗 ×Close Appreciate the author Sweeping payments Pay by AliPay