Xen memory.docx
- 文档编号:5197054
- 上传时间:2022-12-13
- 格式:DOCX
- 页数:13
- 大小:229.01KB
Xen memory.docx
《Xen memory.docx》由会员分享,可在线阅读,更多相关《Xen memory.docx(13页珍藏版)》请在冰豆网上搜索。
Xenmemory
Xen虚拟化技术之内存管理
概述
Xen的内存管理和Linux的内存管理基本上相同,特别在分段和分页机制上没有什么变化。
本文先简单介绍Linux内存管理技术,然后在此基础上讲述Xen的内存管理技术。
从内容上可以看出两者在内存管理上的一些差异。
1、X8632位体系结构的分段和分页机制
X8632体系结构的内存管理分为两部分:
分段和分页。
分段机制能够隔离代码段,数据段,堆栈段,所以多个任务能够运行在同一个处理器上而不相互干扰。
分页机制提供了一个按需调页的虚拟内存系统,一个程序的执行环境被按需的映射到物理内存。
分页机制也能用来提供多个任务之间的隔离。
当运行于保护模式时分段机制的使用是强制的,而分页机制的使用却是可选的。
分段和分页机制可以配合使用来支持单任务系统,多任务系统或使用共享内存的多处理器系统。
1.1分段机制
如下图所示,分段机制能够将处理器可寻址的内存空间(亦称线性地址空间)分割为更小的受保护的地址空间,这些受保护的地址空间称为段。
段可以持有(hold)一个程序的代码,数据和栈,也可以持有系统数据结构(如任务状态段,TaskStateSegment,TSS或LDT,LocalDescriptorTable)。
如果系统中有多个任务同时运行,每个任务可以分配自己的段。
处理器要强制实施段间的边界并保证一个任务不能通过向其它任务的段中写内容而干扰其运行。
分段机制也允许规定段类型,这样某些段操作就只能限制在某种段上。
系统中所有的段都被包含在处理器的线性地址空间中。
要定位段中的一个字节必须要使用逻辑地址。
一个逻辑地址包含一个段选择符和段内偏移。
段选择符是对一个段的唯一标识,它提供了描述符表中的偏移(如全局描述符表,GDT)通过这个偏移可以得到一个段描述符。
每个段都有一个段描述符,描述了段的大小,访问权限,优先级,段的类型和基地址。
逻辑地址中的偏移部分加上段基址就可以定位段中的一个字节。
段基址和段内偏移之和形成了处理器线性地址空间的一个线性地址。
1.2分页机制
分页支持一个虚拟内存环境,一个大的线性地址空间用一个较小的物理内存和一些磁盘存储来模拟。
使用分页时,每个段被划分成很多页(通常每页为4KB)这些页被存储在物理内存或磁盘上。
操作系统维护一个页目录和很多页表来保持对这些页的跟踪。
当一个任务试图访问线性地址空间中的某个地址时,处理器使用页目录和页表将线性地址转换成物理地址,然后再执行相应的操作。
如果访问的页不在物理内存,则处理器通过产生一个页错误异常中断任务的执行。
操作系统然后从磁盘将页读入物理内存并继续执行任务。
下面这个经典的线性地址转换图,表示了将线性地址映射到页时的(大小为4KB)页目录和页表的层次关系,页目录中的条目(entry)指向页表,页表中的entry指向物理内存中的页。
2、Linux内存管理子系统
上面介绍了i386CPU在硬件层次上对内存管理所提供的支持。
内存管理最终的实现要由软件完成。
i386CPU中的页式存储管理的基本思路是:
通过页面目录和页面表分两个层次实现从线性地址到物理地址的映射。
Linux内核的设计要考虑到在各种不同CPU上的实现,还要考虑到在64位38CPU(如Alpha)上的实现,所以不能仅仅针对i386结构来设计它的映射机制而要以一种假想的、虚拟的CPU和MMU(内存管理单元)为基础,设计出一种通用的模型,再把它分别落实到各种具体的CPU上。
因此,Linux内核的映射机制设计成三层,在页面目录和页面表中间增设了一层“中间目录”。
在代码中页面目录称为PGD,中间目录称为PMD,而页面表则称为PT。
PT中的表项则称为PTE(PageTableEntry)。
PGD,PMD和PT三者均为数组。
相应地,在逻辑上也把线性地址从高位到地位划分成4个位段,分别用作在目录PGD中的下标、中间目录PMD中的下标、页面表中的下标以及物理页面内的位移。
这样,对线性地址地映射就分成如下图所示。
具体一点说,对于CPU发出的线性地址,虚拟的Linux内存管理单元分如下四步完成从线性地址到物理地址的映射:
(1)用线性地址中最高的那一个位段作为下标在PGD中找到相应的表项,该表项指向相应的中间目录PMD。
(2)用线性地址中的第二个位段作为下标在此PMD中找到相应的表项,该表项指向相应页面表。
(3)用线性地址中的第三个位段作为下标在页面表中找到相应的表项PTE,该表项中存放的就是指向物理页面的指针。
(4)线性地址中的最后位段为物理页面内的相对位移量,将此位移量与目标物理页面的起始地址相加便得到相应的物理地址。
3、XenVMM核心模块
3.1Xen的内存
Xen为自己的内核和堆分配了12MB的物理内存,并保留了高端64MB的虚拟地址供自己使用,机器地址(MA)和虚拟地址(VA)的映射如图所示
Xen的64MB保留地址可被分成上图所示的七个部分:
(1)RO_MPT(0xF0000000-0xFC3FFFFF):
这4MB的虚拟地址空间被映射到机器地址到物理地址的转换数组,该区域的权限被设成只读,但它可以被GuestOS所访问。
(2)DIRECT_MAP(0xFC400000-0xFEBFFFFF):
这40MB的虚拟地址空间是用于Xen的直接地址映射。
该区域可以被分成以下三个子区域:
XENHEAP:
该区域的默认大小是12MB,它包括Xen的内核,位图以及供使用的堆。
RDWR_MPT:
这块区域也被映射为机器地址到物理地址的转换数组。
它与RO_MPT的区别是该虚拟区域是映射成可读写的,但不能被GuestOS访问。
FRAMETABLE:
该区域映射到frame_table数组,这个数组用于描述所有的机器内存页面。
(3)LINEAR_PT(0xFEC00000-0xFEFFFFFF):
这4MB的虚拟地址空间映射当前工作的页目录和页表。
通过该区域,Xen可以很容易地访问GuestOS的PDE和PTE。
(4)SH_LINEAR_PT(0xFF000000-0xFF3FFFFF):
这4MB的虚拟地址空间用于映射影子页目录和页表。
它与LINEAR_PT类似。
(5)PERDOMAIN(0xFF400000-0xFF7FFFFF):
该虚拟地址空间用于每个Domain的地址映射。
(6)MAPCACHE(0xFF800000-0xFFBFFFFF):
该虚拟地址空间用于Domain的页表映射。
(7)IOREMAP(0xFF000000-0xFFFFFFFF):
该虚拟地址空间用于系统调用ioremap()。
在Xen初始化内存之后,空闲的内存空间将被切分成两块,一块用于Xen,一块用于各个Domain。
在程序中是用宏来定义的:
#defineMEMZONE_XEN0
#defineMEMZONE_DOM1
#defineNR_ZONES2
当GuestOS启动时,Guest所使用的内存是从供Domain使用的那块内存中分配出来的,这块内存也被称为Domain堆。
3.2客户操作系统物理内存
在每个GuestOS启动之前,Xen将为其保留固定数量的机器内存。
下面几个变量用于跟踪机器内存使用。
(1)frame_table
frame_table[]是一个注册所有机器内存页框使用的数组,数组元素是pfn_inf结构。
该数组位于Xen堆,被映射为DIRECTMAP区的FRAMETABLE。
structpage_info
{
structlist_headlist;//每个机器页框被链接到一个双向链表
union{
struct{
u32_domain;//如果这个机器页框被分配,拥有它的domai
………
}__attribute__((packed))inuse;
struct{
u32order;//如果这个机器页框没有被分配,则它会被链接到某个空闲队列
………
}__attribute__((packed))free;
}u;
union{
u32tlbflush_timestamp;//“TLBClock”的时间戳,用于避免多余的TLBflush
unsignedlongshadow_flags;//用于具有相同shadowpage的guestpage
};
};
(2)machine_to_phys_mapping
machine_to_phys_mapping[]是一个映射机器地址到物理地址的数组。
该数组索引是机器页框号,数组内容对应的是客户操作系统物理页框号。
该数组位于Xen堆,除了被映射到DIRECTMAP区的RDWR_MPT,还可映射到RO_MPT
(3)phys_to_machine_mapping
phys_to_machine_mapping[]是一个映射物理地址到机器地址的数组。
该数组索引是客户操作系统物理页框号,数组内容是机器页框号。
该数组位于每个Domain的堆中,每个客户操作系统有自己的phys_to_machine_mapping[]数组。
除了Domain0是由Xen启动,所有其它客户操作系统都是由Domain0的Xend进程启动。
Xend通过hypercalls为客户操作系统建立初始内存环境,这些hypercalls包括do_dom0_op(),do_mmu_update()。
内存初始化工作主要由两部分构成:
第一部分工作由xc_domain_create()(tools/libxc/xc_domain.c)完成。
该函数中
domain初始化内存大小传递给Hypervisor,Hypervisor调用函数alloc_new_dom_mem()从domain堆中按页分配内存,分配好的页被链接到domain.page_list。
第二部分工作由xc_linux_build()(tools/libxc/xc_domain.c)完成,它主要完成
下面工作:
(1)为页目录分配页框;
(2)初始化页表;
(3)填写phys->machine和machine->phys表;
(4)Hypervisor向该页提供正确保护;
(5)启动共享信息页;44
(6)向Hypervisor发送页更新请求。
客户操作系统初始化后的内存布局如上图所示,从0xC0000000到0xC0400000这4MB虚拟内存空间被映射到一个临时客户页表,客户操作系统启动时使用该临时页表。
函数xc_linux_build()最重要的工作就是建立物理地址到机器地址和机器地址到物理地址之间的地址映射。
为了建立物理地址到机器地址之间的映射,Xen首先分配一个临时数组page_array[],该数组通过do_dom0_op()进行初始化;然后拷贝所有的数据到vphysmap_start,vphysmap_start是数组phys_to_machine_mapping[]客户操作系统虚拟地址。
在拷贝之前,Xen必须重新映射vphysmap_start。
3.3Xen虚拟MMU设计
Xen内存管理模块最重要的工作就是将客户操作系统虚拟地址转换成机器地址,并保证客户操作系统正确访问内存。
Xen提供两种地址转换方法直接模式和影子模式。
(1)直接模式
直接模式意味着客户操作系统使用自己的页表直接访问机器内存。
在该模式中,页表访问操作和TLB操作需要重写。
这些操作使用hypercalls陷入到Hypervisor,Hypervisor帮助客户操作系统完成特权级MMU操作。
set_pte()-----xen_l1_entry_update()
set_pmd()-----xen_l2_entry_update()
load_cr3()----xen_pt_switch()
__flush_tlb()----xen_tlb_flush()
__flush_tlb_single()----xen_invlpg()
由于PTE/PDE中是机器地址,因此在写入PTE/PDE之前,物理地址必须转换成机器地址。
同样,从PTE/PDE读出之后,机器地址需要翻译成物理地址。
_pte(x)/_pmd(x)----phys_to_machine(x)
pte_val(x)/pmd_val(x)----machine_to_phys(x)
(2)影子模式
除了使用直接模式外,Xen也使用影子页表实现MMU虚拟化。
Xen影子页表工作原理如下图所示。
客户操作系统维护自身页表,Xen建立影子页表保存每个客户操作系统页表,影子页目录物理地址保存在arch_vcpu.shadow_table。
如果客户操作系统加载自己的页目录地址到CR3,实际上是影子页目录机器地址被加载到CR3。
当客户操作系统第一次访问自己的PTEs指向的内存框时,将产生一个页面错误PF,Xen会调用shadow_fault()。
shadow_fault()将客户操作系统相应PTEs拷贝到影子页表。
由于客户操作系统已经完成物理地址到机器地址的转换,因此客户操作系统PTE使用机器地址,不需要进行地址转换。
具体操作如下所示:
A、取得客户操作系统PTE,检查PRESENT位和RW位;
B、处理写错误:
根据客户操作系统PTE,为影子PTE设置内存页框号、RW位、DIRTY位和ACCESSED位,客户PTE的DIRTY位和ACCESSE位也要设置;
C、处理读错误:
根据客户操作系统PTE,为影子PTE设置内存页框号和ACCESSED位,客户PTE的ACCESSED位也要设置。
如果客户PT的DIRTY位没有设置,清除影子PTE的RW位;如果页表没有被影子化,则分配影子页表页框,并设置相应PTE。
在页表建立影子后,更新页目录。
由于Xen每个客户操作系统都有影子页表,因此其数目很多。
Xen使用哈希表管理这些影子页表,共有SHADOW_HASH_BUCKETS=251条哈希链表,同一条哈希链表中的元素具有相同的哈希值,根据客户操作系统页表的页框号和shadow_type(第几级映射),由函数sh_hash()计算出这个页所对应的影子页表所在的链表。
3.4基于VT的MMU全虚拟化
由于VT机器不支持硬件内存虚拟化,所以我们使用传统的IA-32软件虚拟化方法以向未修改源代码的客户操作系统提供完全内存虚拟化环境。
该方法基本思想是VMM维持系统页表层次结构,该结构有效地缓存客户操作系统地址转换,因此地址转换实际靠该系统页表层次控制,该套由VMM维护的页表被称为影子页表。
在该机制下,Guest上的软件可以自由地修改Guest上的页表而不会触发陷阱陷入VMM中,另外,VMM中的影子页表也有可能并不总是与Guest中的页表一致。
这里存在两种情况:
A、Guest的页表所给予的权限比影子页表给予的权限大:
此时,若某访问操作被Guest的页表运行而被影子页表禁止,则会产生错误于是,VMM将会取得控制权并适当地更新影子页表。
B、Guest的页表所给予的权限比影子页表给予的权限小:
它发生在Guest上的软件修改了页表项并降低了其访问权限,例如,使其PRESENT位为0。
由于老的转换仍存在于TLB中,所以Guest将会执行INVLP指令或重新加载CR3来清空TLB。
这两个操作都会陷入VMM中,然后VMM就可以从影子页表中移除过时的页表项。
可以发现,其基本想法与前文中的影子模式很类似。
但在影子模式中,Xen的实现仍然需要修改Guest操作系统的内核,所以需要对其重新设计以利用VT的硬件支持来运行未经修改的客户操作系统。
在影子模式中,Xen仅仅支持泛虚拟化,仍然允许在Guest操作系统的内核中调用hypercall和修改其页表。
这些对于完全虚拟化来说都是不可行的。
下面就是前文所述的影子模式中针对全虚拟化来说所存在的一些问题:
(1)高端的64MB虚拟地址空间:
Xen将每个Guest中64MB的高端地址空间保
留给自己使用。
在普通Linux中,高64MB地址空间被固定映射区使用,因此它会与Xen虚拟地址空间冲突。
另一个与该地址空间相关的问题是线性页表:
linear_pg_table[]和shadow_linear_pg_table[],因为它们定义在客户操作系统高64MB空间中。
(2)hypercalls:
很显然未修改内核的Linux是不能使用hypercalls的,即使这些操作可以在支持VT的平台上触发VMExit。
(3)物理地址到机器地址的转换:
前面介绍过的影子页表模式,所有客户操作系统PTE/PDE读写操作需要物理地址到机器地址的转换,并且机器地址保存在客户操作系统页表中。
在普通Linux中页表保存物理地址,物理地址到机器地址的转换只能在Hypervisor中完成。
为了支持MMU的全虚拟化,需要在现有的影子模式上做一些改进。
如下图所示
给出了修改后影子模式的处理流程。
(1)初始化映射
客户操作系统内存空间由Xend初始化,Xend实际使用hypercalls分配内存空间。
内存分配过程与前面介绍相同,只是物理地址到机器地址的转换需要做修改。
当Xend为客户操作系统初始化内存时,建立数组machine_to_phys_mapping和phys_to_machine_mapping[]。
machine_to_phys_mapping[]数组由Hypervisor维护,因此在全虚拟化下仍可以使用。
但phys_to_machine_mapping[]之前由Guest维护,所以现在在没有修改GuestOS的情况下就不能再用了,但在全虚拟化下仍然需要物理地址到机器地址的转换,因此把phys_to_machine_mapping[]交由Hypervisor实现,从Xen堆分配页框,它位于Xen的虚拟地址空间。
(2)Hypervisor页表
前面提到,Xen位于每个客户操作系统高64MB虚拟地址空间。
而这和全虚拟化是冲突的。
为了解决这一问题,当VMExit发生时Xen必须使用自己的页表层次结构。
VMExit控制域的LoadCR3位必须设置为1,Xen的CR3必须加载到VMCS的host-state域。
当VMExit发生时必须映射客户操作系统页表框和影子页表框,从而使得Hypervisor可以访问客户操作系统PTEs和影子PTEs。
前面提到Xen使用linear_pg_table[]和shadow_linear_pg_table[]数组来访问客户的PTEs和影子PTEs。
如果影子页目录的机器地址被映射到SH_LINEAR_PT,数组shadow_linear_pg_table[]仍可以用来访问影子PTEs。
但是数组linear_pg_table却无法使用,因为其储存的客户页目录项指向的是Guest的物理地址而不是机器地址。
因此Hypervisor创建一个客户页目录缓存,使得能够使用linear_pg_table数组访问客户操作系统PTEs。
该缓存保存页表机器地址而不是物理地址,因此可以将这些页表直接映射到Hypervisor的LINEAR_PT,从而Linear_pg_table可以正常使用。
Hypervisor页表工作原理如下图所示。
(3)VMExit的处理
在完全虚拟化的情况下,Guest无法使用hypercalls,因此对于Guest来说进入Hypervisor的方法就是VMExit。
共有三类VMExit事件与MMU虚拟化相关页错误为了触发页错误VMExit,VM执行控制域中异常位图的第14比特位需要置成1。
该异常通常都是由于Guest的页表所给予的权限比影子页表给予的权限大造成的。
这在前文已有叙述。
在影子模式中,该异常是由函数shadow_fault()处理的。
全虚拟化条件下,该函数仍可用来处理页异常。
但需要注意的一点是,在未修改的Guest系统中,GuestPTE中页的基地址是物理地址,所以在更新影子PTE之前,需要将物理地址转换成机器地址。
数组phys_to_machine_mapping
仍可使用,但该数组现在由Hypervisor维护。
INVLPG在VM执行控制域中设置INVLPG退出位,则在Guest中执行INVLPG指令时会导致VMExit。
通常,INVLPG指令是在PTE的PRESENT位被标记0后执行。
当该指令被Hypervisor截获时,影子页表中相应的PTE的PRESENT位也应置为0。
CR3切换在VM执行控制域中设置CR3-load和CR3-store退出位,则当Guest对CR寄存器做任何操作时都会导致VMExit。
在Xen的泛虚拟化模式中已经通过调用hypercall实现了CR3切换的部分工作,例如,将客户页目录的基地址保存到domain.mm.pagetable,如果影子页目录不存在就重新建立,将影子页目录基地址加载到CR3寄存器等。
这些工作都可以移植到VMExit处理例程,CR3切换还包括根据客户操作系统PTEs更新影子PTEs。
除此之外,还需要清除guest_pgd_cache,并在物理地址转换成机器地址后拷贝客户页目录PDE到guest_pgd_cache。
(4)MMIO的处理
MMIO是一种特殊的内存访问方式,虚拟MMIO操作也有其特殊之处。
当建立好MMIO映射之后,Guest的页表就会被更新,而此时影子页表还不会被更新,因此第一次MMIO操作将会触发页错误。
这一过程与通常的内存操作一样它的特殊之处在于其特殊的处理方法。
在更新影子页表之前,系统会检查GuestPTE中存储的物理地址。
若发现其物理地址属于MMIO地址空间,则影子页表无需更新,但需要对引发该页错误的指令的解码。
在解码之后,就会产生一个I/O请求,而且根据其物理地址调用相应的处理函数。
如果处理例程返回值,表明这是一次读操作,否则是一次写操作。
接着GuestOS的EIP被修改,使其指向下一条指令,然后重新进入Guest的上下文中。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Xen memory
![提示](https://static.bdocx.com/images/bang_tan.gif)