KVM(Kernel-Based Virtual Machine)是基于Linux内核的虚拟机。2006年10月,由以色列的Qumranet组织开发的一种新的“虚拟机”实现方案。2007年2月发布的Linux2.6.20内核第一次包含了KVM。
下图是KVM,VMM,Guest通信的流程:
在3者通信中,KVM主要做了两件事儿:
- 把VMM的一些配置,配置到guest上
- 把guest的一些events根据情况交给VMM处理
CPU Virtualization:
VCPU的运行概述:
Guest OS运行起来后VMM就会产生一个VCPU,VCPU靠一个thread运行。VMM实际是HOST中的一个进程,而VCPU就是其中一个thread。VMM调用VCPU运行的API后,会陷入内核,VCPU就开始运行了,此时VCPU运行在guest中。KVM中会调用VM运行的API让VM运行起来,如果此时产生什么events,那么KVM中调用VM退出的API,使VM停止,检查原因,如果KVM能fix这个events,那么在KVM中fix,否则退到VMM中,如果能fix就fix,VCPU重新运行,否则VCPU停止。
也有一些APIs可以读写VCPU的属性,当然也是通过ioctl函数,传递的参数如:KVM_SET_REG,KVM_GET_REG,KVM_GET_SREG ,KVM_GET_SREG.......
Memory Virtualization:
可以通过API注册guest physical address到host userspace address的映射。
ioctl(vm_fd, KVM_SET_USER_MEMORY_REGION,....)
-guest_phys_addr, memory_size, userspace_addr, ...
这里guest的physical address实际映射到host的虚拟内存,而不是物理内存
这里看一下虚拟地址到物理地址的转换,shadow page与EPT:
在Guest OS中要访问physical address的时候,会产生page fault,这时KVM会根据Guest OS的page table填充shadow page。shadow page中存放了Guest OS virtual address到HOST OS physical address的转换。这里的write-protected主要的作用是写Guest OS的page table的时候会产生异常,这样就会同步shadow page了。Guest CR3是不会被加载的,只用于帮助KVM??修正shadow page,实际加载的是shadow page的CR3。
这个是EPT,是intel的地址转换技术。EPT也是由KVM一层层修正的。
shadow page与EPT比较:
- shadow page会比较浪费,每次context swtich shadow page就没了,但是如果一个进程如果有多个线程,那么可以共享这个进程的shadow page
- 所有VCPU都加载同一个EPT,如果转换被cache了可以直接用,否则去EPT中找。如果EPT没有,则VCPU退出到guest,进入KVM中修正
在有EPT支持的时候,就可以关掉shadow page了。
IO Virtualization:
当访问guest IO的时候会引发VM-EXIT,然后KVM会解释这个退出event,进行模拟
当从VMM中退出后,检查时候是否是IO访问引起的,如果是获得端口号,长度和方向。如果是端口是被KVM emulate的,则进入KVM中emulate。如果端口是被VMM emuate的,则进入VMM中emulate。那么IO emulate放在KVM中比较好呢,还是放在VMM中emulate比较好呢?比如VM-EXIT了,此时VCPU要退出guest,进入KVM,如果不是KVM emulate的,那么则进入VMM,处理完后再退入KVM,然后再退入guest。如果emulate在KVM中,就省去了进入VMM了。
MMIO的emulation:
VMCS是Intel-x中一个很重要的数据结构,它占用一个page大小,由VMM分配,但是硬件是需要读写的,有点类似于页表。vmcs的各个域在manual中有说明,但是每个域在vmcs页中的具体位置是不知道的,程序员不用关心,只需要用相应的VMWRITE和VMREAD指令去访问。这样做的好处是,vmcs页中结构的具体layout可以透明的进行变动。
CPU虚拟化
3.1 基本原理
CPU虚拟化是VMM中最核心的部分,由于内存和IO访问的指令都是敏感指令,所以内存和IO虚拟化都依赖于CPU虚拟化的实现。CPU虚拟化的目标是让虚拟机中执行的所有敏感指令都能产生异常而“陷入”,并由VMM进行模拟。VMM的陷入是通过CPU的保护机制、中断或异常来完成的。通常,VMM的陷入方式有如下3种:
1、 由CPU的保护机制触发。CPU在执行敏感指令之前,会检查执行条件是否满足,执行条件主要包括:当前特权级别、运行模式、内存映射关系等,只要有任一条件不满足,就会VM-Exit陷入到VMM进行进一步处理。
2、 异步中断。包括处理器内部的中断源和外设的中断源,当中断信号到达CPU时,CPU会强行中断Guest OS当前执行的指令,然后VM-Exit到VMM注册的中断服务程序进行进一步处理。
3、 虚拟机主动触发的异常,也就是通常所说的陷阱。虚拟机可以通过陷阱指令主动VM-Exit到VMM中。
在KVM虚拟化解决方案中,利用了硬件的虚拟化特性(比如Intel VT-x和AMD SVM)实现CPU的虚拟化。如之前介绍,VT-x提供了一套称作VMX的新的工作模式,工作在该模式下的处理器又具有两类操作模式:VMX root operation和VMX non-root operation。对操作系统来说,VMX non-root operation模式与传统的x86处理器兼容,最大的差别在于当虚拟机执行一些访问全局资源的指令时将导致虚拟机退出操作(VM-Exit),从而使虚拟机监控器获得控制权,以便对访问全局资源的指令进行模拟。以后,虚拟机监控器可以通过虚拟机进入操作(VM-Entry)使虚拟机重新获得控制权。
其次,VT-x为系统编程接口状态的切换提供硬件支持。VT-x为每个虚拟机维护至少一个VMCS(Virtual Machine Control Structure)结构,其中保存了虚拟机和虚拟机监控器的系统编程接口状态。当执行VM exit和VM entry操作时,VT-x自动根据VMCS中的内容完成虚拟机和虚拟机监控器间的系统编程接口状态切换。另外,VT-x还提供了一组指令,使得虚拟机监控器通过一条指令就可以完成虚拟机间的切换。
3.2 VCPU
硬件虚拟化使用VCPU(Virtual CPU)描述符来描述虚拟CPU,VCPU描述符与OS中进程描述符类似,本质是一个结构体,其中包含如下信息:
ü VCPU标识信息,如VCPU的ID号,VCPU属于哪个Guest等。
ü 虚拟寄存器信息,在VT-x的环境中,这些信息包含在VMCS中。
ü VCPU状态信息,标识白VCPU当前所处的状态(睡眠、运行等),主要供调度器使用。
ü 额外的寄存器/部件信息,主要指未包含在VMCS中的寄存器或CPU部件,比如:浮点寄存器和虚拟的LAPIC等。
ü 其他信息:用户VMM进行优化或存储额外信息的字段,如:存放该VCPU私有数据的指针。
总体来说,VCPU可以划分为两部分:
1、 以VMCS为主,由硬件来使用和更新的部分,主要是虚拟寄存器。
2、 除VMCS之外,由VMM来使用和更新的部分。如VCPU标识、状态信息等。
当VMM创建虚拟机时,首先要为虚拟机创建VCPU,整个虚拟机的运行实际上可以看做VMM调度不同的VCPU运行。
3.3 中断虚拟化
在物理平台上,中断架构和外部中断处理流程如下图所示:
大致处理过程为:
ü IO设备通过中断控制器(IO APIC或PIC)向CPU发送中断请求
ü IO APIC将中断转发至目标CPU和Local APIC
ü 目标APIC对该中断进行处理
在虚拟化环境中,VMM为Guest OS虚拟了一个与物理中断架构类似的虚拟中断架构,如下图所示:
每个VCPU对应一个虚拟Local APIC,用于接收中断,同时还模拟了虚拟IO APIC(或虚拟PIC)用于接收外设(虚拟设备)发出的中断请求并进行转发。虚拟Local APIC、虚拟IO APIC、虚拟PIC等都是VMM中的软件实体(对应于相应的数据结构)。主要处理流程如下:
ü 当虚拟设备需要发送中断时,虚拟设备会调用虚拟IO APIC的接口发送中断;
ü 而虚拟IO APIC根据中断请求,选出适合的虚拟Local APIC,并调用其接口发送中断请求;
ü 虚拟Local APIC进一步利用CPU硬件虚拟化功能(Intel VT-x或AMD SVM)的事件注入机制,将中断注入到相应的VCPU;
ü 当相应VCPU得到调度时,即处理相应的中断。
3.4 调度
在KVM虚拟化环境中,KVM虚拟机作为一个系统进程运行,因此虚拟机的的调度,实际上就是利用了Linux自身的调度器完成。本文不做详述。
4 内存虚拟化
4.1 客户机物理地址空间
在物理机上,虚拟地址通过Guest页表即可转换为物理地址。但是在虚拟化环境中,由于VMM和VM都需要独立的地址空间,则产生了冲突。
为实现内存虚拟化,让客户机使用一个隔离的、从零开始且具有连续的内存空间,KVM引入一层新的地址空间,即客户机物理地址空间 (Guest Physical Address, GPA),该地址空间并不是真正的物理地址空间,它只是宿主机(Host主机)虚拟地址空间在Guest地址空间的一个映射。对Guest来说,客户机物理地址空间都是从零开始的连续地址空间,但对于宿主机来说,客户机的物理地址空间并不一定是连续的,客户机物理地址空间有可能映射在若干个不连续的宿主机地址区间,如下图所示:
由于物理MMU只能通过Host机的物理地址(Host Physical Address, HPA)进行寻址,所以实现内存虚拟化,关键是需要将Guest机的虚拟地址(Guest Virtual Address, GVA)转换为HPA。传统的实现方案中,这个过程需要经历:GVAàGPAàHVAàHPA的转换过程,需要对地址进行多次转换,而且需要KVM的介入,效率非常低。为提供GVA到HPA的地址转换效率,KVM提供了两种地址转换方式:
1、 影子页表(Shadow Page Table),是纯软件的实现方式
2、 基于硬件特性的地址转换。如基于Intel EPT(Extended Page Table,扩展页表),或AMD NPT(Nested Page Table,嵌套页表)
4.2 影子页表
4.2.1基本原理
由于内存虚拟化在将GVA转换为HPA的过程中,需要经历多次转换,无法直接使用Guest机页表和CR3。使用影子页表(Shadow Page Table)可以实现客户机虚拟地址(GVA)到宿主机物理地址(HPA)的直接转换,与传统方式的转换过程对比如下:
影子页表中记录的是GVA跟HPA的对应关系,每个页表项指向的都是宿主机的物理地址。由于客户机中每个进程都有自己的虚拟地址空间,所以 KVM需要为客户机中的每个进程页表都要维护一套相应的影子页表。在Guest机访问内存时,VMM在物理MMU中载入的是Guest机当前页表所对应的影子页表,从而实现GVA到HPA的直接转换。
Guest机中的每一个页表项都有一个影子页表项与之相对应。为了快速检索Guest机页表所对应的的影子页表,KVM为每个客户机都维护了一个哈希表,影子页表和Guest机页表通过此哈希表进行映射,基本原理如下:
对于每一个Guest机来说,Guest机的页目录/页表都有唯一的GPA,通过页目录/页表的GPA就可以在哈希链表中快速地找到对应的影子页目录/页表。在检索哈希表时,KVM把Guest页目录/页表的客户机物理地址低10位作为键值进行索引,根据其键值定位到对应的链表,然后遍历此链表找到对应的影子页目录/页表。当然,如果没有找到对应的影子页目录/页表,则说明影子页表项和Guest页表项的对应关系还没有建立,此时KVM会为其分配新的物理页,并建立起Guest页目录/页表和对应的影子页目录/页表之间的映射。
4.2.2影子页表的建立与更新
影子页表的建立和更新过程交织在一起,影子页表的建立和更新主要发生在如下3中情况下:
1、 Guest OS修改Guest CR3寄存器。由于相关指令为敏感指令,所以相关操作会被VMM截获,此时VMM会根据相关情况进行影子页表的维护。比如,当客户机切换进程时,客户机操作系统会把待切换进程的页表基址载入 CR3,而该特权指令将被VMM截获,进行新的处理,即在哈希表中找到与此页表基址对应的影子页表基址,载入客户机 CR3,使客户机在恢复运行时 CR3实际指向的是新切换进程对应的影子页表。
2、 因Guest机页表和影子页表不一致而触发的缺页异常,此时也会VM-Exit到VMM,进而可进行相关维护操作。
3、 Guest OS中执行INVLPG指令刷新TLB时,由于INVLPG指令为敏感指令,所以该操作也会被VMM进行截获,并进行影子页表相关维护操作。
其中,第2中情况发生几率最高,相关处理也最复杂。如下做重点描述。不同的缺页异常,处理方式不用,常见的缺页异常包括如下3类:
1、 影子页表初始化时产生的缺页异常。在虚拟机运行之初,VMM中与Guest机页表对应的影子页表都没有建立,而物理CR3中载入的却是影子页目录地址,所以,此时任何的内存操作都会引发异常,如果此时Guest机的相应页表已经建立,那么处理这种异常即是建立相应的影子页表即可;如果Guest机的页表项尚未建立,那就是Guest机自身的缺页异常,即为如下的第2中情况。
2、 Guest机上的缺页异常。如果Guest OS尚未给这个GVA分配Guest机物理页,即相应的Guest机页表项尚未建立,此时将引发缺页异常。另外,当Guest机访问的Guest页表项存在位(Present Bit)为0,或相关访问权限不匹配时,也将引发缺页异常。
3、 VMM将Host机物理页换出到硬盘上时引发的缺页异常。
影子页表缺页异常的默认处理流程
VMM截获缺页异常(VM-Exit),并检查此异常是否由Guest即自身引发,如果是,则将直接返回Guest OS(Vm-Entry),然后由Guest OS自身的page fault流程处理;如果不是,则为影子页表和Guest机页表不一致导致,这样的异常也叫“影子缺页异常”,此时,VMM会根据Guest机页表同步影子页表,过程如下:
1、 VMM根据Guest机页表项建立影子页目录和页表结构
2、 VMM根据发生缺页异常的GVA,在Guest机页表的相应表项中得到对应的GPA
3、 VMM根据GPA,在GPA与HPA的映射表中(通过之前描述的HASH表建立),得到相应的HPA,再将HPA填入到影子页表的相应表项中。
影子页表和Guest机页表不是时刻同步的,只有在需要时才进行通过,从某种角度看,影子页表可以看做是Guest页表的TLB,常称为虚拟TLB(VTLB)。
影子页表解决了传统IA32架构下的内存虚拟化问题,由于影子页表可被载入物理 MMU为客户机直接寻址使用,所以客户机的大多数内存访问都可以在没有 KVM 介入的情况下正常执行,没有额外的地址转换开销,也就大大提高了客户机运行的效率。但也有比较明显的缺点:
1、 实现复杂。影子页表同步需要考虑各种情况。
2、 内存开销大。需要为每个Guest机进程维护一个影子页表。
4.3 EPT 页表
4.3.1基本原理
为解决影子页表的问题,Intel和AMD都提供了相应的硬件技术,直接在硬件上支持GPA到HPA的转换,从而大大降低了内存虚拟化的难度,并提升了相关性能。本文主要描述Intel EPT技术。
EPT 技术在原有Guest机页表对GVA到GPA转换的基础上,又引入了 EPT 页表来实现GPA到HPA转换,这两次地址转换都是由硬件自动完成,可高效的实现地址转换。Guest运行时,Guest页表被载入 CR3,而 EPT页表被载入专门的 EPT页表指针寄存器 EPTP。从GVA到HPA的具体转换过程如下(以经典的2级页表为例):
完整的地址翻译流程描述为:
1、 Guest OS加载Guest进程的gCR3,gCR3中存放的是Guest进程页目录表的GPA。
2、 处于非根模式的CPU的MMU查询硬件EPT TLB,如果有所请求的GPA到HPA的映射,则使用其对应的HPA作为Guest页目录表的基址。
3、 如没有所请求的GPA到HPA的映射,则查询EPT,获得gCR3所映射的HPA,并将其作为Guest页目录表的基址。
4、 根据GVA获得页目录偏移(图中的Dir Offset),获得用于索引Guest页表的基址,该地址为GPA。
5、 再由VCPU的MMU查询硬件EPT TLB,如果有所请求的GPA到HPA的映射,则使用其对应的HPA作为Guest页表的基址。
6、 如没有所请求的GPA到HPA的映射,则查询EPT,将其转换为HPA,使用该HPA再加上GVA中的页表偏移(图中的Table Offset),即可得到PTE(页表项)的GPA。
7、 再由VCPU的MMU查询硬件EPT TLB,如果有所请求的GPA到HPA的映射,则其对应的HPA加上GVA中的Offset即为最终的宿主机物理地址(HPA)。
8、 如没有所请求的GPA到HPA的映射,则查询EPT,将其转换为HPA,使用该HPA加上GVA中的Offset即为最终的宿主机物理地址(HPA)。
EPT页表实现GPA到HPA的转换的原理,与Guest页表实现GVA到GPA的转换原理相同,需要经历多级页表的查询,图中没有详细画出。假设Guest机有m级页表,宿主机EPT有n级,在TLB均miss的最坏情况下,会产生m*n次内存访问,完成一次客户机的地址翻译,EPT硬件通过增大硬件EPT TLB来尽量减少内存访问。
4.3.2 EPT缺页异常处理
在GPA到HPA转换的过程中,由于缺页、写权限不足等原因也会导致客户机退出,产生 EPT异常。对于 EPT 缺页异常,处理过程大致如下:
1、 KVM首先根据引起异常的GHA,映射到对应的HVA;
2、 然后为此虚拟地址分配新的物理页;
3、 最后 KVM再更新 EPT页表,建立起引起异常的GPA到HPA的映射。
EPT 页表相对于影子页表,其实现方式大大简化,主要地址转换工作都由硬件自动完成,而且Guest内部的缺页异常也不会导致VM-Exit,因此Guest运行性能更好,开销更小。
1、虚拟化概念(Virtualization)
2、虚拟机(Virtual Machine)
- 同质:量可以不同,但质与物理机本质相同,例如CPU的ISA(指令集架构,Instruction Set Architecture)必须相同;
- 高效:性能与物理机接近,大多数指令应在硬件直接执行,只有少量由VMM模拟处理),因此wine这类模拟器不是VM;
- 资源可控,VMM对物理机,虚拟机的资源绝对可控;
3、完全虚拟化和半虚拟化(Full Virtualization & Paravirtualization)
- 完全虚拟化是指所抽象的 VM 具有完全的物理机特性,OS 在其上运行不需要任何修改,非常方便,但它的缺点是效率不高(特别是在Intel VT & AMD-V 硬件虚拟化支持前)。典型的有 VMWare, Virtualbox, Virtual PC, KVM-x86等。
- 半虚拟化需 OS 协助的 虚拟化,在其上运行OS 需要修改。起初采用主是为了解决 x86 体系结构上完全虚拟化的困难(没有 Intel VT & AMD-V 硬件虚拟化支持前;且不屑于动态扫描指令修补之方法的性能),后来则主要是为了提高虚拟化效率。典型有 Xen, KVM-PowerPC 等。
从上图可以看出,Guest OS是运行在用户态上,当执行到特权指令时,会陷入到 VMM 模拟执行(陷入-模拟)。从这个角度上来说,完全虚拟化是指当Guest OS运行到特权指令时,VMM将特权指令”翻译“成陷入指令,这个”翻译“过程对系统性能有损耗。而半虚拟化是指事先修改 Guest OS 的代码,避免Guest OS执行特权执行的过程,因此它的性能接近于物理机。
4、主要虚拟化流派
了解了完全虚拟化和半虚拟化的本质之后,下来再来看看主要的虚拟化流派。
- 基于二进制翻译的全虚拟化:其主要思想是在执行时将 VM 上执行的 Guest OS 之指令,翻译成 x86 ISA 的一个子集,其中的特权指令被替换成陷入指令;翻译过程与指令执行交叉进行,不含特权指令的用户态程序可以不经翻译直接执行,该技术为 VMWare Workstation,VMWare ESX Server 早期版本,Virtual PC 以及 QEMU 所采用。
- 基于扫描和修补的全虚拟化:VMM 会在 VM 运行每块指令之前对其扫描,查找敏感指令;补丁指令块会在 VMM 中动态生成,通常每一个需要修补的指令会对应一块补丁指令;特权指令被替换成一个外跳转,从 VM 跳转到 VMM,在 VMM 中执行动态生成的补丁指令块;当补丁指令块执行完后,执行流再跳转回 VM 的下一条指令处继续执行;Oracle 的 Virtualbox 即采用该技术。
- OS协助的半虚拟化:通过修改 Guest OS 的代码,将含有敏感指令的操作,替换为对 VMM 的超调用 (Hypercall,类似 OS 的系统调用,可将控制权转移到 VMM);该技术的优势在于 VM 的性能能接近于物理机,缺点在于需要修改 Guest OS;该技术因 Xen 项目而广为人知。
- 硬件协助的虚拟化:引入新的处理器运行模式和新的指令,使得 VMM 和 Guest OS 运行于不同的模式下,Guest OS 运行于受控模式,原来的一些特权指令在受控模式下全部会陷入 VMM,这样就解决了部分非特权的敏感指令的陷入 — 模拟难题,而且模式切换时上下文的保存恢复由硬件来完成,这样就大大提高了陷入 — 模拟时上下文切换的效率。该技术的引入使 x86 可以很容易地实现完全虚拟化。故皆被几乎所有之前分歧流派各方所采用,如: KVM-x86,新版 VMWare ESX Server 3,Xen 3.0。
5、VMM模型(Virtual Machine Monitor)
1)宿主模型(OS-hosted VMM)
VMM 通过调用 Host OS 的服务来获得资源,实现 CPU,内存和 I/O 设备的虚拟化,VMM 创建出 VM 后,通常将 VM 作为 Host OS 的一个进程参与调度;VMM 模块负责 CPU 和内存虚拟化,由 ULM 请求 Host OS 设备驱动,实现 I/O 设备的虚拟化。
优点:可以充分利用现有 OS 的设备驱动,VMM 无需自己实现大量的设备驱动,轻松实现 I/O 设备的虚拟化。
缺点:资源受 Host OS 控制,VMM 需调用 Host OS 的服务来获取资源进行虚拟化,其效率和功能会受到一定影响。
采用该结构的 VMM 有:VMware Workstation, VMWare Server (GSX), Virtual PC,Virtual Server, KVM(早期)
2)Hypervisor 模型 (Hypervisor VMM)
该结构中,VMM 可以看作一个为虚拟化而生的完整 OS,掌控有所有资源(CPU,内存,I/O 设备),VMM 承担管理资源的重任,其还需向上提供 VM 用于运行 Guest OS,因此 VMM 还负责虚拟环境的创建和管理。
优点:因 VMM 同时具有物理资源的管理功能和虚拟化功能,故虚拟化的效率会较高;安全性方面,VM 的安全只依赖于 VMM 的安全。
缺点:因 VMM 完全拥有物理资源,因此,VMM 需要进行物理资源的管理,包括设备的驱动,而设备驱动的开发工作量是很大的,这对 VMM 是个很大的挑战。
采用该结构的 VMM 有:VMWare ESX Server, WindRiver Hypervisor, KVM(后期)
3)混合模型((Hybrid VMM)
该结构是上述两种模式的混合体,VMM 依然位于最底层,拥有所有物理资源,但 VMM 会主动让出大部分 I/O 设备的控制权,将它们交由一个运行在特权 VM 上的特权 OS 来控制。VMM 只负责 CPU 和内存的虚拟化,I/O 设备的虚拟化由 VMM 和特权 OS 共同完成.
优点:可利用现有 OS 的 I/O 设备驱动;VMM 直接控制 CPU 和内存等物理资源,虚拟化效率较高;若对特权 OS 的权限控制得当,虚拟机的安全性只依赖于 VMM。
缺点:因特权 OS 运行于 VM 上,当需要特权 OS 提供服务时,VMM 需要切换到特权 OS,这里面就产生上下文切换的开销。
采用该结构的 VMM 有:Xen, SUN Logical Domain35