1、内存虚拟化概述
所谓的内存虚拟化,即如何在多个虚拟机之间共享物理内存以及如何进行动态分配。在《x86架构基础》一文中已经介绍操作系统对物理服务器内存管理的知识,它的本质就是将物理内存地址映射到一段线性地址空间,也有叫逻辑地址空间,应用程序访问内存物理地址是通过段页查询机制完成。而这个线性地址空间或逻辑地址空间本身就是物理内存的虚拟化呈现。在虚拟化环境中,分配给虚拟机内存非常类似于操作系统中关于线性地址空间的实现。操作系统负责维护虚页号到实页号的映射,并将这一 映射信息保存到页表(Page Table)。在 x86架构的CPU中,内存管理单元MMU与TLB这两个模块就负责实现并优化虚拟内存的性能。详见《DPDK技术在电信云中最佳实践》系列文章。
一个操作系统对其物理内存存在两个主要的基本认识:物理地址从0开始和内存地址连续性(至少在一些大的粒度上连续)。
如上图所示,而VMM与客户机操作系统在对物理内存的认识上存在冲突,这使得真正拥有物理内存的VMM必须对客户机操作系统所访问的内存进行一定程度的虚拟化。换句话说,就是VMM 负责将MMU进行虚拟化,为客户机操作系统提供一段连续的“物理”地址空间,而操作系统本身不会意识到这种变化,仍能够将虚拟机虚拟地址(Guest Virtual Address,GVA)映射到虚拟机物理地址(Guest Physical Address,GPA),但是需要VMM将虚拟机物理地址映射到物理机物理地址(Host Physical Address,HPA)。
所以,内存虚拟化的本质就是把物理机的真实物理内存统一管理,包装成多份虚拟的内存给若干虚拟机使用。内存虚拟化的核心,在于引入一层新的地址空间---客户机物理地址空间,客户机以为自己运行在真实的物理地址空间中,实际上它是通过VMM访问真实的物理地址的。在VMM中保存客户机地址空间和物理机地址空间之间的映射表。
虚拟化系统中包括三层内存地址空间:虚拟机虚拟地址GVA、虚拟机物理地址GPA和物理机物理地址HPA。因此,原先由MMU完成的线性地址到物理地址的映射已经不能满足,必须由VMM接入来完成这三层地址的映射维护和转换。
- GVA:指GuestOS提供给其应用程序使用的线性地址空间。
- GPA:经VMM抽象的,虚拟机看到的伪物理地址空间。
- HPA:真实的机器地址,即地址总线上出现的地址信号。
为了实现上述映射和转换关系,主要有两种解决方案:软件解决方案---影子页表和硬件解决方案---Intel的EPT和AMD的RVI。
2、内存虚拟化软件解决方案
2.1 MMU半虚拟化(MMU Paravirtualization)
这种方式主要为Xen所用,MMU半虚拟化主要原理是:
- 当Guest OS创建新页表时,VMM从维护的空闲内存中为其分配页面并进行注册。后续,Guest OS对该页表的写操作都会陷入VMM进行验证和转换;VMM检查页表中的每一项,确保它们只映射到属于该虚拟机的机器页面,而且不包含对页表页面的可写映射。
- 然后,VMM会根据其维护的映射关系PA-MA,将页表项中的虚拟机逻辑地址VA替换为相应的机器地址MA。
- 最后把修改过的页表载入MMU,MMU就可以根据修改过的页表直接完成虚拟地址VA到机器地址MA的转换。
这种方式的本质是将映射关系VA-MA直接写入Guest OS的页表中,以替换原来的映射VA-PA映射关系。
2.2 影子页表
相比较MMU半虚,大部分虚拟化厂商在VMM中还使用了一种称为影子页表(Shadow Page Table)的技术实现上述功能。对于每个虚拟机的主页表(Primary Page Table),VMM都维持一个影子页表来记录和维护GVA与HPA的映射关系。
影子页表包括以下两种映射关系,
1)GVA>>>GPA,虚拟机操作系统负责维护从虚拟机逻辑地址到虚拟机物理地址的映射关系,VMM可以从虚拟机主页表中获取这种映射关系。
2)GPA>>>HPA,VMM负责维护从虚拟机物理地址到物理机物理地址的映射关系。
通过这种两级映射的方式,VMM为Guest OS的每个页表维护一个影子页表,并将GVA-HPA的映射关系写入影子页表,Guest OS的页表内容保持不变,然后,VMM将影子页表写入MMU。同时,又对虚拟机可访问的内存边界进行了有效控制。并且,使用TLB缓存影子页表的内容可以大大提高虚拟机问内存的速度。
影子页表的维护将带来时间和空间上的较大开销。时间开销主要体现在Guest OS构造页表时不会主动通知VMM,VMM必须等到Guest OS发生缺页错误时(必须Guest OS要更新主页表),才会分析缺页原因再为其补全影子页表。而空间开销主要体现在VMM需要支持多台虚拟机同时运行,每台虚拟机的 Guest OS通常会为其上运行的每个进程创建一套页表系统,因此影子页表的空间开销会随着进程数量的增多而迅速增大。
为权衡时间开销和空间开销,现在一般采用影子页表缓存(Shadow Page Table Cache)技术,即VMM在内存中维护部分最近使用过的影子页表,只有当影子页表在缓存中找不到时,才构建一个新的影子页表。当前主要的虚拟化技术都采用了影子页表缓存技术。
3、内存虚拟化的硬件解决方案
为了解决影子页表导致的上述开销问题,除了使用影子页表缓存技术外(这项技术虽然能避免时间上的一部分开销,但是空间开销还是实实在在存在的), Intel与AMD公司都针对MMU虚拟化给出了自 的解决方案:Intel公司在Nehalem微架构CPU中推出扩展页表(Extended Page Table,EPT)技术;AMD公司在四核皓龙CPU中推出快速虚拟化索引(Rapid Virtualization Index,RVI)技术。
RVI与EPT尽管在具体实现细节上有所不同,但是在设计理念上却完全一致:通过在物理MMU中保存两个不同的页表,使得内存地址的两次映射都在硬件中完成,进而达到提高性能的目的。具体来说,MMU中管理管理了两个页表,第一个是GVA >>>GPA,由虚拟机决定;第二个是GPA>>>HPA,对虚拟机透明,由VMM决定。根据这两个映射页表,CPU中的page walker就可以生成最近访问过key-value键值对 ,并缓存在TLB中(类似影子页表缓存技术思路)。
另外,原来在影子页表中由VMM维持的GPA>>>HPA映射关系,则由一组新的数据结构扩展页表(Extended Page Table,也称为Nested Page Table)来保存。由于GPA >>>HPA的映射关系非常定,并在虚拟机创建或修改页表时无需更新,因此VMM在虚拟机更新页表的时候无需进行干涉。VMM也无需参与到虚拟机上下文切换,虚拟机可以自己修改GVA >>>GPA的页表。
我们以Intel EPT技术为例说明。Intel EPT是Intel VT-x 提供的内存虚拟化支持技术,其基本原理下图所示。在原有的CR3页表地址映射的基础上,EPT引入EPT页表来实现另一次映射。比如:假设客户机页表和EPT页表都是4级页表,CPU完成一次地址转换的基本过程如下:
CPU首先查找客户机CR3寄存器指向的L4页表。客户机CR3寄存器给出的是GPA,所以,CPU通过EPT页表将客户机CR3中的GPA转换为HPA:CPU 首先查找EPT TLB,如果没有相应的记录,就进一步查找EPT页表,如果还没有,CPU则抛出EPT Violation异常交给VMM处理。
CPU获得L4页表地址(指的是HPA)后,CPU根据GVA和L4页表项的内容来获取L3 页表项的GPA。如果L4页表中GVA对应的表项显示为“缺页”,那么CPU 产生Page Fault,直接交由客户机操作系统处理。获得L3 页表项的GPA后,CPU通过查询EPT页表来将L3的GPA转换为HPA。同理,CPU 会依次完成L2、L1页表的查询,获得GVA所对应的GPA,然后进行最后一次查询EPT页表获得HPA。
正如上图所示,CPU需要5次查询EPT页表,每次查询都需要4次内存访问。这样,在最坏的情况下总共需要20次内存访问。EPT硬件通过增大EPT TLB 尽量减少内存访问。
4、内存虚拟化管理
在虚拟化环境中,内存是保证虚拟机工作性能的关键因素。如何尽可能提高虚拟机的性能、提高内存利用率、降低虚拟机上下文切换的内存开销,依然非常复杂,这就引入了内存虚拟化管理的问题。像介绍CPU虚拟化管理一样,我们还是通过实例来说明内存的虚拟化管理。以VMware的ESX解决方案为例,在没有出现硬件支持的内存虚拟化技术之前,ESX/ESXi采用影子页表来实现虚拟机的虚拟地址到物理机物理地址的快速转换。当Intel和AMD公司分别推出了EPT与RIV技术之后,ESX/ESXi很快转向硬件支持来提高内存虚拟化的性能。
4.1 内存复用技术
在虚拟化内存管理 上,ESX/ESXi实现了主机内存超分配的目标:即多个虚拟机总的内存分配量大于物理机的实际内存容量。如下图所示,一个物理内存只有4GB的Host,可以同时运行三个内存配置为2GB的虚拟机。
主机内存超分配功能意味着VMM必须能够有效地回收虚拟机中不断释放的内存,并在有限的内存容量中尽能 地提高内存利用率。因为,Host Memory与Guest Memory并不是一一对应的关系,通过Host Memory超配技术可以实现某一个Host上某一个虚拟机内存大于主机内存,这一点与CPU虚拟化不一样。但是,在执行超配技术时,需要考虑主机性能问题,不能过大。一般的超配限额是主机内存的50%。要实现主机内存超配,必须通过内存复用技术实现。目前常用的内存复用技术有:零页共享技术、内存气球回收技术和内存置换技术三种。
4.1.1 零页(透明页)共享技术
当运行多个虚拟机时,有些内存页面的内容很可能是完全一样的,比如:什么数据都没有的零页。这就为虚拟机之间甚至在虚拟机内部提供了共享内存的可能。例如:当几个虚拟机都运行相同的操作系统、相同的应用程序或者包含相同的用户数据时,那些包含相同数据的内存页面完可以被共享。基于这个原理,VMM完全可通过回收冗余数据的内存页面,仅维持一个被多个虚拟机共享的内存拷贝来实现这个功能。
原理如下:用户进程定时扫描虚拟机的的内存数据,如果发现其数据内容全为零,则通过修改PA to MA映射的形式,把其指向一个特定的零页,从而做到在物理内存中只保留一份零页拷贝,虚拟机的所有零页均指向该页,从而达到节省内存资源的目的。当零页数据发生变动时,由Xen动态分配一页内存出来给虚拟机,使修改后的数据有内存页进行存放。因此,对GuestOS来说,整个零页共享技术是完全不感知的。
而在VMware的ESX解决方案中,也有同样的技术。在VMware ESX/ESXi中,检测页面数据是否冗余是通过散列的方法来实现的。
首先VMM会维持一个全局散列表,其中每个表项都记录了一个物理页面数据的散列值与页号。当对某一个虚拟机进行页面共享扫描时,VMM会针对该虚拟机物理页面的数据计算散列值,并在全局散列表 中进行遍历及匹配是否有相同的散列值的表项。当找到了匹配的表项,还要对页面数据内容逐位比较,以避免由于散列冲突而导致的页面内容不一样的可能性。一旦确定页面数据完全一致,则会修改逻辑地址到物理地址的映射关系,即将从逻辑地址对应到包含冗余数据的物理地址的映射关系(上图中虚线所示)改为对应到要被共享物理地址的映射关系,并回收冗余的物理页面。这一过程对于虚拟机操作系统是完全透明的。因此,共享页面中含有敏感数据的部分不会在虚拟机之间泄露。
当虚拟机对共享页面发生写操作时,通过“写时拷贝”( Copy-on-Write)技术来实现。
具体来说,任何一个对共享页面的写操作都会引发页面错误(Minor Page Fault)。当VMM捕获到这个错误时,会给发起写操作的虚拟机创建一个该页面的私有拷贝,并将被写的逻辑地址映射到这个私有拷贝页面。这样虚拟机就可以安全地进行写操作,并且不会影响到其他共享该页面的虚拟机。相比于对非共享页面的写操作,尽管这种处理方法的确导致了一些额外的开销,但是却在一定程度上提高了内存页面的利用率。
4.1.2 内存气球回收技术
内存气球回收技术也称为内存气泡技术,基于气球回收法的内存管理机制与页面共享完全不同。在虚拟化环境中,VMM会一次性在虚拟机启动后分配给虚拟机内存,由于虚拟机并没有意识到自己运行于 虚拟化平台上,之后它会一直运行在分配好的内存空间,而不主动释放分配的物理内存给其他虚拟机。因此VMM需要一种机制使得虚拟机能够主动释放空闲内存归还给物理机,再由VMM分配给其他有需求的虚拟机。并且,在内存资源需求紧张时还能从物理机中“拿回”自己释放的那部分内存。
原理如下:Hypervisor通过利用预装在用户虚拟机中的前端驱动程序,“偷取”Guest OS的内存贡献给VMM,以供其他虚拟机使用,反向时由VMM“偷取”气泡中的内存给特定虚拟机使用。内存气泡本质是将较为空闲的虚拟机内存释放给内存使用率较高的虚拟机,从而提升内存利用率。
在VMware的ESX解决方案中,也有类似的技术。
在上图(a)中,VMM有四个页面被映射到虚拟机的内存页面空间中,其中左侧两个页面被应用程序占用,而另两个被打上星号的页面则是在内存空闲列表中。当VMM要从虚拟机中回收内存时,比如要回收两个内存页面,VMM就会将Balloon驱动的目标膨胀大小设置为两个页面。Balloon驱动获得了目标膨胀值之后,就会在虚拟机内部申请两个页面空间的内存,并如上图(b)所示,调用虚拟机操系统的接口标示这两个页面被“ 钉住”,即不能再被分配出去。
内存申请完毕后,Balloon驱动会通知VMM这两个页面的页号,这样VMM就可以找到相应的物理页号并进行回收。在上(b)中虚线就标示了这两个页面从虚拟机分配出去的状态。
由于被释放的页面在释放前已经在虚拟机的空闲列表中,因此没有进程会对该页面进行读写操作。如果虚拟机的进程接下来要重新访问这些页面,那么VMM可以像平常分配内存一样,再分配新的物理内存给这台虚拟机。当VMM决定收缩气球膨胀大小时,通过设置更小的目标膨胀值,balloon驱动会将已经被“钉住” 的页面归还给虚拟机。
通过气球回收法,尽管虚拟机的负载略微增加,但VMM却成功地将系统内存压力转移到各个虚拟机上。当balloon驱动发起申请内存的请求时,由虚拟机操作系统决定了是否要将虚拟机物理内存换出来满足balloon驱动的申请内存请求。如果虚拟机有充足的空闲内存,那么balloon驱动申请内存并不会对虚拟机的性能造成影响;如果虚拟机内存已经吃紧,那么就需要由虚拟机的操作系统决定换出哪些内存页面,满足balloon驱动的请求。因此,气球回收法巧妙地利用了各个虚拟机操作系统的内存换页机制来确定哪些页面要被释放给物理机,而不是由VMM来决定。
气球回收法要求虚拟机操作系统必须安装balloon驱动,在VMware的ESX/ESXi产品中,就是VMware Tool。另外,气球回收法回收内存需要一段时间,不能马上满足系统的需求。
4.1.3 内存置换技术
页面共享机制与气球回收法都从不同的角度尽可能地提高虚拟机的内存利用率,从虚拟机中收回可以复用或者空闲的内存。然而这两种方法都不能在短时间内满足系统内存回收的要求:页面共享依赖于页面的扫描速度,以及是否有页面可共享;气球回收法则取决于虚拟机操作系统对于balloon驱动申请内存的响应时间。如果这两种温和的方法都不能满足需求,VMM则会采取内存换出机制,即强制性地从虚拟机中夺回内存,这就是内存置换技术。
原理如下:通过VMM实现请页功能,这时Guest OS类似进程一样在VMM缺少内存时,能被换出到宿主机磁盘上,也就是将虚拟机长时间未访问的内存内容被置换到存储中,并建立映射,当虚拟机再次访问该内存内容时再置换回来。该方法也对虚拟机透明,即虚拟机不感知。
具体来说,VMM会在每个虚拟机启动时创建一个单独的换页文件(Swap File)。在必要的时候,VMM会主动将虚拟机的物理内存页面换到这个换页文件上,释放给其他虚拟机使用。内存换出机制是VMM需要在短时间内缓解内存压力的一种有效方法,然而这种方法却很可能严重导致VMM的性能下降。由于VMM对于虚拟机的内存使用状态并不解,且该方法对虚拟机透明,强制内存换出可能触发虚拟机操作系统内部的一些换页机制。举例来说,虚拟机操作系统永远都不会将内核的内存页面换出,而VMM并不知道哪些页正在被内核使用,一旦这些页面被换出,会使得虚拟机性能严重受损。
4.2 内存的回收
接下来以VMware ESX为例,结合上述三种内存复用技术,介绍内存回收机制。一般来说,ESX会对物理机的空闲内存状态按照空闲内存的百分比设置四种状态,分别是:高(6%)、平缓(4%)、繁重(2%)和低(1%)。ESX会按照这四种状态来选择前述三种内存回收机制。
缺省状态下,ESX会启用页面共享机制,因为页面共享机制能以较小的开销提高内存利用率。何时启用气球回收和换页则取决于当前系统的内存状态。当内存状态处于“高”,很显然此时总的虚拟机内存使用量要小于物理机的内存容量,因此不管虚拟机的内存是否已经被过载分配,VMM都不会使用气球 或者换页的方法回收内存。
然而,当物理机空闲内存状态下降到了“平缓” 状态,VMM则开始使用气球回收法。事实上,气球回收法是在空闲内存的百分比高于“平缓” 的阈值4%之前启动的,这是因为该方法总是需要一段时间才能在虚拟机内申请到一些内存。通常气球回收法都能够及时将空闲内存比的阈值控制在“平缓”状态之上。
一旦气球回收法不能够及时回收内存,并且空闲内存下降到“繁重”状态,即空闲内存比低于2%,那么VMM就会再启动内存换出机制强制从虚拟机回收内存。使用这种办法,VMM能够很快回收内存,并将空闲内存比控制回“平缓”状态。
在最坏的情况下,万一空闲内存状态低于“低”状态,即空闲内存比低于1%,那么VMM会继续使用内存换出法,同时将那些消耗内存值超过内存配置值的虚拟机挂起。
在某些情况下,VMM可能不会考虑物理机空闲内存状态,而仍然启动物理机内存回收机制。比如,即使整个系统的物理机空闲内存状态为“高”,如果某个虚拟机的内存使用量超过了其指定的内存上限,那么VMM会启动气球回收法,如有必要,也会启动内存换出机制从虚拟机回收内存,直到该虚拟机的内存低于指定的内存上限。
4.3 内存QoS保障
在虚拟化系统中,内存虚拟化的QoS保障包括两个基本特征:预留和份额。
内存预留:虚拟机预留的最低内存。预留的内存会被VM独占。即,一旦内存被某个虚拟机预留,即使虚拟机实际内存使用量不超过预留量,其它VM也无法抢占该VM的内存空闲资源。即,上述的三种内存复用技术对该虚拟机不生效。
内存份额:适用上述三种资源复用场景,按比例分配内存资源。以6G内存规格的主机为例,假设其上运行有3台4G内存规格的虚拟机VMA,VMB,VMC。内存份额分别为20480,20480,40960,那么其内存分配比例为1:1:2。当三台VM内部逐步加压,策略会根据三个虚拟机的份额按比例分配调整内存资源,最终三台虚拟机获得的内存量稳定为1.5G/1.5G/3G。
⚠同样需要注意一点:内存份额只在各虚拟机发生资源竞争时生效。如没有竞争,则有需求的虚拟机可独占物理内存。
但是,内存QoS不像CPU QoS一样设置上限?,这是因为分配给虚拟机的内存大小就是其内存上限。