kvm内存管理之获取guest内存分布
一 KVM内存管理机制(在此先只讲qemu如何让KVM获取非root的客户机的内存分布:事实上还没有与物理上形成映射,只是在qemu进程的线性空间中开辟一块区域)
KVM不改变GUEST OS,而操作系统对内存的基本认识包括:
1. 物理地址从0开始;(eg, 0~4G)
2. 存在连续性;(4KB为一页,按页组织)
同一个host中,要满足多个guest os的对内存的需求,就需要内存的虚拟化。 如多台guestOS均分配的是4G内存,而所有VM的OS认为自己虚拟地址就是0~4G ,总页帧数为1024*1024,即客户机认为内存是独占的,而VMM则负责管理所有的物理内存,和VM对实际物理内存的占用。
OS分页机制中,利用页表来完成虚拟地址到物理的转换:
其中CR3记录了基地址,不同的进程基地址不同,因此,虽然不同进程可见的都是同样比如4GB的地址空间,同样的逻辑地址,但使用过程中映射到的是不同的物理地址,这个过程由CR3完成。
TLB位于页表的 cache中,如果CPU访问某个线性地址(byte级别)时,如果其所在页面映射存在于TLB中,则不用再查找页表,直接将PFN+偏移地址即可。
对客户机操作系统不作修改,意味着VM也有一套同样的分布的机制和MMU实现,有GVA和GPA的概念和自己的虚拟(机)页表。而GVA到实际host的HPA过程由影子列表来完成。
TLB是CPU相关的全局量,由当前的进程来更新,当进程来切换时,TLB失效,随着进程的访问来补充相应的buffer。
GUEST OS(VM)对内存相关的操作:
1. 修改GUEST页表;
2. 修改GUEST页表的TLB。
客户机要维护的只有自己的页表和对应的TLB,影子列表由VMM来维护,缺页page fault是重要的一个方面,主要有以下几种:
A. VM的Guest OS根本就没有分配虚拟机物理地址,即VPA不存在;(客户机虚拟机管理内存的问题)
B. GUEST OS中页表正常存在,可查询到VPA,但向HPA转换时要查询 shadow page时发现没有,即影子列表没有相应项(影子页表没有同步或还没有初始化);
C. 相应的表项完整过,但将host对应的内存页换出到硬盘上,这一操作会被影子页表捕捉并更新,所以guest os查自己的虚拟页表可得到GPA,但影子页表中没有。
针对缺页异常,首先是检查GUESTOS中的虚拟页表上的访问权限位,与缺页异常的错误码比较,来确定来源(GUEST orVMM),检查过程由VMM来执行(当然VMM首先需要捕捉这个异常),又会出现两种情况:
A. 异常由客户机引起,如GPA没有分配,VMM直接返回GUEST OS。
B. 由VMM引起,意味着shadow page需要更新。
客户机内存区域的初始化:(事实上每次只设置一个memslot,多次完成整个内存区域的映射)
1. kvm_vm_ioctl_set_memory_region:检查32slot上限
2. __kvm_set_memory_region:
1) 参数检查;
2) 更新slot的base_gfn和npages及flags(管理区编号)
3) (情况一)维护两套memslot(最开始是完全一样的,每次更新new),释放在新的memslot里却不在旧的memslot中的rmap指针 、dirty_bitmap及 lpage 信息。(注:kvm_memory_slot实际记录的是GVA到HVA的地址区间映射包含了对应此映射区间的起始客户机页帧号GFN,映射的内存页数目以及起始宿主机虚拟地址,rmap存储了page的相关信息,指向页描述符)
4) (情况二)如果新旧的两套memslotf的页表数相同且均不为空,则检查各个 slot间的重叠的发生,有重叠则直接释放内存。(注:3、4的处理原则依据是memslot的大小不能变,且不同的memslot之间不能重叠)(注:意味着实际位置是可变的)
5) Flags指示脏页的bitmap,如果无效则释放(bitmap仍存在,只清空了指示针)
6) (分支)(npages不为空,mem->memory_size不为0)则为各个page指针分配相应的空间(vmalloc给new.rmap),存储指向页描述符的指针。如果npages为空,则直接跑到第 8步;
7) X86中使用了大页的概念(两个level,分别是2M和1G),计算大页相关的信息,(包括没有对齐直接禁止大页对于slot的支持),大页在此处只分配了大页管理的lpage_info的管理信息(包括rmap_pde和write_count)。
8) 有脏页产生且new 的脏页还没生成,则生成脏页的位图(初始化时生成),并标记 flush shadow=1,用于后面刷新影子页表。
9) 如果参数npage==0,即有新的页面需要set 则分配新的memslot,并初始化,而且需要设置memslot为不可用(不可用原因?页表同步?),并刷新到影子页表中,再释放掉old_memslot的内存。将new.rmap / new.dirty_bitmap/new.lpage_info置NULL
10) Npage!=0时,用new去更新slots,用将新的slots赋值给kvm->memslots。
11) 调用kvm_arch_commit_memory_region:若arch的n_requested_mmu_pages不为0,则计算kvm所需要的page数,如果实际使用值 超过计算值,则释放kvm->arch.active_mmu_pages.prev,循环直至page达标,移除写权限。
12) 释放临时变量old_memslots的内存;
13) 更新影子页表。
KVM_GET_DIRTY_LOG 控制字:
返回用户态提供的dirty_log所属的memslot的脏页位图,并在更新kvm中对应位置(清空为0)
KVM_SET_MEMORY_REGION控制字:
事实上KVM_SET_MEMORY_REION与KVM_SET_USER_MEMORY_REGION过程完全相同,区别主要在两点:
1. 用户态传入的数据类型:没有指明usr_space,此项为空;
2. User_alloc值不同,set_user_memory_region中为1,而此处为0。区别在于是否添加反射映射。
KVM_SET_NR_MMU_PAGES控制字:
改变分配给VM的mmu 的页面数目
1、如果当前内存的活动页超过需要设定的pages数目,则需要释放一些内存页;
2、释放过程:从当前的进程的内存中释放一个page(prev),再循环执行检查释放。
释放过程由kvm_mmu_zap_page()来完成功能:
-------将sp回收,涉及到的是sp的子页面或页表信息的更新 sp 的parent的页表的更新
-------unaccount相应的影子页表并释放对应的page
-------将各个虚拟CPU的vcpu->arch.last_pte_updated置为空
3、更新kvm->arch.n_max_mmu_pages为设定值。
4、更新kvm->arch.n_requested_mmu_pages为设定值。
KVM_GET_NR_MMU_PAGES控制字:
获取returnkvm->arch.n_max_mmu_pages
kvm_mmu_page_role 中定义了多级页表中的级数,包括guest和影子页表。
虚拟机每一次运行,执行vcpu_enter_guest这个函数,都会执行一次r = kvm_mmu_reload(vcpu); 实际上是执行kvm_mmu_load。
kvm_mmu_load的过程详解:
1、KVM预先分配vcpu的arch 中的mmu的pte ramp page header等重要数据结构的内存;
2、判断vcpu的活动内存,不够则释放(至少需要5个page);
3、调用mmu_alloc_roots分配mmu的root_hpa(pgd全局页目录地址)。
4、在mmu_alloc_roots进一步调用kvm_mmu_get_page,并建立影子页的hash映射,生成sp的数据结构,并更新空闲页数目;对页表设置写保护。
事实上mmu_load /unload才是内存管理最为复杂的地方,下次集中再写。
版权声明:本文为博主原创文章,未经博主允许不得转载。
页:
[1]