这篇内容,通过从操作系统的虚拟地址介绍到虚拟机层面的内存虚拟化技术,并介绍这两种技术之间的关联。
(以下实例都以intel 64位处理器和win10 x64操作系统位例。 )
一、虚拟地址
在保护模式下,每个进程拥有自己独立的虚拟内存,程序的代码、全局数据、栈空间等使用自己独立的虚拟内存。每个进程可以访问自己独立的以0开始的虚拟地址(VA),即每个进程可以有跟其他进程相同的虚拟地址(VA)起始地址(start address)和结束地址(end address)。
虚拟内存被操作的时候,会分配一块实际的物理内存与它对应。
这篇内容,通过从操作系统的虚拟地址介绍到虚拟机层面的内存虚拟化技术,并介绍这两种技术之间的关联。
(以下实例都以intel 64位处理器和win10 x64操作系统位例。 )
一、虚拟地址
在保护模式下,每个进程拥有自己独立的虚拟内存,程序的代码、全局数据、栈空间等使用自己独立的虚拟内存。每个进程可以访问自己独立的以0开始的虚拟地址(VA),即每个进程可以有跟其他进程相同的虚拟地址(VA)起始地址(start address)和结束地址(end address)。
虚拟内存被操作的时候,会分配一块实际的物理内存与它对应。
CPU内部的MMU(内存管理单元)会把虚拟地址(virtual address)翻译为对应的物理地址(physical address),借助于CR3寄存器。CR3寄存器存储该进程的最顶级页目录。
每一个虚拟地址被分成几份,每一份作为一个索引,得到多个索引值和一个页偏移。从最顶级页目录找到索引所在的项。从项取出次顶级页目录的起始物理地址。再用下一个索引从次顶级页目录找到下一个页目录的起始物理地址。直至直到页表的起始物理地址,外加页偏移,计算该虚拟地址对应的实际物理地址。
从上面描述的,从虚拟地址到实际的物理地址,需要耗费一定的时间,TLB(translation lookaside buffer)可以省去中间过程。如果从TLB找到虚拟地址对应的物理地址,则跳过从页目录到页表的翻译过程。TLB会维护虚拟地址到物理地址的对应过程一定的时间。
完整的过程为:当一个虚拟地址(VA)被访问(access),如果该虚拟地址(VA)对应的物理地址(PA)没有找到或不存在,则触发缺页中断(INT E),由缺页处理程序建立该虚拟地址(VA)的页目录页表(PML4、PDP、PDE、PTE),并把虚拟地址(VA)与物理地址(PA)的对应关系记录在TLB中。
二、内存虚拟化
我们下面描述的是内存虚拟化主要用于CPU支持并提供的硬件辅助虚拟化技术当中。
在一台计算机上安装多个虚拟机(VM)时,每个虚拟机(VM)都认为自己独占使用物理内存,都可以独立的访问以0开始的起始物理地址。如下图:
虚拟内存技术用于把虚拟地址VA(virtual Address)转化为物理地址PA(physical address),但有了内存虚拟化技术在原本的虚拟地址翻译为物理地址的过程中又加了一层,把原本的物理地址所(PA)处的位置叫做虚拟机(virtual machine)的物理地址,也叫做GPA(guest physical address)。从而实际的物理地址(PA)叫做VMM(virtual monitor machine)的物理地址,也叫做HPA(host physical address)。
(*注:虚拟机(VM)有的资料上也叫客户机,是一个意思)。
再由VMM(Virtual Machine Monitor)把虚拟机的GPA翻译为HPA。最终达到的效果是VA映射到了HPA。转换过程如下图:
三、虚拟内存和内存虚拟化的关系
如果没有内存虚拟化技术,操作系统通过虚拟地址(VA)访问物理地址(PA)的过程如下:操作系统(OS)把一个虚拟地址分成4个索引值(Index)和一个物理内存页偏移(offset)。4个索引值分别是Index4、Index3、Index2、Index1,分别表示L4(第四级页表PML4T)中的索引、L3(第三级页表PDPT)中的索引、L2(第二级页表PDT)中的索引、L1(第一级页表PT)中的索引。
使用索引值在页表中找到下一级页表的起始物理地址,过程如下:利用CR3寄存器找到PML4T(页表)的起始物理地址(PA),用Index4在PML4T中找到PDPT的起始物理地址(PA);再使用Index3从PDPT 找到PDT的起始物理地址(PA);再使用Index2从PDT中找到PT的起始物理地址(PA);再使用Index1从PT中取出PFN的起始物理地址(PA);最后PFN的物理起始地址加上页内偏移(offset)找到虚拟地址对应的物理地址(PA)。
在内存虚拟化技术下,所有上面提到的物理地址(PA)都将作为虚拟机的物理地址(GPA),需要把所有的GPA借助于CPU内部的EPT(Extended Page Table)转化为HPA,才能被CPU给访问。所以,过程变为了下面这样:
虚拟机里面的操作系统使用的CR3寄存器存放的PML4T的起始物理地址(GPA)需要被VMM(Virtual Machine Monitor)转化为HPA,然后才能使用Index4找到PDPT的起始物理地址(GPA)。继续借助于EPT把PDPT的GPA转化为HPA,再利用Index3找到PDT的起始物理地址(GPA)。继续借助于EPT把PDT的GPA转化为HPA,再利用Index2找到PT的起始物理地址(GPA)。继续借助于EPT把PT的GPA转化为HPA,再利用Index1找到PFN的起始物理地址(GPA)。最后借助于EPT把PFN的GPA转化为HPA,加上页内偏移(offset),得到最后的虚拟地址(VA)对应的HPA。
最后说明下,从GPA到HPA的转化过程如下图:
一个GPA地址也被分成5部分,L4,L3,L2、L1和页内偏移offset。每一部分作为一个索引,Index4,Index3,Index2,Index1,Offset。从EPT取出PML4 Table的基地址,加上Index4*4找到PML4 Entry;并取出PML4 Entry中存放的PDP Table的基地址,加上Index3*4找到PDP Entry;取出PDP Entry中存放的PD Table的基地址,加上Index2*4找到PD Entry;取出PD Entry中存放的PT Table的基地址,加上Index1*4找到物理页帧号PFN;PFN 加上Offset就是最终的物理地址(HPA)。
四、示例
下面简单介绍下代码《Part 7 - Using EPT & Page-level Monitoring Features》,用于了解下EPT的一个应用场景。该代码来源于Hypervisor From Scratch – Part 7: Using EPT & Page-Level Monitoring Features | Rayanfam Blog,演示了EPT的从构建到使用的过程。下面代码跟上面介绍过的内容的唯一不同的点是,页目录(PD)的Large Page标记为1,意味着页目录指向PFN,省去了PTE。每一个PDTE指向的2MB大小的物理内存。
在不同架构的处理器下,4KB物理页面和2MB的物理内存说明如下图:
该代码演示了多CPU对应一个EPT表,修改完EPT需要调用Invept指令使TLB失效。该代码去除了ExAllocatePoolWithTag函数的可执行权限,从而触发VM Exit,进入VMM的处理例程VmxVmexitHandler,然后再恢复ExAllocatePoolWithTag函数的执行权限,允许其可执行。具体过程如下:
初始化完成EPT表后,查找函数ExAllocatePoolWithTag所在的PDE,把该PDE转化为一个包含512个PT的表,找到函数ExAllocatePoolWithTag所在的PTE,然后去除函数ExAllocatePoolWithTag所在的PTE的执行权限。
当调用函数ExAllocatePoolWithTag时,函数ExAllocatePoolWithTag所在PTE标记没有执行权限,触发违例进入VmxVmexitHandler。在处理例程VmxVmexitHandler找到函数ExAllocatePoolWithTag所在的PTE,并恢复可执行权限,最后调用Invept指令刷新TLB。
四、总结
以上,从操作系统层面介绍了虚拟地址与物理地址之间的关系及转化过程,又在引入了硬件辅助虚拟化技术后,介绍了从虚拟机(操作系统处于虚拟机内部)层面的物理地址再到虚拟机监视器的实际的物理地址之间的关系及转化过程。并通过一份开源代码,简单介绍了一下以上内容的一个应用场景。
参考资料:
《系统虚拟化——原理与实现》
《虚拟化技术原理与实现》
《Intel手册卷3》
《虚拟化与云计算》
《Windows Internals Seventh Edition Part 2》文章来源:https://www.toymoban.com/news/detail-433048.html
《Part 7 - Using EPT & Page-level Monitoring Features》文章来源地址https://www.toymoban.com/news/detail-433048.html
到了这里,关于硬件辅助虚拟化 之EPT(内存虚拟化)介绍的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!