云计算那些事儿:从IaaS到PaaS进阶
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

3.2 内存虚拟化

物理的内存是有限的。操作系统在使用内存的时候本身就是一种虚拟化的使用方式,通过页表机制,每个进程都享有自己独立的内存空间,例如32位操作系统中,每个程序都被虚拟出一个可以使用4GB的内存空间,但实际的物理内存可能不足4GB。操作系统通过页表将虚拟内存和物理内存建立映射关系。每当程序访问内存页(虚拟)的时候,如果物理内存还没分配,将会发生缺页异常,此时内核才真正为该程序分配内存。当物理内存不足时,驻留内存的某些页会通过LRU等算法交换到Swap分区中。

如果是在虚拟化场景中,这种映射就更加复杂了。虚拟机的内存最终需要映射到物理内存上面,经过多次转化,首先将虚拟机内的虚拟机地址通过虚拟机页表转化为虚拟机“自认为的”物理地址,然后再通过Hypervisor将虚拟机的物理地址转化为物理机的虚拟地址,然后再通过虚拟机的页表转化为物理地址,效率较低。可以借助影子页表的方式,记录虚拟地址和物理地址对应关系,从而直接将虚拟地址映射到物理地址,加速寻址过程,如图3-4所示。

图3-4 虚拟化场景映射示意图

但通过软件方式实现的影子页表本身是驻留在内存中,需要消耗很多内存。既然CPU有硬件加速,那么内存虚拟化是否也有对应的方案呢?Intel的EPT和AMD的NPT都提供了硬件辅助方案。下面简单介绍一下EPT技术,它是一种两层页表,首先,虚拟机通过CR3找到虚拟机页表,GVA转化为GPA,然后再通过EPT页表,将GPA转化为HPA。EPT页表是由Hypervisor管理维护的,两次页表转化都是硬件支持的,速度非常快,如图3-5所示。

图3-5 EPT技术示意图

上面解释了内存映射的问题,那么内存又是怎么分配的呢?这里拿一个VMware经典的内存气球(Memory Balloon)技术为例来说明,如图3-6所示。

图3-6 VMware经典的内存气球技术示意图

假设一台物理服务器有8GB的物理内存,上面创建两个8GB的内存的VM,第一台虚拟机VM1虽然申请了8GB内存,但只使用了2GB,而第二台虚拟机VM2此时处于关机状态,虽然也是申请了8GB内存,但使用了0B,可见此时内存是overcommit的。

如果一台VM伴随着程序的运行不断申请内存,已经申请占用了6GB内存,在宿主机看来,它的确是消耗了6GB内存。如果此时发生了GC,释放了34GB内存。在虚拟机的操作系统看来,此时只占用了2GB内存,但这释放的4GB内存并没有交还到宿主机。在宿主机看来,它还是占用了6GB内存。

但宿主机(Hypervisor)并不是放任内存一直被空闲占用,它会每隔一段时间(如60s)去扫描一下VM1的内存,以便了解它真实的内存状态。此时,另一台虚拟机VM2也不断地向宿主机申请内存空间,此时宿主机还剩余1GB空间,所以刚开始申请内存没有遇到问题,但当VM2申请内存+已分配给VM1的6GB内存>内存警戒值(一般是94%×总共物理内存)的时候,会触发内存气球机制。结合Hypervisor内存扫描掌握的客户机内存的使用情况,通过“空闲内存税”算法,在VM1内确定启动“气球”,Balloon驱动会不断地向客户机操作系统申请内存,并将申请的内存分配给VM2虚拟机使用,示意图如图3-7所示。

图3-7 内存气球机制示意图

通过底层的虚拟化技术将底层计算资源抽象之后,就可以在数据中心层面形成一个统一的计算资源池,这就是云计算设计的初衷。资源池化,按需计费。当完成池化以后,用户申请使用计算资源的时候,就可以从池中取出一部分资源供用户使用,当用户退订资源后,这部分资源又回到池中,供其他用户使用。

我把容器也纳入计算虚拟化,容器相比于虚拟机有很多优势,如:轻量级(无须打包整个操作系统,镜像通常在几百兆字节之内,相比于虚拟机动辄几GB的镜像要小很多)、跨平台(容器一直宣传的就是一次打包,多次部署,build->ship->run)、细粒度(容器可以将CPU等资源进行更细粒度的划分,如0.1个CPU)。当然,容器也有很多问题,如:隔离比较差、不够安全等,这些将在后面单独介绍。容器技术并非是一个新东西,LXC很早就有了,容器依赖的cgroup资源限制、namespace资源隔离等,这些都是Linux已经有的技术,Docker最大的贡献应该是定义了一套镜像规范。容器技术目前以Docker最为出名,当然还有Rkt、Kata等容器。为了不使容器技术过于封闭,Linux基金会联合各大厂商制定了OCI规范。