前面讲过,在Intel x86 CPU中,为了加快线性地址到物理地址的转换,引入了各种地址转换的cache(TLB、PDE cache、PDPTE cache、PML4 cache)和PCID(Processor Context Identifier)机制。不清楚的可以出门左转查看上一篇文章。
而在虚拟化环境中,即VMX(Virtual Machine Extension)中,Intel x86 CPU同样也引入了EPT(Extended Page Table)、地址转换Cache和VPID(Virtual Processor Identifier)机制用于加快地址的转换。
1. EPT(Extended Page Table)
在虚拟化环境中,由于VMM(Virtual Machine Monitor)可以同时管理、运行多个虚拟机,每个虚拟机看到的硬件资源都由VMM模拟或者分配,所以虚拟机看到的硬件资源可能不是真实的,比如内存,所以在虚拟化环境中就需要将虚拟机所认为的物理内存转换成实际的物理内存。即在虚拟机内部的客户机操作系统,它能感受到的是和普通的操作系统一样,即将虚拟地址转换为物理地址(客户机物理地址),而在实际的实现中,还需要在客户机操作系统无法感知的地方将客户机物理地址转换为真实的物理地址,即宿主机物理地址,这个过程即GVA(Guest Virtual Address)-> GPA(Guest Physical Address) -> HPA(Host Physical Address)。
为了实现这个目的,可以通过纯软件的方式,也可以通过硬件辅助的方式。对于纯软件的方式而言,由于硬件上只提供了一个地址转换页表的入口点——CR3寄存器。为了让一个页表实现两层的地址转换(GVA->GPA是虚拟机操作系统可以见的,GPA->HPA是虚拟机操作系统不可见的),VMM将采用陷入再模拟的方式,让虚拟机操作系统对CR3和页表的访问都触发退出到VMM,然后VMM将两次地址转换的页表结合起来形成一系列的页表,即影子页表。虚拟机操作系统以为自己将GVA->GPA的转换页表入口加载到CR3寄存器,而实际上已经被VMM“偷梁换柱”了,加载到CR3的是GVA->HPA的页表,这也是影子页表名称的来源。如下图所示:
为了实现影子页表,大部分的CR3寄存器和页表访问都需要陷入到VMM中,让VMM在软件层面上进行模拟。同时VMM需要为每个虚拟机操作系统的每个进程维护一张地址转换页表,会占用大量的内存资源。为此带来了两个问题,一个是地址转换的效率不高,另一方面则是内存资源占用过多。
为了克服在虚拟化环境中地址转换的性能和资源问题,Intel x86 CPU的VMX功能集中引入了EPT(Extended Page Table)机制,即在原本基于CR3和地址转换页表实现分页机制的基础上,再在硬件上引入一层分页机制,该新的分页机制就叫做EPT,EPT分页机制用于将GPA转化为HPA,如下图所示:
EPT机制所使用的页表的入口地址位于VMCS(Virtual Machine Control Structure)结构体中的EPTP(Extended Page Table Pointer),当EPT使能的时候,虚拟机操作系统内部维护的地址转换页表由CR3提供入口,将GVA转化为GPA,让后再由虚拟机操作系统看不到的EPT表将GPA转化为HPA,HPA HPA的访问请求发送给内存控制器,实现内存的访问。这两个转换过程都由硬件自动完成,软件需要做的就是在内存中设置好地址转换页表(由VMM完成),并将页表的入口地址放到CR3或者EPTP位置。硬件的地址转换速度就比软件快多了,解决了性能的问题。
另外,由于EPT的作用是进行GPA->HPA的转换,每个虚拟机操作系统只有一份GPA,所以VMM只需要为每个虚拟机维护一份GPA->HPA的地址转换页表,这样就可以大大减少地址转换页表对内存的占用。
2. 地址转换Cache
在前一篇文正中提到,x86 CPU引入了TLB和Paging-Structure Cache用于加速地址转换,而在虚拟化环境中,也引入了类似的机制用于加快GVA->GPA->HPA的地址转换,主要包括:
- Linear mapping
- Linear translations,类似于TLB,用于直接将GVA表示页表的部分(linear page number)直接转换为虚拟机物理内存页框的基地址,即GPA的高位。
- Linear paging-structure-cache entries,类似于前面提到的Paging-Structure Cache,只是它针对的是GVA,可以跳过部分的地址转换页表的访问,达到加速的效果。
- Guest-physical mapping
- Guest-physical translations,类似于TLB,用于直接将前面得到的GPA表示页表的部分(guest-physical page number)直接转换为主机物理内存页框的基地址。
- Guest-physical paging-structure-cache entries,类似于之前提到的Paging-Structure Cache,只是它针对的是GPA,可以跳过部分的地址转换页表的访问,达到加速的效果。
- Combined mapping
- Combined translations,类似于TLB,只是它的转换幅度更大,直接从GVA的高位(linear page number)转换为主机物理内存页框的基地址,即HPA的高位。
- Combined paging-structure-cache entries,类似于之前提到的Paging Structure Cache,只是它针对的是GVA的高位,并且可以直接转换到GPA->HPA的部分地址转换页表上,省去了中间GVA->GPA和部分GPA->HPA的转换,达到了加速的效果。
从上面的分类可以看出,在虚拟化环境VMX操作模式中,地址转换也是引入了TLB和Paging Structure Cache这两种cache来加速地址转换,只是因为在虚拟化环境中需要有两次的地址转换,所以有对这些地址转换的cache进行了细分,软件上维护的逻辑复杂度也跟着相应地增加,但是基本原来都是相同的。
3. VPID (Virtual Processor Identifiers)
由于在VMX虚拟化环境中,一个逻辑CPU可以运行来自不同虚拟机的多个虚拟CPU,即vCPU,为了防止多个不同的vCPU之间,vCPU和逻辑CPU之间的地址转换cache(TLB和Paging-Structure Cache)之间相互干扰,在每次进行VM Entry或者VM Exit操作的时候,都需要把这些地址转换所用到的cache清掉,再重新加载目标CPU/vCPU的地址转换cache。Cache的清除和重新加载是一个比较耗时的操作,会影响到CPU/vCPU的性能,特别是逻辑CPU一直在执行某个vCPU的时候,其实某些cache完全可以保留,不需要进行特殊的操作。
为了减少这些耗时的Cache清除和加载操作,VMX中引入了VPID(Virtual Processor Identifiers)机制,即在为每个地址转换的Cache(TLB或者Paging-Structure Cache)打上一个VPID的标志(VMM为每个vCPU分配一个唯一的VPID(位于VMCS中),逻辑CPU的VPID为0,其他vCPU的VPID大于0),这样就不用担心不同的vCPU/CPU所使用到的地址转换Cache混到一起,CPU硬件在寻找Cache的时候,将会多进行一次比较,即将当前运行的vCPU/CPU的VPID和Cache中所包含的VPID进行匹配,只有相等才会使用该Cache条目。这样在每次进行VM Entry或者VM Exit的时候就不需要将地址转换的Cache清除掉,完全可以复用之前保留着的Cache,只在必要的时候进行地址转换Cache的更新。
和之前非虚拟化环境下地址转换加速机制进行一下对比会发现,VPID的机制和PCID(Process Context Identifiers)的机制类似,都是在不同的层级上对地址转换的Cache进行保留,减少地址转换Cache的清除和加载次数。VPID针对的是虚拟化环境下不同vCPU的层面,而PCID针对的是在操作系统下,不同进程的层面,两者是可以同时起作用的。
总结
总的来说,对于x86 CPU而言,在虚拟化环境中,主要是通过引入EPT、地址转换Cache和VPID的机制来对GVA->GPA->HPA的地址转换进行加速。