内存虚拟化

目的及基本概念

  • 为虚拟机提供一个从零开始的连续物理地址空间
  • 各个虚拟机之间有效隔离,调度以及共享内存资源

guest os负责客户机虚拟地址(Guest Virtual Address, GVA)到客户机物理地址(Guest Physical Address, GPA)的转换,
VMM则负责把GPA转换成宿主机物理地址(Host Physical Address, HPA)。

因此可以让VMM截获所有试图修改客户机页表或刷新TLB的指令, 把修改GVA到GPA映射的操作,转换成GVA直接到HPA的操作。
也因此需要另一个新的页表存储这种关系–影子页表

软件完全虚拟化–影子页表

  • 影子页表存储了GVA到HPA的映射关系,可以直接被MMU加载使用
  • 而客户机维护的页表不能被MMU使用,看到和操作的都是虚拟MMU
  • TLB和CPU cache上缓存的都是来自影子页表的GPA到HPA的映射

传统物理机中刷新TLB的场合

  • 写入CR3寄存器(存放页目录表的物理地址),如果写入的物理地址与原地址相同,意味着os只想使当前TLB内容全部失效,要访问虚拟地址,硬件需要重新遍历页表
  • 若写入CR3的是个不同的物理地址,意味着操作系统想使用一套新的页表,通常意味着进程的切换,自然TLB全部失效
  • 操作系统只修改部分页表。修改页表和使用INVLPG指令使单个TLB失效

VMM截获客户机内存访问操作,如果客户机页表被修改,VMM也需要维护影子页表的一致性
写入CR3寄存器和INVLPG指令都是特权指令,但是客户机os本身就有对页表的读写权限, 如果影子页表的访问权限也是可写的,则不能被VMM截获
因此影子页表的访问权限必须是只读的, 客户机os任何写内存的操作都会被触发缺页异常被VMM截获处理。
在处理函数中,VMM代客户机os更新页表项外还会同时更新影子页表。

影子页表的结构和建立

  1. 当客户机os进入保护模式之前会为第一个保护模式的进程准备好客户机CR3的值, 该值的高20位为客户机页帧号(Guest Frame Number, GFN), VMM截获该请求并映射到宿主机的物理页帧号(Machine Frame Number, MFN)
  2. 同时VMM需要创建对应的影子页表,因此需要新分配一个物理页面, 这个页面的桢号称为影子宿主机物理页帧号(Shadow Machine Frame Number, SMFN), 并把这个物理页面的起始地址载入物理CR3寄存器,指向影子页表
  3. 当下次这个进程被调度的时候,VMM就不需要重新创新影子页表,只要找到这个SMFN即可,因此要建立MFN到SMFN的映射关系即可, SMFN=hash(MFN, type)
    影子页表的页表项和客户机页表中的页表项对应, 此时页表为空。

缺页异常

发生缺页异常时,VMM将发生异常的客户机虚拟地址在客户机页表中对应页表项的访问权限位与缺页异常的错误码进行比较,来判断此缺页异常是否是由于客户机本身引起的

  • 如果客户机页表项的Present位为0,或者写一个只读的的客户机物理页,VMM则直接返回客户机操作系统,再由客户机os的缺页异常处理机制来处理,此时可能分配客户机物理页,因为页表的修改,而被VMM截获,然后同步客户机页表和影子页表
  • 否则就说明不是由客户机引起的,而是客户机页表和影子页表不一致引起的(Shadow Page Fault),则VMM会为相应的影子页表填入宿主机物理地址(如果不是最后一级页表,则填入SMFN)
  • 当发生缺页异常时,如果客户机os尚未给这个GVA分配GPA,则VMM首先将缺页异常传给客户机os,让客户机os分配GPA,然后由于客户机os分配物理页需要修改页表,因此再被VMM截获,VMM更新影子页表中的相应的页表项,增加到HPA的映射。

影子页表开销

  • VMM需要为每个客户机的每套页表结构(每个进程)都维护一套相应的影子页表,带来较大的内存开销
  • 比如客户机进程退出时,VMM无法感知, 需要凭算法来推测。此时VMM应对这些页面取消写保护,否则会造成额外的性能开销。(可以在客户机中植入一个”气球模块”,由这个模块申请内存,再被VMM截获,VMM释放这个模块获取的客户机物理地址空间)
  • 截获内存访问,维护影子页表的一致性带来很大的开销

硬件辅助虚拟化–EPT(Extended Page Table)

VT-x规范在VMCS的VM-Excution控制域中提供了Enable EPT字段,启用EPT的话(GVA->GPA->HPA)两次转换都将由硬件进行。
EPT页表的基地址由VM-Excution控制域中的Extended page table pointer来指定, 其为宿主机物理地址。
并且EPT支持hugePage

EPT原理

  1. CPU首先查找Guest CR3的HPA, 先查看EPT TLB是否存在(GPA->HPA)映射, 没有则查找EPT页表,还是没有CPU则抛出EPT Violation异常由VMM来处理
  2. 获取到L4页表项的内容后,如果L4页表中GVA对应的表项显示为缺页,CPU则产生Page Fault,直接交给Guest Kernel处理(不产生VM-Exit)
  3. 获得L3表项的GPA后,继续通过EPT来查找对应的HPA。
  4. 一直查到最后一级页表,因此4级页表需要查询5次EPT,每次EPT查询需要4次访存,总共20次访存; 因此引入EPT TLB来减少内存访问。

性能

  • 如果VMM给虚拟机分配的物理内存足够连续的话,可以在EPT中尽量使用hugePage,可以提升TLB的性能
  • EPT Violation的原因可能是: 访问MMIO地址,此时VMM转交给IO虚拟化模块; 或是有些VMM采用惰性方法,一开始EPT为空,第一次使用发生EPT Violation时再建立映射。
  • 对于客户机内部的Page Fault不用发生VM-Exit,提升了性能
  • EPT只需要维护一张也表, 不像影子页表需要为每个客户机进程的页表维护一张影子页表,减少了内存的开销

硬件辅助虚拟化–VPID

VPID通过在每个TLB项上增加一个标志,标识并区分来自不同的虚拟处理器,因此避免了在每次VM-Entry/VM-Exit时使全部的TLB失效,提升了性能。
Intel VT-x通过在VMCS设置来支持VPID

  • 为VMCS分配一个非0的VPID,标记不同的VCPU,(VPID=0被用于VMM)
  • Enable VPID置位