再谈EPT与影子页表


之前写过一篇博客记录自己对EPT和影子页表的学习。但是随着其他知识的输入,发现之前对EPT和影子页表的理解还太肤浅。本人在最近的学习过程中又遇到了与EPT和影子页表有关的话题,发现自己还有很多细节问题没有弄懂。故本文旨在进一步分析自己对两者概念的理解,以及对两者之间差异的认识。相较于之前,多了一些自己的想法。

本文的提纲如下:

  1. 简述Linux内存映射
  2. 虚拟机的内存映射—以KVM为例
  3. 影子页表机制的原理概述
  4. EPT机制的原理概述
  5. 比较影子页表和EPT机制

一、简述Linux内存映射

这部分其实是一个很庞大的话题,它包括分段、分页机制等,在不同架构、不同地址转换机制下,地址转换过程是不同的。本文的重点不在于这些复杂的分段分页保护机制、保护模式、实模式等内容。主要着眼于以下几个概念和问题:

  • 虚拟地址和物理地址

  • MMU

  • 内存置换

  • 缺页中断处理

  • 页表项初始化过程

1.1 虚拟地址和物理地址
从进程和CPU角度分析虚拟地址和物理地址:

虚拟地址(VA,virtual address):对于正在运行的进程而言,代码指令中指针所指的地址、函数入口地址、返回地址等,进程可以直接看到的地址属于虚拟地址。

物理地址(PA,physical address):对于物理CPU来说,CPU在执行具体的指令时,他需要获取一些地址中具体的数据信息,此时他访问的地址属于物理地址。

总的来说,物理地址用于访问真实存在于内存空间中的内容;虚拟地址则是应用程序所能看到的地址信息。在实际执行中需要将进程中的虚拟地址转化为物理地址后,继续之后的运行。

1.2 MMU

那么实际操作过程中,是如何进行虚拟地址和物理地址的转化呢?这就是接下来要介绍的MMU。

通过MMU来实现虚拟地址和物理地址的转换

MMU(内存管理单元,Memory Management Unit;又称PMMU,分页内存管理单元,Paged Memory Management Unit)。它的作用包括:

​ a. 负责虚拟地址到物理地址的转换

​ b. 提供硬件机制的内存访问权限检查

如下图所示,没有启动或者没有MMU时,外设(包括物理内存)等所有部件使用的都是物理地址,cpu通过物理地址来访问外设(包括物理内存)。启动MMU后,CPU核心对外发出虚拟地址给MMU,MMU把虚拟地址转换为物理地址,最后使用物理地址读取实际设备

以二级页表为例分析MMU的工作原理

以下图32位CPU为例,线性地址32位。

外部传入CR3寄存器的值,该值为存放页目录表页面的物理地址,即下图中第一个表的基地址,该地址可以认为是地址映射的入口。

线性地址的32位中,前10位用于索引页目录表中的页目录项(页目录项包含某页表的基地址),中间10位用于索引页表中的页表项(页表项包含某一个内存页的基地址),最后12位是页内地址偏移,该偏移在线性地址和物理地址中是不变的,利用偏移量和已找到的内存页,就可得到原线性地址转化后的物理地址。

在整个过程中,需要外部输入给MMU的内容有:CR3寄存器的值、进程需访问的虚拟地址。

1.3 内存置换

我被一个问题困惑了很久:我们都买过手机或者PC,并且结合已知的虚拟地址和物理地址的概念,可以知道虚拟内存地址空间远远大于物理内存地址空间,而物理内存才是真正能被CPU访问并取值的地方,那么是否存在多个虚拟地址映射到一个物理地址的情况呢?如果是这样,CPU和操作系统又是怎么解决这样的冲突呢?毕竟不同的进程访问相同的物理内存肯定是有内容上的冲突的。

虚拟内存的概念

虚拟内存,是指将内存中暂时不需要的部分写入硬盘,看上去硬盘扩展了内存的容量,所以叫做“虚拟”内存。使用虚拟内存,应用程序可以使用比实际物理内存更大的内存空间。

页面置换

基于上述的描述,我们可以知道:不是所有进程的虚拟页面都会作为页帧存在于物理内存中。并且虚拟页只有加载到物理内存才可以被CPU访问或读写。

假设某一时刻,物理内存页帧已经被写满了,但这时又需要将一个页写到物理内存中,就需要将原本在物理内存中的某一页换出来。这个过程就叫做页面置换。

1.4 缺页中断处理

有多种原因可能导致缺页中断,这里只讨论由于虚拟内存页没有被映射到物理内存或者被置换出的情况。

根据1.3可知,进程运行过程中,访问的虚拟地址对应的虚拟页可能不在物理内存中。这时该如何处理?

进程访问的物理页不在内存时如何处理
  • CPU在执行一条指令时,如果发现他要访问的页没有在内存中(即该虚拟页的页表项存在位为0),那么停止该指令的执行,并产生一个页不存在的中断,触发缺页中断处理函数。
  • 其具体流程为:
    • 保护CPU现场
    • 分析中断原因
    • 转入缺页中断处理函数
    • 恢复CPU现场,继续执行
  • 其中缺页中断处理函数的工作为:
    • 首先,如果页表不存在或被交换出,则要首先分配页面给页表
    • 然后才真正实施页面的分配,并在页表上做记录。

缺页中断处理过程详解 这篇文章可以再看一下。

1.5 页表项初始化过程

最开始的时候用户态物理内存为空,当不断有进程开始执行时,就会开始访问虚拟内存,由于此时的虚拟页并没有映射到物理页帧,因此会触发缺页中断,然后从物理页中申请页,并分配给虚拟页,进而建立虚拟页和物理页帧之间的映射关系,同时填充页表项。

二、虚拟机的内存映射机制—以KVM为例

和第一部分中Linux物理机的内存映射机制不同,虚拟机运行在VMM之上,虚拟机可以认为是物理机的一个进程在执行。因此可以认为虚拟机将自己的虚拟地址分配给虚拟机,虚拟机将其作为物理地址使用。本文将主要介绍KVM框架下的虚拟机的内存映射关系。

为了实现内存虚拟化,让客户机使用一个隔离的、从零开始且具有连续的内存空间,KVM 引入一层新的地址空间,即客户机物理地址空间 (Guest Physical Address, GPA),这个地址空间并不是真正的物理地址空间,它只是宿主机虚拟地址空间在客户机地址空间的一个映射。

对客户机来说,客户机物理地址空间都是从零开始的连续地址空间。但对于宿主机来说,客户机的物理地址空间并不一定是连续的,客户机物理地址空间有可能映射在若干个不连续的宿主机地址区间。

由于客户机物理地址不能直接用于宿主机物理 MMU 进行寻址,所以需要把客户机物理地址转换成宿主机虚拟地址 (Host Virtual Address, HVA),为此,KVM 用一个 kvm_memory_slot 数据结构来记录每一个地址区间的映射关系,此数据结构包含了:

  • 对应此映射区间的起始客户机页帧号 (Guest Frame Number, GFN)
  • 映射的内存页数目
  • 起始宿主机虚拟地址。

于是 KVM 就可以实现对客户机物理地址到宿主机虚拟地址之间的转换。即:

  • 首先根据客户机物理地址找到对应的映射区间。
  • 然后根据此客户机物理地址在此映射区间的偏移量就可以得到其对应的宿主机虚拟地址。
  • 进而再通过宿主机的页表也可实现客户机物理地址到宿主机物理地址之间的转换,也即 GPA 到 HPA 的转换。

实现内存虚拟化,最主要的是实现客户机虚拟地址 (Guest Virtual Address, GVA) 到宿主机物理地址之间的转换。

根据上述内容可知,实现GVA(客户机虚拟地址,Guest Virtual Address)到HPA(宿主机物理地址,Host Physical Address)的转换,需要以下操作:

  • 客户机虚拟地址(GVA)到客户机物理地址(GPA)的转换,借助客户机页表(GPT)。
  • 虚拟机物理地址(GPA)到宿主机虚拟地址(HVA)的转换,借助kvm_memory_slot。
  • 宿主机虚拟地址(HVA)到宿主机物理地址(HPA)的转换,借助宿主机页表(HPT)。

三、影子页表机制的原理概述

3.1 影子页表的地址映射关系
GVA--->HPA

从第二部分可以看到,客户机虚拟地址(GVA)到宿主机物理地址(HPA)的转换需要分为三次,这会造成很大的性能开销。影子页表的提出实现了从GVA到HPA的一次转换。即下图中,原始的KVM虚拟机内存映射为该图左侧流程,影子页表为该图右侧流程。

3.2 影子页表的缺页异常处理机制

通过影子页表进行寻址的过程中,有两种原因会引起影子页表的缺页异常:

  • 一种是由客户机本身所引起的缺页异常。
    • 具体来说就是客户机所访问的客户机页表项存在位 (Present Bit) 为 0,或者写一个只读的客户机物理页,再者所访问的客户机虚拟地址无效等。
  • 另一种异常是由客户机页表和影子页表不一致引起的异常。

当缺页异常发生时,KVM 首先截获该异常,然后对发生异常的客户机虚拟地址在客户机页表中所对应页表项的访问权限进行检查,根据触发异常的原因,进行相应的处理:

  • 如果该异常是由客户机本身引起的:KVM 则直接把该异常交由客户机的缺页异常处理机制来进行处理。
  • 如果该异常是由客户机页表和影子页表不一致引起的:KVM 则根据客户机页表同步影子页表。
    • 为此,KVM 要建立起相应的影子页表数据结构,填充宿主机物理地址到影子页表的页表项,还要根据客户机页表项的访问权限修改影子页表对应页表项的访问权限。

由此可见,只要是缺页异常,不论是由客户机产生的还是VMM都会陷入到VMM进行处理。

3.3 影子页表与客户机页表、MMU的关系
影子页表与客户机页表的关系

在影子页表中,每个页表项指向的都是宿主机的物理地址。这些表项是随着客户机操作系统对客户机页表的修改而相应地建立的。客户机中的每一个页表项都有一个影子页表项与之相对应。

影子页表与MMU的关系

影子页表可被载入物理 MMU 为客户机直接寻址使用, 所以客户机的大多数内存访问都可以在没有 KVM 介入的情况下正常执行,没有额外的地址转换开销,也就大大提高了客户机运行的效率。

3.4 影子页表的初始化

此部分内容可以和1.5的过程类比。

  • 最初始的时候,影子页表为空。

  • 当CPU开始执行指令时,在影子页表中无法找到虚拟地址对应的物理地址。此时会触发缺页异常。

  • KVM捕获缺页异常,对触发异常的原因进行判断,发现原因是没有该地址对应的影子页表数据结构,于是由KVM处理。

  • KVM要建立起相应的影子页表数据结构,填充宿主机物理地址到影子页表的页表项,还要根据客户机页表项的访问权限修改影子页表对应页表项的访问权限。

四、EPT机制的原理概述

4.1 EPT的地址映射关系
GPA--->HPA

EPT技术由以下两部分组成,这两部分都是由硬件自动完成的:

  • 客户机原有的客户机虚拟地址(GVA)到客户机物理地址(GPA)的转换,借助GPT。

    • 客户机页表被载入 CR3,MMU执行GVA转GPA。
  • EPT页表实现从客户机物理地址(GPA)到宿主机物理地址(HPA)的转换,借助EPT。

    • EPT 页表被载入专门的 EPT 页表指针寄存器 EPTP,进而由MMU执行GPA到HPA的转换。
    • EPT页表转换过程如下图:
4.2 EPT的缺页异常处理机制

在客户机物理地址(GPA)到宿主机物理地址(HPA)转换的过程中,由于缺页会导致客户机退出,产生EPT缺页异常。其处理过程如下:

  • KVM 首先根据引起异常的客户机物理地址,映射到对应的宿主机虚拟地址。
  • 然后为此虚拟地址分配新的物理页。
  • 最后 KVM 再更新 EPT 页表,建立起引起异常的客户机物理地址到宿主机物理地址之间的映射。

这里值得说明的是,客户机内部的缺页异常不会致使客户机退出,提高了客户机运行的性能。

4.3 EPT的初始化

此部分内容可以和本文1.5、3.4过程类比。

  • 最初始的时候,GPT和EPT为空。

  • 当CPU开始执行指令时,在GPT中无法找到客户机虚拟地址(GVA)对应的客户机物理地址(GPA)。于是会触发缺页中断。这部分由客户机自行处理。

  • 当CPU找到客户机物理地址后,在EPT找不到客户机物理地址(GPA)对应的宿主机物理地址(HPA)。于是会触发EPT缺页异常,产生VM-Exit,陷入到VMM。

  • KVM 首先根据引起异常的客户机物理地址,映射到对应的宿主机虚拟地址。然后为此虚拟地址分配新的物理页。

  • 最后 KVM 再更新 EPT 页表,建立起引起异常的客户机物理地址到宿主机物理地址之间的映射。

五、比较影子页表和EPT机制

本部分从四个角度对影子页表机制和EPT机制进行对比:

实现方式复杂度

影子页表基于软件实现GVA到HPA的的映射关系,客户机页表和和影子页表的同步也比较复杂。EPT基于硬件,将两种地址转换算法都嵌入到MMU硬件单元中,实现方式简化。

因此,EPT机制实现过程更为简化。

缺页异常

影子页表对于所有的缺页异常(包括客户机内部触发缺页异常、影子页表与客户机页表不一致)都会触发VM-Exit,陷入到VMM中,由VMM判断异常类型后再决定交由客户机处理或由VMM处理。EPT机制中,由于GPA到HPA转化过程中产生的EPT缺页异常会导致VM-Exit,陷入VMM;而对于客户机自身产生的缺页,则由客户机自省处理。

因此,EPT机制下,整个系统缺页异常处理的效率更高。

内存消耗

影子页表机制下,由于客户机中每个进程都有自己的虚拟地址空间,所以 KVM 需要为客户机中的每个进程页表都要维护一套相应的影子页表。EPT维护的是虚拟机物理地址(GPA)到宿主机物理地址(HPA)之间的映射关系,所以需要为每一个虚拟机维护一套EPT页表。。

因此,EPT机制占用更小的内存资源。

执行效率

影子页表实现一步转化,并且影子页表可以加载到MMU中,因此在执行过程中,只需要执行一次地址转换就可以实现GVA到HPA的转换。EPT页表需要进行两次地址转换,先执行GVA转GPA,再执行GPA转HPA。

因此,影子页表具有更高的执行效率。

六、说明

本文1.1、1.5、第5部分为本人理解,如不严谨,还望指出。

本文1.2内容部分借鉴Linux内存映射机制页表机制,部分为自述。1.3内容部分借鉴页面置换算法。1.4内容部分借鉴详解缺页中断-----缺页中断处理(内核、用户)第2、3、4部分内容借鉴KVM 内存虚拟化及其实现

问题:

  • 没有弄清楚线性地址和虚拟地址的关系?

  • 不同的进程一定具有不同的CR3吗?

Logo

更多推荐