系统虚拟化—内存管理与虚拟化(hypervisor)

3 篇文章 1 订阅

系统虚拟化—内存管理与虚拟化(hypervisor)

一、Protected-Mode内存管理

1.1 内存管理介绍

IA-32体系结构的内存管理机制分为两部分:分段和分页。 分段提供了一种隔离各个代码,数据和堆栈模块的机制,以便多个程序(或任务)可以在同一处理器上运行而不会互相干扰。 分页提供了一种机制,用于实现常规的按需分页的虚拟内存系统,其中程序执行环境的各个部分根据需要映射到物理内存中。 分页还可以用于提供多个任务之间的隔离。 在保护模式下运行时,必须使用某种形式的分段。 没有模式位可禁用分段。 但是,使用分页是可选的。

如图1-1所示,分段提供了一种机制,用于将处理器的可寻址内存空间(称为线性地址空间)划分为较小的受保护的地址空间(称为段)。 段可用于保存程序的代码,数据和堆栈,或保存系统数据结构(例如TSS或LDT)。 如果处理器上正在运行多个程序(或任务),则可以为每个程序分配自己的一组段。 然后,处理器会强制这些段之间的边界,并通过写入另一个程序的段来确保一个程序不会干扰另一个程序的执行。 分段机制还允许键入分段,以便可以限制对特定类型的分段执行的操作。

系统中的所有段都包含在处理器的线性地址空间中。为了在特定段中定位字节,必须提供逻辑地址(也称为远指针)。逻辑地址由段选择器和偏移量组成。段选择器是段的唯一标识符。除了其他功能外,它还提供了到描述符表(例如全局描述符表,GDT)的数据段(称为段描述符)的偏移量。每个段都有一个段描述符,该描述符指定段的大小,段的访问权限和特权级别,段类型以及段的第一个字节在线性地址空间(称为的基地址)中的位置。细分)。逻辑地址的偏移部分将添加到该段的基地址中,以在该段中定位一个字节。因此,基地址加上偏移量就形成了处理器线性地址空间中的线性地址。
图1-1 分段和分页
图1-1 分段和分页

如果不使用分页,则处理器的线性地址空间将直接映射到处理器的物理地址空间。物理地址空间定义为处理器可以在其地址总线上生成的地址范围。

由于多任务计算系统通常会定义一个线性地址空间,而线性地址空间要比在物理内存中一次全部包含在内存上可行的要大得多,因此需要某种“虚拟化”线性地址空间的方法。线性地址空间的这种虚拟化是通过处理器的分页机制处理的。
分页支持“虚拟内存”环境,其中使用少量的物理内存(RAM和ROM)以及一些磁盘存储来模拟较大的线性地址空间。使用分页时,每个段都分为几页(通常每个大小为4 KB),这些页存储在物理内存或磁盘中。操作系统或执行人员维护页面目录和一组页面表以跟踪页面。当程序(或任务)尝试访问线性地址空间中的地址位置时,处理器使用页目录和页表将线性地址转换为物理地址,然后在请求的地址上执行请求的操作(读取或写入)。内存位置。

如果正在访问的页面当前不在物理内存中,则处理器会中断程序的执行(通过生成页面错误异常)。然后,操作系统或执行程序从磁盘将页面读取到物理内存中,并继续执行程序。

如果在操作系统或执行程序中正确实现了分页,则物理内存和磁盘之间的页面交换对于正确执行程序是透明的。当它们以虚拟8086模式运行时,即使是为16位IA-32处理器编写的程序也可以被分页(透明)。

1.2 X86内存分段机制

(1)段的介绍

我们可以把整个内存想象成一个长长的纸条,在内存中分段,就像从长纸条上截下一小段来。注意,我们并没有改变内存的物理性质,并不是真的把它分成几块。段的划分是逻辑上的,从本质上来说,只是如何去看待和组织内存中的数据和指令而已。由于Cpu是自动化器件,在给出起始地址后,它将从这个地址开始,自动的取出每条指令执行。为了做某件事而编写的指令,它们一起形成了我们平时所说的程序。程序总要操作大量数据,这些数据也应该集中在一起,位于内存中的某个地方,形成一个小段,称为数据段。故此,Cpu至少要提供2个段寄存器,分别是代码段寄存器(Code segment,CS)和数据段寄存器(Data segment,DS)。

因此,分段机制其实就是把虚拟地址空间中的虚拟内存组织成一些长度可变的称为段的内存单元。 80386虚拟地址空间中的虚拟地址(逻辑地址)由一个段部分和一个偏移部分构成。段是虚拟地址到线性地址转化的基础。每个段有三个参数定义:

1、段基地址,指定段在线性地址空间中的开始地址。基地址是线性地址对应于段中偏移0处。
2、段限长,是虚拟地址空间中段内最大可用偏移地址。定义了段的长度。
3、段属性,指定段的特性。如该段是否可读、可写或可作为一个程序执行,段的特权级等。
多个段映射到线性地址中的范围可以部分重叠或覆盖,甚至完全重叠,如图1-2-1所示。
在这里插入图片描述
图1-2-1 分段示意图

(2)相关数据结构

段的基地址、段限长以及段的保护属性存储在一个称为段描述符的结构项中。在逻辑地址到线性地址的转换映射过程中会使用这个段描述符。段描述符保存在内存中的段描述符表中。段描述符表是包含段描述符项的一个简单数组。 使用段选择符来指定段描述符表中一个段描述符的位置来指定相应的段。

(3)逻辑地址到线性地址的转化

即使是使用段的最小功能,使用逻辑地址也能访问处理器地址空间中的每一个字节。逻辑地址由 16 位的段选择符和 32 位的偏移量组成。

当需要访问处理器地址空间中的某个字节时。段选择符指定了该字节所在的段,偏移量指定了该字节在段中相对于段基址的位置。处理器会把每个逻辑地址转换成线性地址。线性地址是处理器线性地址空间中的 32 位地址。也是连续的 4GB 地址空间,地址范围从 0 到 0xFFFFFFFF。线性地址空间中含有系统定义的所有段和系统表。
处理器把逻辑地址转化成一个线性地址的过程(如图1-2-2所示):

1、使用段选择符中的偏移值(段索引)在GDT(全局描述符表) 或 LDT(局部描述符表)中定位相应的段描述符。(仅当一个新的段选择符加载到段寄存器中时才需要)
2、利用段描述符校验段的访问权限和范围,以确保该段是可以访问的并且偏移量位于段界限内。
3、利用段描述符中取得的段基地址加上偏移量,形成一个线性地址。
在这里插入图片描述
图1-2-2 逻辑地址到线性地址转换

(4)段描述符表

段描述符表示存放段描述符的一个数组。 它的长度可变,最多可以包含 8192 个段描述符,每个段描述符长度为 8 个字节。段描述符表有两种:
1、全局描述符表GDT
2、局部描述符表LDT
段描述符表结构如图1-2-3所示。
在这里插入图片描述
图1-2-3 全局和局部描述符表

段描述符表存储在由操作系统维护着的特殊数据结构中,由处理器的内存管理硬件来引用。 这些特殊的数据结构保存在只有操作系统能够访问的受保护的内存区域,防止被应用程序修改。

虚拟地址空间被分割成大小相等的两半。一半由GDT来映射变换到线性地址,另一半由LDT来映射。整个虚拟地址空间共含有 2^14 个段: 一半空间(2^13)是由GDT映射的全局虚拟地址空间,另一半是由LDT映射的局部虚拟地址空间。 指定一个描述符表(GDT或LDT)和表中的描述符号,就可以定位到一个段描述符(通过段描述符就可以定位到段)。

当任务切换时,LDT会更换成新任务的LDT,GDT不会改变,因为GDT所映射的一半虚拟地址空间是系统中所有任务公有的,LDT所映射的另一半则在任务切换时被改变。系统中所有任务共享的段有GDT来映射。

系统中每个应用程序对应一个任务,并且每个任务都有自己的LDT,如下图所示,应用程序A在任务A中运行,拥有LDTa ,用来映射段Codea和Dataa。类似地,应用程序B在任务B中运行,使用LDTb来映射Codeb和Datab。操作系统内核的两个段Codeos和Dataos 使用GDT来映射,这样它可以被像个人物所共享。 LDTa和LDTb两个段也使用 GDT来映射。如图1-2-4所示。
在这里插入图片描述
图1-2-4 LDT、GDT和任务切换示意图

当任务A在运行时,可以访问 LDTa映射的Codea 和Dataa段,以及GDT映射的操作系统段COdeos和Dataos。当任务B在运行时,可以访问的段包括LDTb映射的Codeb和Datab段,以及GDT映射的操作系统段COdeos和Dataos。

通过让每个任务使用不同的LDT,演示了虚拟地址空间如何隔离每个任务。当任务A在运行时,任务B的段不是虚拟地址空间的部分。因此,任务A无法访问任务B的内存。同样的,当任务B运行时,任务A的段也不能被任务B访问。这种使用LDT来隔离每个应用程序任务的方法,正式关键保护的需求之一。

每个系统必须定义一个GDT,并可用于系统中所有程序或任务。可以选定义一个或多个LDT。可以为每个运行任务定义一个LDT,或者某些任务共享一个LDT。
GDT本身并不是一个段,而是线性地址空间的一个数据结构。GDT的基线性地址和长度必须加载进 GDTR 寄存器中。处理器并不使用 GDT中的第一个描述符。把这个"空描述符"加载到段寄存器中并不会产生一个异常。但是,如果使用这些加载了空描述符的段选择符来访问内存就会引发一般保护性异常。通过使用这个段选择符初始化段寄存器,就会引发异常。

LDT表存放在LDT类型的系统段中。此时GDT必须含有LDT的段描述符。如果系统支持多LDT,那么每个LDT都必须在GDT中有一个段描述和段选择符。一个LDT的段描述符可以存放在GDT表的任何地方。 访问LDT需使用其段选择符。为了在访问LDT是减少地址转换次数,LDT的段选择符、段基址、段限长以及访问权限需要存放在LDTR寄存器中。

(5)段选择符

段选择符(或称段选择子)是段的一个十六位标志符,如图1-2-5所示。段选择符并不直接指向段,而是指向段描述符表中定义段的段描述符。 段选择符包括 3 个字段的内容:
1、求特权级RPL([0:1])
2、表指引标志TI([2])TI = 0 ,表示描述符在GDT中,TI = 1,表示描述符在LDT中。
3、索引值,给出了描述符在GDT或LDT表中的索引项号。(第3至15位)在GDT或LDT中选择8192个描述符之一。 处理器将Index值乘以8(段描述符中的字节数),然后将结果加到GDT或LDT的基地址(分别来自GDTR或LDTR寄存器)。
在这里插入图片描述
图1-2-5 段选择符

(6)段描述符

段描述符是一个存在于GDT或者LDT的数据结构。可为处理器提供段的大小和位置以及访问控制和状态信息。 每个段描述符长度是 8 字节,含有三个主要字段:段基地址、段限长和段属性。段描述符通常由编译器。链接器、加载器或者操作系统来创建,而不由应用程序创建。段描述符通用格式如图1-2-6所示:
在这里插入图片描述
图1-2-5 段描述符

1.3 X86 分段模型

1.3.1 Flat Model

(1)平坦模型(flat model)

系统最简单的内存模型是基本的“平面模型”,其中操作系统和应用程序可以访问连续的,未分段的地址空间。这种基本的平面模型在最大程度上使系统设计者和应用程序程序员都看不到体系结构的分割机制。

要使用IA-32架构实现基本的平面存储器模型,必须至少创建两个段描述符,一个用于引用代码段,另一个用于引用数据段(见图1-3-1)。但是,这两个段都映射到整个线性地址空间:也就是说,两个段描述符都具有相同的基地址值0和相同的段限制4 GB。通过将分段限制设置为4GBytes,即使没有物理内存驻留在特定地址,也可以防止分段机制为超出限制的内存引用生成异常。 ROM(EPROM)通常位于物理地址空间的顶部,因为处理器从FFFF_FFF0H开始执行。 RAM(DRAM)放置在地址空间的底部,因为重置初始化后DS数据段的初始基地址为0。
在这里插入图片描述
图1-3-1 Flat Model

(2)平坦模型和用户程序的结构

不分段的内存管理模型称为平坦模型(flat Model)。尽管说不分段,但是并不意味着真的不分段,不用信以为真。分段是Intel处理器的固有机制。处理器总是按照”段地址+偏移量“来形成线性地址,并没有绕开这种工作机制。

因此如图1-3-2所示,所谓的平坦模型,就是将全部的4GB内存整体上作为一个大段来处理,而不是分成小的区别。在这种模型下,所有段都是4GB,每个段的描述符都执行4GB的段,段的基地址都是0x00000000,段界限都是0xFFFFF,粒度为4KB。

在这种基本的平坦模式下,程序在编写的时候不分段,即,只保留一个段,代码和数据都在这个段内,相互邻接,但一般不交叉。很显然,在这种模式下,不能享受到段保护机制的好处,段界限和数据访问的检查仍然进行,但不会产生违例的情况。原因很简单,每个段描述符的基地址都是0,实际使用的段界限都是0xFFFFFFFF,就任务内的地址空间而言,对任何内存位置访问都是合法的。

传统上,这几个部分是以段为自然分界的。但是现在,它们混合在一起,按类型划分,而不是段,类似于几种流行的可执行文件中的节。
在这里插入图片描述
图1-3-2 基本的平坦模型

1.3.2 Protected Flat Model

受保护的平面模型与基本平面模型相似,不同之处在于,将段限制设置为仅包括实际存在物理内存的地址范围(请参见图1-3-3)。然后,在尝试访问不存在的内存时会生成一般保护异常(#GP)。此模型提供了针对某些程序错误的最低级别的硬件保护。

可以向此受保护的平面模型添加更多的复杂性以提供更多的保护。例如,为了使分页机制在用户和管理员代码与数据之间提供隔离,需要定义四个段:用户的特权级别为3的代码和数据段,以及管理员的特权级别为0的代码和数据段。通常,这些段彼此重叠,并从线性地址空间中的地址0开始。此平面分段模型以及简单的分页结构可以保护操作系统不受应用程序的影响,并且通过为每个任务或进程添加单独的分页结构,还可以保护应用程序彼此之间。几种流行的多任务操作系统使用类似的设计。
在这里插入图片描述
图1-3-3 Protected Flat Model

1.3.3 Multi-Segment Model

(1)多段模型

多段模型(如图1-3-4所示)使用分段机制的全部功能为代码,数据结构以及程序和任务提供硬件强制保护。在这里,每个程序(或任务)都有自己的段描述符表和自己的段。这些段可以完全独立于其分配的程序,也可以在程序之间共享。对所有段的访问以及对系统上运行的单个程序的执行环境的访问都由硬件控制。

访问检查不仅可以用于防止引用超出段限制的地址,还可以防止在某些段中执行不允许的操作。例如,由于代码段被指定为只读段,因此可以使用硬件来防止写入代码段。为段创建的访问权限信息也可以用于设置保护环或级别。保护级别可用于保护操作系统过程,以防止被应用程序未经授权的访问。
在这里插入图片描述
图1-3-4 Multi-Segment Model

(2)传统的多段模型和用户程序结构

传统的分段模型(Multi-Segment Model)适用于开启了页功能之后的系统环境,如图1-3-5所示,首先依然是按照程序的结构分段,创建各个段的描述符,但是,段是在任务自己的虚拟地址空间创建内分配的,而不是在物理内存中分配。因此,段描述符中的基地址是段的线性地址,或者说是虚拟地址。

因为开启了页功能,虚拟地址空间上的段要映射到物理内存中的一个或多个页。段是连续的,但是它所占用的页不要求是相邻的。在未开启页功能之前,段基地址和段便宜相加产生的线性地址就是物理地址,开启页功能之后,线性地址还要经过页部件转换后,才能得到实际的物理地址。
在这里插入图片描述
图1-3-5 分页机制下的多段模型示意图

二、x86 分页机制

2.1 分页模型

(1) x86分页的主要控制位

分页的行为主要有以下寄存器所控制:
寄存器CR0的16和31位的WP和PG标记。
寄存器CR4的4,5,7,17,20,22和22位的 PSE, PAE, PGE, PCIDE, SMEP, SMAP和PKE标记。
寄存器IA32_EFER_MSR的8和11位的 LME和NXE标记。
寄存器 EFLAGS的18位的AC标记。
其中CR0的PG位是否被置位,主要用于决定是否开启分页功能。

(2) 三种分页模型

如果 CR0.PG = 0,则分页功能没有被开启,此时线性地址被直接视为物理地址。分页功能因为能力不同分成如下三种模型:
1、32-bit paging,当CR0.PG = 1, CR4.PAE = 0时启用。
2、PAE paging, 当CR0.PG = 1, CR4.PAE = 1和IA32_EFER.LME = 0时启用。
3、4-level paging 当CR0.PG = 1, CR4.PAE = 1, and IA32_EFER.LME = 1时启用。
三种分页模式在以下细节方面有所不同:
1,线性地址宽度。可以转换的线性地址的大小。
2,物理地址宽度。分页产生的物理地址的大小。
3,页面大小。线性地址转换的粒度。将同一页上的线性地址转换为同一页上的相应物理地址。
4,支持禁用执行权限。在某些分页模式下,可以防止软件从其他可读的页面中获取指令。
5,支持PCID。通过4级分页,软件可以启用一种功能,逻辑处理器可通过该功能为多个线性地址空间缓存信息。当软件在不同的线性地址空间之间切换时,处理器可以保留缓存的信息。
6,支持保护锁。通过4级分页,软件可以启用一种功能,通过该功能,每个线性地址都与保护键相关联。软件可以使用新的控制寄存器为每个保护密钥确定软件如何访问与该保护密钥相关联的线性地址。
如图2-1-1所示:
在这里插入图片描述
图2-1-1 不同分页模式属性图

(3) 32-BIT分页模型

如果CR0.PG = 1并且CR4.PAE = 0,则逻处理器使用32位分页。32位分页将32位线性地址转换为40位物理地址。尽管40位对应于1 TByte,但线性地址限于32位。在任何给定时间最多可以访问4 GB的线性地址空间。

32位分页使用分页结构的层次结构来生成线性地址的转换。 CR3用于查找第一个分页结构,即页面目录。32分位分页模式主要控制于CR3和CR4寄存器,32位分页可以将线性地址映射到4 KB页面或4 MB页面,由于篇幅问题,此处不再赘述,详细内容还请查阅Intel芯片手册。。如下用图表方式粗略的介绍32为分页的机制规则。其中图2-1-2说明了使用4 KB页面时的转换过程。图2-1-3涵盖了4 MB页面的情况。
在这里插入图片描述
图2-1-2 32位分页下4k页大小的线性地址转换

在这里插入图片描述
图2-1-2 32位分页下4M页大小的线性地址转换

如下图2-1-3总结了CR3的格式和32位分页的分页结构。(其他分页模式均有期对应图标,详细内容还请查阅Intel芯片手册)。对于分页结构,它分别标识映射页面的页面格式,引用其他分页结构的页面格式以及由于“不存在”而都不执行的条目的格式;突出显示位0(P)和位7(PS),因为它们确定如何使用此类条目。
在这里插入图片描述
图2-1-2 32位分页结构下CR3对应格式

(4) PAE分页模型

如果CR0.PG = 1,CR4.PAE = 1,并且IA32_EFER.LME = 0,则逻辑处理器将使用PAE分页。PAE分页将32位线性地址转换为52位物理地址。尽管52位对应于4 PByte,线性地址限制为32位;在任何给定时间最多可以访问4 GB的线性地址空间。
使用PAE分页时,逻辑处理器会维护一组四(4)个PDPTE寄存器,这些寄存器是从CR3中的地址加载的。线性地址使用内存分页结构的4个层次进行转换,每个层次使用PDPTE寄存器之一定位。 (这与其他分页模式不同,在其他分页模式中,CR3引用了一个层次结构。)

同样的,PAE分页也是主要使用CR3和CR4寄存器来都分页机制进行选择和控制。下面用图粗略的展示PAE分页可以将线性地址映射到4 KB页面或2 MB页面的过程。其中图2-1-3说明了生成4 KB页面时的转换过程。图2-1-4涵盖了2 MB页面的情况。至于转换中具体的控制和选择,由于篇幅问题,此处不再赘述,还请查阅Intel芯片手册。
在这里插入图片描述
图2-1-3 PAE分页下4K页大小的线性地址转换

在这里插入图片描述
图2-1-4 PAE分页下2M页大小的线性地址转换

(4) 4-level分页模型

如果CR0.PG = 1,CR4.PAE = 1,并且IA32_EFER.LME = 1,则逻辑处理器将使用4级分页。对于4级分页,线性地址将使用内存分页结构的分层结构进行转换,该分层结构使用 CR3的内容。 4级分页将48位线性地址转换为52位物理地址。 尽管52位对应于4 PByte,但线性地址限于48位。 在任何给定时间,最多可以访问256 TB的线性地址空间。

4级分页使用分页结构的层次结构来生成线性地址的转换。 CR3用于查找第一个分页结构PML4表。 将CR3与4级分页一起使用取决于是否通过设置CR4.PCIDE启用了过程上下文标识符(PCID):

同样的,4分页也是主要使用CR3和CR4寄存器来都分页机制进行选择和控制。下面用图粗略的展示PAE分页可以将线性地址映射到4 KB页面或2 MB页面的过程。4级分页可以将线性地址映射到4 KB页面,2 MB页面或1 GB页面。图2-1-5说明了生成4 KB页面时的转换过程。图2-1-6讨论了2 MB页面的情况,图2-1-7讨论了1 GB页面的情况。至于转换中具体的控制和选择,由于篇幅问题,此处不再赘述,还请查阅Intel芯片手册。
在这里插入图片描述
图2-1-5 4分页下4K页大小的线性地址转换
在这里插入图片描述

图2-1-6 4分页下2M页大小的线性地址转换
在这里插入图片描述
图2-1-7 4分页下1G页大小的线性地址转换

2.2 解读分页机制

(1)分页机制概要

分页是更加粒度化的内存管理机制,与分段机制将内存划分成以基地址和长度描述的多个段进行管理不同,分页机制是用粒度化的单位“页”来管理线性地址空间和物理地址空间。X86架构下一个典型的页的大小是4KB,则一个4GB的虚拟空间可以划分成1024x102个页面。物理地址空间划分同理。X86架构允许大于4KB的页面大小(如2MB,4MB),限于篇幅关系,这里只介绍最为经典的4KB页面管理机制。

同时,分页机制让现代操作系统中的虚拟内存机制成为了可能,感谢这种机制,一个页面可以存在于物理内存中,也可以存放在磁盘的交换区域(如Linux下的Swap分区,Windows下的虚拟内存文件)中,程序可以使用比机器内存更大的内存区域。

分页机制的核心思想就是通过页表将线性地址转换为物理地址,并配合旁路转换缓冲区(Translation Lookaside Buffer, TLB)来加速地址转换过程。操作系统在启动过程中,通过将CR0寄存器的PG位置1来启动分页机制,如图2-2-1展示了分页机制的概要。
在这里插入图片描述
图2-2-1 分页机制概要

从图中可以看出,分页机制主要由页表、CR3寄存器和TLB三个部件构成,下面以4KB大小为例,对各个部件进行解读。

(2)页表

页表(Page Table)是用于将线性地址转化为物理地址的主要数据结构,一个地址对齐到页边界后的只称为页帧号(或页框架),他实际上是该地址所在页面的基地址,线性地址对应页帧号即虚拟页帧号(Virtual Frame Number, VFN),物理地址对应的页帧号即物理页帧号(Physical Frame Number, PFN)或机器页帧号(Machine Frame Number)。故也可以认为页表是存储VFN到PFN映射的数据结构。4KB大小的页面使用两级页表。如图2-2-2所示。
在这里插入图片描述
图2-2-2 二级页表

1、页目录表(Page Directory Entry):包含页表的物理地址。页目录项存放在页目录(Page Directory)中,CPU使用线性地址的22~31位索引页目录,以获的该线性地址对应的目录项。每个页目录项位4B的大小,故页目录占用一个4KB大小的物理页面,共包含1024的页目录项。

2、页表项(Page Table Entry):页表项包含该线性地址对应的PFN。页表项存放在页表(Page Table)中,CPU使用线性地址的12–21位索引页表,获得该线性地址对应的页表项。通过将线性地址的0~11位偏移量和基地址详解,就可以得到线性地址对应的物理地址。页表项为4B的大小,故页表包含1024个页表项,占用一个4KB页面。
如2-2-3显示了页目录项、页表项的构成,通过其中各个字段,可以对页面访问权限,缓存机制、全局页等属性进行控制,具体含义不再赘述。这里只关注其中的P(Present)字段,该字段使虚拟内存的实现成为可能。P字段根据其值不同,可以代表两种情况。
在这里插入图片描述
在这里插入图片描述
图2-2-3 页目录表和页表

1)、P = 1,物理页面存在有物理内存中,CPU完成地址转换后,可以直接访问该页面。
2)、P = 0,页面不再物理内存中,当CPU访问该页面时会产生一个缺页错误(Page Fault)并交由操作系统的缺页错误处理程序处理。通常操作系统会将存放在磁盘上的页面调入物理内存,使访问继续。P = 0 时,页目录项、页表项格式变为图2-2-4中的格式。此时1~31位供操作系统使用以记录物理页面在磁盘上的信息,通常是物理页面在磁盘上的位置。
在这里插入图片描述
图2-2-4 P = 0时的页目录项、页表项

CPU在索引页目录前,必须知道页目录所在的物理地址,该物理地址存放CR3(Contorl Register 3)寄存器中,也称为页目录基地址寄存器(Page-directory base register, PDBR)。一个进程在运行前,必须将其页目录的基地址存入CR3。页目录的基地址必须对齐到4K边界。

(3)TLB

为了提高地址转换的效率,x86架构使用TLB对最近用到的页面映射进行缓存,当CPU访问某个线性地址,其所在页面的映射存在于TLB中时,无须查找页表即可进行地址转换。注意,TLB中存放的是VFN到PFN的转换,也就是说,CPU从TLB获得一个线性地址对应的PFN后,任然要和线性地址的偏移相加,才能得到最后的物理地址,而非直接从TLB中获取物理地址。

TLB作为缓存,其能存放的映射条目是有限的,当TLB中没有空闲条目可用时,替换哪一个旧条目由CPU决定。

TLB也存在缓存一致性的问题,这主要是指TLB中的映射条目和页表中的映射条目的一致性。当操作系统对页表进行修改后,要负责对TLB中对应的条目或者整个TLB进行刷新。从软件的角度来看,x86提供了两种方式刷新TLB:

1、更新CR3:此操作可以导致TLB被整体刷新,TLB中的所有的条目失效(全局TLB除外)。操作操作系统将当前CR3中的值重新写回CR3以刷新整个TLB。进程切换时,新进程的也目录基地址会写入CR3而使老进程在TLB中的条目失效。

2、INVLPG指令:这是一种更细粒度的刷新,操作系统可以用它对TLB中单独的页目录项、页表项进行刷新。这通常是在操作系统修改页表后进行的(例如分配/释放了页面)。

前面已经说过逻辑地址转换成线性地址的过程,这里再总结一下CPU使用分页机制,将线性地址转换成物理地址的过程:
1)、CPU访问一个线性地址,映射在TLB中则跳到6)。如映射不存在于TLB中,我们称为一次TLB Miss(TLB缺失)发生,进行下一步。
2)、查找页表,页面在物理内存中则跳到4),不再进行下一步。
3)、操作系统的缺页处理函数接管,通常会进行如下操作:
a)将页面从磁盘复制到物理内存中;
b)更改对应页表项,设置P位为1,并对其他字段进行相应设置;
c)刷新TLB中对应的页表项;
d)从缺页错误处理函数中返回。
4)、到这一步,页面已经存在于物理内存中,并且页表已经包含映射。此时,重新执行引发TLB Miss指令。
5)、TLB Miss再次发生,CPU重新查页表,把对应的映射插入到TLB中。
6)、到这一步,TLB就已经包含了该线性地址对应的PFN。通过线性地址中的偏移部分和PFN相加,就得到了对应的物理地址。

三、内存虚拟化

3.1 基于软件的完全虚拟化

(1) 概述

为了让客户机操作系统使用一个隔离的、从零开始且具有连续的内存空间,VMM引入一层新的地址空间,即客户机物理地址空间。客户机物理地址空间是客户机操作系统所能“看见”和管理的物理地址空间,这个地址不是真正的物理地址空间,它和物理地址空间还有一层映射。有了客户机物理地址空间,就形成了从应用程序所在的客户机虚拟地址(Guest Virtual Address, GVA)到客户机物理地址(Guest Phyical Address, GPA),再从客户机物理地址GPA到宿主机物理地址(Host Physical Address, HPA)的两层地址转换。前一个转换由客户机操作系统完成,后一个转换有VMM负责。

为了实现从客户机物理地址GPA到宿主机物理地址HPA的地址翻译,VMM为每个虚拟机动态地维护了一张从客户机物理地址到宿主机物理地址映射的表。
有了这张表之后,VMM截获任何试图修改客户机页表或刷新TLB的指令,根据这张表,将修改从客户机虚拟地址到客户机物理地址映射的操作,变成修改客户机虚拟地址到相应的宿主机地址地址映射的操作。

自从有了这张表之后,虽然宿主机物理地址只有一个零起始地址,但在不同客户机物理地址空间里,可以各有一个零起始地址,而客户机操作系统看来连续的客户机物理内存空间,其对应的宿主机物理内存空间可能是不连续的,而这增加了VMM为多个虚拟机分配宿主机物理内存时的灵活性,提高了宿主机物理内存的利用率。
VMM还可以通过该表确保运行于同一宿主机上的不同客户机访问的是不同的物理内存,即相同的客户机物理地址被映射到了不同的宿主机物理地址上。这样一来,一个客户机只能访问VMM通过该表设置分配给它的宿主机物理内存,而不能访问其他客户机拥有的宿主机物理内存。

有时,VMM使用页共享技术以写时复制(Copy On write)的方式让不同的客户机可以共享 包含相同数据的宿主机物理页,删除多余的也备份。这种页共享技术,是通过将不同客户机的某些客户机物理地址映射到相同的宿主机物理地址上,来实现共享这个宿主机物理地址对应的宿主机物理页。

除此之外,VMM可以在客户机完全不知情的情况下,将客户机所有的某一客户机物理页映射到另一张新的宿主机物理页上,甚至可以将客户机所拥有的某一客户机物理页所对应的宿主机物理页换出到硬盘上,而客户机仍然以为它访问的客户机物理页是普通的硬件内存资源。只有当它被真正访问时,VMM才将换进的页表再次换入到宿主机内存中。

(2) 影子页表

客户机操作系统所维护的页表负责传统的从客户机虚拟地址GVA到客户机物理地址GPA的转换。如果MMU直接装载客户机操作系统所维护的页表来进行内存访问,那么由于页表中每项所记录的都是GPA,硬件无法正确通过多级页表来进行地址翻译。

针对这个问题,影子页表(Shadow Page Table)是一个有效的解决方法。如果3-1-1所示,一份影子页表与一份客户机操作系统的页表对应,其作的是由GVA直接到HPA的地址翻译。
在这里插入图片描述
图3-1-1 影子页表的作用

(3) MMU虚拟化

在有虚拟化的情况下,如果MMU直接装载客户机操作系统所维护的页表来进行内存访问,那么由于页表中所记录的都是GPA, 硬件无法正确地通过多级页表来进行地址翻译。所以,VMM还需要对MMU实现虚拟化。

客户机操作系统所能看到和操作的都是虚拟MMU,客户机操作系统所维护的页表只是被客户机操作系统载入到虚拟MMU中,不能被物理MMU直接使用。VMM在物理MMU中载入的是影子页表。其原理如图3-1-2所示。
在这里插入图片描述
图3-1-2 影子页表与客户机操作系统页表

在影子页表的实现过程中,影子页表的页表结构并不一定与客户机页表的页表结构完全一致, 比如在64位机上模拟32位机,客户机的页表结构便与宿主机不同,但只要保证相对于同一个虚拟地址,在影子页表中最后的那级页表的页表项,所指向的宿主机物理页是且必须是客户机物理页在客户机物理地址与宿主机物理地址映射表中相对应的宿主机物理页。只有这样,客户机操作系统才能由影子页表访问到它想访问的客户机物理地址。这就是影子页表的基本原理。

(4) 影子页表的建立和维护

影子页表的建立与维护过程交织在一起, 贯穿于VMM针对客户机操作系统修改客户机页表和刷新TLB所做的操作中,主要包括三种VMM对客户机操作系统修改客户机CR3寄存器的截获与处理、VMM对客户机操作系统INVLPG指令的截获与处理,以及VMM对因客户机页表和影子页表不一致而触发的缺页异常的截获与处理。其中最后一种发生概率最高,也最复杂。

(1)缺页异常的分类
我们首先介绍缺页异常的分类,对于不同类型的缺页异常,其处理方式往往也是不同的。常见的缺页异常包括以下三类。

a)、影子页表初始化时的缺页异常。
开始时,VMM中与客户机操作系统所拥有的页表相对应的影子页表是空的,但是影子页表又是载入到物理CR3中真正为物理MMU所利用进行寻址的页表,因此,开始时任何的内存访问操作都会引起缺页异常。如果客户机操作系统为所访问的客户机虚拟地址分配了客户机物理页,即客户机操作系统的当前页表中包含了从这个客户机虚拟地址到已经分配了的某一客户机物理页地址的映射,那么,正是由于影子页表中相应的从客户机虚拟地址到宿主机物理地m址的映 射尚未初始化造成了这种异常的发生。 处理这种异常的过程 也就是完成影子页表初始化的过程。

b)、 VMM将宿主机物理页换出到硬盘上引发的缺页异常。
VMM在客户机操作系统不知情的情况下,将分配给客户机的宿主机物理页换出到硬盘上,那么,虽然客户机操作系统为所访向的客户机虚拟地址分配了客户机物理页,但是由于VMM没在影子页表中为这个客户机虚拟地址建立相应的到宿主机物理地址的映射,便会引发缺页异常。

c)、客户机上的缺页异常。
如果客户机操作系统尚未给这个客户机虚拟地址分配客户机物理页,即相应的客户机操作系统页表中没有这个客户机虚拟地址到某一客户页的映射, 这时也会引发缺页异常。此外,还有客户机所访问的客户页表项存在位( Present Bit)为0,或者写一个只读的客户机物理页,也会引起缺页异常。

(2)影子页表的缺页处理机制
VMM首先截获到缺页异常的发生,并将发生异常的客户机虚拟地址在客户机页表中对应页表项的访问权限位与缺页异常的错误码进行比较,从而检查此缺页异常是否是由客户机本身引起的。对于由客户机本身引起的缺页异常,VMM将直接返回客户机操作系统,再由客户机操作系统的缺页异常处理机制来处理该缺页异常;如果缺页异常不是由客户机引起的,那么它必定是由于客户机页表和影子页表不一致,这样的异常也叫“影子缺页异常"。对于影子缺页异常,VMM会根据客户机页表同步影子页表。同步影子页表的过程如下。

a)、VMM根据客户机页表项建立起相应的影子页目录和页表结构。
以x86架构上最简单的二级页表为例,介绍影子页目录和页表结构的建立过程。当客户机操作系统进人保护模式之前,会为第一个保护模式的进程准备好客户机CR3寄存器的值,我们将该值右移12位后称为客户机页帧号( GuestFrame Number, GFN)。每帧代表一个内存页。VMM在为它建立影子页表时,会根据GFN找到与之相对应的映射在宿主机上的物理页帧号( Machine FrameNumber, MFN)。客户机认为存储在该GFN中的数据实际上是存储在MFN中的。

VMM要从宿主机的物理内存中新分配一个物理页, 该物理页的起始地址右移12位后称为影子宿主机物理页帧号( Shadow Machine Frame Number,SMFN)。VMM将这个新分配的物理页的起始地址载人物理CR3寄存器,指向相应的客户机进程的影子页表。客户机操作系统总会切换进程,当这个进程再次被调度执行时,VMM不需要重新分配新的宿主机物理页,只需要找到以前为它分配的可载入物理CR3寄存器的宿主机物理页(即SMFN )即可。所以,VMM要在GFN、MFN和SMFN之间建立定的联系, 而GFN 和MFN 是一对一的关系,只需建立MFN和SMFN的关系。建立MFN和SMFN之间的关系最常用的算法是HASH表,以MFN的值和SMFN所对应的影子页表的类型type (type通常指在影子页表中是第几级页表,也有其他特殊类型)为键值来索引SMFN, SMFN=hash(MFN,type)。

宿主机物理页已经载入了物理CR3寄存器,对于两级页表来讲,每张影子页表有1024个页表项,每个页表项对应客户机页表相应位置的页表项。客户机页表项的存在位如果为
1 (即客户机物理页存在),则VMM会为相应的影子页表的页表项填人宿主机物理地址。如果该页表项所处的页表不是页表结构中的最后一级页表(即存储的是指向其他页表的指针),那么根据客户机页表项所含物理地址右移12位得到GFN ,将其转换为相应的MFN,若hash(MFN,type)存在,则将hash(MFN,type) (即SMFN)填人该影子页表项的宿主机物理地址;反之,VMM需为其新分配宿主机物理页,并为该宿主机物理页和客户机物理页映射的宿主机物理页在HASH表中建立映射关系,以备下次使用。

b ) 、VMM根据发生缺页异常的客户机虚拟地址,在客户机页表的相应页表项中得到与之对应的客户机物理地址。

c)、根据客户机物理地址,在客户机物理地址与宿主机物理地址映射表中得到相应的宿主机物理地址,VMM再把这个宿主机物理地址填入到影子页表项中。在根据客户机页表项同步影子页表时,除了要建立起相应的影子页表数据结构、填充宿主机物理地址到影子页表的页表项中外,VMM还要根据客户页表项的访问位和修改位设置对应影子页表项的访问位和修改位。

(5) 影子页表总结

影子页表和客户机页表之间并不是时刻同步的,只有在需要的时候才进行同步。影子页表可以看做是客户机页表巨大的TLB,称为虚拟TLB。当客户机操作系统需要访向它的客户机页表时,物理MMU真正访问的是被称为“虚拟TLB”的影子页表。

当客户机页表被修改时,若影子页表中对应该客户机页表的表项访问权限低于客户机页表表项的,VMM会截获一个缺页异常,这可以理解为TLB未命中,它表示尽管客户机页表中所访问的是合法的地址映射,但是影子页表中尚未建立起与之对应的映射,即发生了影子缺页异常。此时,WMM根据客户机页表的客户机虚拟地址到客户机物理地址的映射,在影子页表中建立相应的客户机虚拟地址到宿主机物理地址的映射,并置相应的权限位,就相当于TLB填

充当客户机修改客户机页表的表项时,由于客户机执行敏感指令重写CR3或执行 INVLPG敏感指令刷新TLB,VMM将截获这一操作,并对影子页表进行相应的修改,刷新影子页表(即客户机页表的虚拟TLB)中的全部或部分内容,就相当于TLB刷新。

(6) 影子页表的缺点

影子页表解决了传统的IA-32架构下的内存虚拟化问题,但是它的缺点也比较明显,具体如下。

首先,实现非常复杂,需要考虑各种各样的页表同步情况。

其次,影子页表的内存开销也很大,需要为每个客户机进程对应的页表都维护一个“影子页表”。

为解决这些问题, Intel和AMD都提供了相应的技术,直接在硬件上支持

GVA→GPA→HPA的两次地址转換,大大降低了内存虚拟化的难度,同时也大大提高了内存虚拟化的性能。

3.2 硬件辅助虚拟化

(1) 概述

内存虚拟化的主要任务是实现地址空间的虚拟化,内存虚拟化通过两次地址转换来支持地址空间的虚拟化,即客户机虚拟地址GVA→客户机物理地址GPA→宿主机物理地址HPA的转换。其中,GVA→GPA的转换是由客户机软件决定的,通常是客户机操作系统通过VMCS中客户机状态域CR3指向的页表来指定;GPA→HPA的转换是由VMM决定的,VMM在将物理内存分配给客户机时就确定了GPA→HPA的转换,VMM通常会用内部数据结构来记录这个映射关系。

传统的IA-32架构只支持一次地址转换,即通过CR3指定的页表来实现“虚拟地址”物理地址”的转换。这和内存虚拟化所要求的两次地址转换产生了矛盾。可以通过将两次转换合并为一次转换来解决这个问题,即VMM根据GVA→GPA→HPA的映射关系,计算出GVA→HPA的映射关系,并将其写入“影子页表”。类似于“影子页表”这样的软件方法尽管能够解决问题,但是缺点也很明显。首先是实现非常复杂,例如需要考虑各种各样页表同步情况等,这样导致开发、调试和维护都比较困难。此外,“影子页表”的内存开销也很大,因为需要为每个客户机进程对应的页表都锥护一个“影子页表”。
为了解决这个问题,VT-x提供了 Extended Page Table(EPT)技术,直接在硬件上支持GVA→GPA→HPA的两次地址转换,大大降低了内存虚拟化的难度,也进一步提高了内存虚拟化的性能。

此外,为了进一步提高T1B的使用效率,VT-x还引人了 Virtual Processor ID(VPID)功能,进一步增加了内存虚拟化的性能。

(2) EPT

Intel EPT技术(Extended Page Table,扩展页表)是Intel VT-x提供的内存虚拟化支持技术,其基本原理如图3-2-1所示。在原有的CR3页表地址映射的基础上,EPT引入 了EPT页表来实现另一次映射。这里假设客户机页表和EPT页表都是4级页表,CPU完成一次地址转换的过程如下。
在这里插入图片描述
图3-2-1 EPT原理图

CPU首先查找客户机CR3寄存器指向的L4页表。客户机CR3寄存器给出的是GPA,所以,CPU通过EPT页表将客户机CR3中的GPA转换为HPA;CPU首先査找 EPT TLB,如果没有相应的记录,就进一步查找EPT页表,如果还没有,CPU则抛出 EPT Violation异常交给VMM处理。

获得L4页表地址(指的是HPA)后,CPU根据GVA和L4页表项的内容来获取L3页表项的GPA。如果L4页表中GVA对应的表项显示为“缺页”,那么CPU产生 Page Fault,直接交由客户机操作系统处理。注意这里不会产生VM-Exit。获得L3页表项的GPA后,CPU同样通过查询EPT页表来将L3的GPA→HPA的转换,过程和上面一样。
同理,CPU会依次完成L2、L1页表的査询,获得GVA所对应的GPA,然后进行最后一次查询EPT页表获得HPA。

正如图3-2-1所示,CPU需要5次查询EPT页表,每次查询都需要4次内存访问。这样,在最坏的情况下总共需要20次内存访问。EPT硬件通过增大EPT TLB来尽量减少内存访问。

(3) EPT的硬件支持

为例支持EPT, VT-X规范在VMCS的“VM-Execution控制域”中提供了Enable EPT字段。如果在VM-Entry的时候该位被置上,EPT功能就会被启用,CPU会使用EPT功能进行两次转换。

EPT页表的基础是由VMCS的“VM-Execution控制域”的Extended page table pointer字段来指定的,它包含了EPT页表的宿主机物理地址。

EPT是一个多级页表,每级页表的表项格式是相同的,如图3-2-2所示。
在这里插入图片描述
图3-2-2 EPT页表的表项格式

EPT也变的转换过程和CR3页表的转换过程是类似的,图3-2-3展示了CPU使用EPT页表进行地址转换的过程。
在这里插入图片描述
图3-2-3 EPT页表转换

EPT通过EPT页表中的SP字段支持大小为2MB或者1GB的超级页。图3-2-4给了2MB超级页的地址转换过程。和图3-2-3的不同点在于,当CPU发现SP字段为1时,就停止继续向下遍历页表,而是直接转换了。
在这里插入图片描述
图3-2-4 EPT页表转换:超级页

EPT同样会使用TLB缓冲来加速页表的查找过程。因此,VT-x还提供了一条新的指令 INVEPT,可以使EPT的TLB项失效。这样,当EPT页表有更新时,CPU可以执行INVEPT使旧的TLB失效,使CPU使用新的EPT表项。

和CR3页表会导致 Page Fault一样,使用EPT之后,如果CPU在遍历EPT页表进行GPA→HPA转换时,也会发生异常。
(1)GPA的地址位数大于GAW。
(2)客户机试图读一个不可读的页(R=0)。
(3)客户机试图写一个不可写的页(W=0)。
(4)客户机试图执行一个不可执行的页(X=0)。

发生异常时,CPU会产生VM-Exit,退出原因为 EPT Violation。VMCS的“ VM-Exit信息域”还包括如下信息。
VM-EXIT physical- address information: 引起 EPT Violation的GPA。
VM-EXIT linear-address informationt: 引起 EPT Violation的GVA 。
Qualification: 引起 EPT Violation的原因,如由于读引起、由于写引起等。

(4) EPT的软件使用

要使用EPT,VMM需要做如下事情。

首先需要在VMCS中将EPT功能打开,这个只需要写VMCS相应字段即可。

其次需要设置好EPT的页表。EPT页表反应了GPA→HPA的映射关系。由于是VMM负责给虚拟机分配物理内存,因此,VMM拥有足够的信息来建立EPT页表。此外,如果VMM给虚拟机分配的物理内存足够连续的话,VMM可以在EPT项表中尽量使用超级页,这样有利于提高TLB的性能。

当CPU开始使用EPT时,VMM还需要处理 EPT Violation。通常来说, EPT Violation的源有如下几种。

(1)客户机访问MMIO地址。这种情况下,VMM需要将请求转给I/O虚拟化模块。

(2)EPT表的动态创建。有些VMM采用一开始EPT页表为空,当第一次使用发生 EPT Violation时再建立映射。

由此可以看出,EPT相对于传统的“影子页表”方法・其实现大大地简化了。而且,由于客户机内部的 Page Fault不用发生 VM Exit,也大大减少了VM-Exit的个数,提高了性能。此外,EPT只需要维护一张EPT页表,不像“影子项表”那样需要为每个客户机进程的页表堆护一张影子负表,也减少了内存的开销。

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Luke_Lx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值