一 KVM内存管理机制
(在此先只讲qemu如何让KVM获取非root的客户机的内存分布:事实上还没有与物理上形成映射,只是在qemu进程的线性空间中开辟一块区域)
KVM不改变GUEST OS,而操作系统对内存的基本认识包括:
1. 物理地址从0开始;(eg, 0~4G)
2. 存在连续性;(4KB为一页,按页组织)
同一个host中,要满足多个guest os的对内存的需求,就需要内存的虚拟化。 如多台guest OS均分配的是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 or VMM),检查过程由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、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网 享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com