上一期《虚拟化与云计算硬核技术内幕 (7) —— 花名与破冰》中,我们经过研究《Intel64 & IA32 Architectures Software Developer Manual》,发现Intel通过给内存起“花名”(虚拟地址)实现了内存虚拟地址与物理地址解耦,但如果缺乏对VM root和VM Non-root状态下对内存地址访问的区分,会发生类似“破冰”的侵犯他人界限的现象。
为了避免虚拟机工作在ring0下的Guest OS操作系统内核随意访问其他虚拟机或宿主机的内存空间,需要实现这两点:
举一个栗子:
如果虚拟机A访问线性地址0x8001A360,映射到物理地址0xA871A360;同时,虚拟机B也访问线性地址0x8001A360,应当映射到哪里呢?当然是与虚拟机A不同的一个物理地址了。
也就是,TLB中保存的映射关系表格,其Key除了虚拟地址外,还需要一个标识虚拟机的字段。
Intel在VMX扩展中,管这个字段叫VPID (Virtual Processor Identifiers,虚拟处理器标识)。VPID是一个16bit的数,因此,理论上每个物理CPU最多只可以虚拟出65536个VM(敲黑板)。在VM Root下,这个数为0。如果这个数不为0,说明目前在虚拟机中。
同时,Intel还引入了新的页表机制:EPT(Extended Page Table)。从字面上理解,EPT是对原有页表机制的扩展。实质上,它是实现了从虚拟机内存地址到物理地址的映射。
EPT的基本原理如下图:
虚拟化环境下,虚拟机使用的是客户机虚拟地址GVA(Guest Virtual Address)。在虚拟机的程序指令访问这个地址时,实际上CPU的MMU会进行两次查表:
第一次是根据虚拟机的CR3寄存器指向的页表,得到虚拟机的GPA(Guest Physical Address),第二次再根据EPT Base Pointer进行查表,将GPA转化为HPA(Host Physical Address),也就是宿主机上的物理地址。
EPT查表的Key,除了GPA以外,另一个字段就是VPID —— 如果没有VPID,是无法区分两个不同的虚拟机发出的同一个GPA的。
EPT的翻译机制如下图:
假设处理器工作在IA32e模式,也就是64位模式下。
GPA的长度为48位,也就是每台虚拟机最多可寻址248=281.5TB内存。
EPT页表是4级页表,查表分为五个步骤:
在EPT机制加持之下,Intel的处理器就可以有效隔离不同虚拟机的内存地址,避免多个虚拟机发出的同一个逻辑地址发生混淆了。
EPT从根本上解决了类似“饭圈互撕”的虚拟机之间直接互访内存或虚拟机访问宿主机内存的可能性,使得虚拟化的Intel处理器从原理上而言,真正能够用于生产场景。
想一想,我们还有什么问题没有解决呢?
对了,是虚拟机的输入和输出。
在经典的冯·诺伊曼架构中,计算机除了处理器(CPU),存储器(RAM)外,还有IO设备。最常见的IO设备是磁盘和网络适配器。如何让虚拟机能够使用磁盘和网络适配器呢?
当然,最简单的办法是完全使用软件来模拟这些外部设备的接口,并调用真实的外部设备实现输入和输出。但此种行为的效率极为低下。
有没有效率更高的方法呢?
请看下回分解。