做完实验二后,大家可以了解并掌握物理内存管理中的连续空间分配算法的具体实现以及如何建立二级页表。本次实验是在实验二的基础上,借助于页表机制和实验一中涉及的中断异常处理机制,完成Page Fault异常处理和FIFO页替换算法的实现。实验原理最大的区别是在设计了如何在磁盘上缓存内存页,从而能够支持虚存管理,提供一个比实际物理内存空间“更大”的虚拟内存空间给系统使用。
和lab2一样,依旧使用Ubuntu下的meld工具来进行文件拷贝,在此不再多余赘述。
在程序的执行过程中由于某种原因导致CPU无法最终访问到相应的物理内存单元,即无法完成从虚拟地址到物理地址的映射,这时CPU会产生一次页错误异常,从而需要进行相应的页错误异常服务例程。当相关处理完成后,将返回产生异常的指令处重新执行,才能使得软件正常运行。
当出现上面情况之一,就会产生页面page fault(#PF)异常。产生异常的线性地址存储在CR2中,并且将是page fault的产生类型保存在 error code 中。
因此此函数是完成页错误异常处理的主要函数,他根据从CPU的控制寄存器CR2中获取的页错误异常的虚拟地址,以及根据error code的错误类型来查找次虚拟地址是否在某个VMA的地址范围内,并且是否满足正确的读写权限,如果在此范围内并且权限也正确,就认为这是一次合法访问,但没有建立虚实对应关系,所以需要分配一个空闲的内存页,并修改页表完成虚地址到物理地址的映射,刷新TLB,然后调用iret中断,返回并重新执行。如果该虚地址不在某VMA范围内,这认为是一个非法访问。(这里把vma_struct结构的变量简称为VMA变量。)
page_fault函数不知道哪些是“合法”的虚拟页,原因是ucore还缺少一定的数据结构来描述这种不在物理内存中的“合法”虚拟页。为此ucore通过建立mm_struct和vma_struct数据结构,描述了ucore模拟应用程序运行所需的合法内存空间。当访问内存产生page fault异常时,可获得访问的内存的方式(读或写)以及具体的虚拟内存地址,这样ucore就可以查询此地址,看是否属于vma_struct数据结构中描述的合法地址范围中,如果在,则可根据具体情况进行请求调页/页换入换出处理;如果不在,则报错。
虚拟地址空间和物理地址空间的示意图
vma_struct:
vma_struct述应用程序对虚拟内存“需求”,以及针对vma_struct的函数操作。里把一个vma_struct结构的变量简称为VMA变量。
VMA是描述应用程序对虚拟内存需求的变量。vm_start和vm_end描述的是一个合理的地址空间范围,即严格确保 vm_start < vm_end的关系,list_link是一个双向链表,按照从小到大的顺序把一系列用vma_struct表示的虚拟内存空间链接起来,并且还要求这些链起来的vma_struct应该是不相交的,即vma之间的地址空间无交集。
vm_flags表示了这个虚拟内存空间的属性,目前的属性包括
而vm_mm是一个指向接下来要介绍的mm_struct数据结构
mm_struct
mmap_list是双向链表头,链接了所有属于同一页目录表的虚拟内存空间,mmap_cache是指向当前正在使用的虚拟内存空间,由于操作系统执行的局部性原理,当前正在用到的虚拟内存空间在接下来的操作中可能还会用到,这时就不需要查链表,而是直接使用此指针就可找到下一次要用到的虚拟内存空间。pgdir 所指向的就是 mm_struct数据结构所维护的页表。通过访问pgdir可以查找某虚拟地址对应的页表项是否存在以及页表项的属性等。map_count记录mmap_list 里面链接的 vma_struct的个数。sm_priv指向用来链接记录页访问情况的链表头,这建立了mm_struct和后续要讲到的swap_manager之间的联系。
首先查找页目录,如果不存在则失败
如果物理地址不存在,那么分配一个物理页 并且与虚拟内存建立对应关系,失败则退出
请描述页目录项(Pag Director Entry)和页表(Page Table Entry)中组成部分对ucore实现页替换算法的潜在用处。
页目录项(Page Director Entry):使用双向链表存储了目前所有的页的物理地址和逻辑地址的对应,即在物理内存中的所有页。替换算法中被换出的页从pgdir中选出。
页表(Page Table Entry)存储了替换算法中被换入的页的信息,替换后会将其映射到一物理地址。页表项中的dirty bit和访问位可以帮助实现一些页替换算法(比如extended clock)。
如果ucore的缺页服务例程在执行过程中访问内存,出现了页访问异常,请问硬件要做哪些事情?
发生异常,将产生异常的地址保存在CR2寄存器中,同时还要将EFLAGS,CS,EIP,ErrorCode等保存在内核栈上。ErrorCode设为页访问异常的编码,即0xE
CPU将页访问异常的中断服务例程的地址加载到CS和EIP中,并开始执行中断服务程序。
OS:OS首先将寄存器保存在内核栈中,接下来按照trap->trap_dispatch–>pgfault_handler–>do_pgfault的调用关系来执行中断服务程序。
OS:在do_pgfault里,OS可以获得ErrorCode,并根据ErrorCode为页访问异常,OS将执行上面补充的代码段,并完成中断服务例程。
页错误异常发生时,有可能是因为页面保存在swap区或者磁盘文件上造成的,所以我们需要通过页面分配解决这个问题。 页面替换主要分为两个方面,页面换出和页面换入。
页面换入主要在上述的do_pgfault()函数实现;
页面换出主要在swap_out_vistim()函数实现。
完善练习1中do_pgfault中的代码:
如果页表项非空,尝试换入页面,接着根据mm结构和addr地址,尝试将硬盘中的内容换入至page中,建立虚拟地址和物理地址之间的对应关系 ,再将此页面设置为可交换的 。
在换入时,需要先检查产生访问异常的地址是否属于某个vma表示的合法虚拟地址,并且保存在硬盘的swap文件中(对应的PTE的高24位不为0)。如果满足以上亮点,则执行swap_in() 函数换入页面。
_fifo_map_swappable()函数的主要作用是将最近被用到的页面添加到算法所维护的次序队列。
_fifo_swap_out_victim()函数是用来查询哪个页面需要被换出,它的主要作用是用来查询哪个页面需要被换出。
换出则相对简单,当申请空闲页面时,alloc_pages()函数不能获得空闲页,则需要调用swap_out()函数换出不常用的页面。
如果要在ucore上实现"extended clock页替换算法"请给你的设计方案,现有的swap_manager框架是否足以支持在ucore中实现此算法?如果是,请给你的设计方案。如果不是,请给出你的新的扩展和基此扩展的设计方案。并需要回答如下问题
在本次实验中所涉及到的知识点有:
虚拟内存管理的基本概念与原理;
page fault异常的处理流程;
页替换算法;
物理内存的管理;
对应到的操作系统中的知识点分别有:
操作系统中虚拟内存机制的实现;
操作系统中对具体某一个页面替换算法的实现;
操作系统中的中断处理机制;
它们之间的关系为,前者的知识点为后者中操作系统中的具体实现提供了理论基础(比如说页替换算法就为OS中具体实现swap out的机制提供了基础)
1.整个实验下来感觉自己收获很多,通过上网查找资料,自己深刻学习了虚拟内存管理的机制,通过实验更是加深了印象。
2.实验中遇到了很多的问题,比如进程虚拟内存的管理涉及到的一些结构体的关系,由于种类比较多很容易混乱,通过列图梳理了他们之间的关系。
3.操作系统这门课程比较基础,涉及到的内容比较多。有时学起来会比较吃力,但这门课程非常重要,是以后专业课的基石,希望在以后自己留给更多的时间在做实验上,从而更加深入的理解、掌握操作系统。