操作系统lab2实验报告


实验2:物理内存管理


实验目的:

  • 理解基于段页式内存地址的转换机制
  • 理解页表的建立和使用方法
  • 理解物理内存的管理方法

实验内容:

本次实验包含三个部分。首先了解如何发现系统中的物理内存;然后了解如何建立对物理内存的初步管理,即了解连续物理内存管理;最后了解页表相关的操作,即如何建立页表来实现虚拟内存到物理内存之间的映射,对段页式内存管理机制有一个比较全面的了解。

练习:


练习0:填写已有实验

本实验依赖实验1。请把你做的实验1的代码填入本实验中代码中有“LAB1”的注释相应部分。提示:可采用diff和patch工具进行半自动的合并(merge),也可用一些图形化的比较/merge工具来手动合并,比如meld,eclipse中的diff/merge工具,understand中的diff/merge工具等。

meld是一个非常好用并且快捷的文件合并工具,我们这里在Ubuntu下直接用meld就可以把lab1的代码复制到lab2里面了。

打开终端输入meld就可以看到meld的界面了,于是我们选择将lab1和lab2这两个文件目录进行对比
操作系统lab2实验报告_第1张图片点击compare,进入对比界面后,找到有lab1 step1的的kdebug.c文件和lab1 step2 step3的trap.c文件,将文件从左边复制到右边。这样一来,lab1中的代码就自动复制到了lab2中。
操作系统lab2实验报告_第2张图片操作系统lab2实验报告_第3张图片

练习1:实现firstfit连续物理内存分配算法

提示:在实现first fit 内存分配算法的回收函数时,要考虑地址连续的空闲块之间的合并操作。提示:在建立空闲页块链表时,需要按照空闲页块起始地址来排序,形成一个有序的链表。可能会修改default_pmm.c中的default_init,default_init_memmap,default_alloc_pages, default_free_pages等相关函数。请仔细查看和理解default_pmm.c中的注释。

根据提示,我们知道firstfit的分配算法的实现都是在default_pmm.c中的,我们需要做的就是完善和修改default_pmm.c中的每一个相关函数(根据代码的注释)

接下来我们需要了解firstfit算法的原理是什么:

firstfit算法是一种简单高效的分配算法:

物理内存页管理器顺着双向链表进行搜索空闲内存区域,直到找到一个足够大的空闲区域,因为 它尽可能少地搜索链表。如果空闲区域的大小和申请分配的大小正好一样,则把这个空闲区域分配出去,成功返回;否则将该空闲区分为两部分,一部分区域与申请分配的大小相等,把它分配出去,剩下的一部分区域形成新的空闲区。其释放内存的设计思路很简单,只需把这块区域重新放回双向链表中即可。

这个算法的缺点就是会在低地址区留下很多非常小的难以分配的空闲区,增加查找的开销。

接下来我们来看看几个有关的关键数据结构:

首先就是物理页数据结构Page,这个数据结构的定义我们可以在memlayout.h中看到,截图如下:

操作系统lab2实验报告_第4张图片该结构四个成员变量意义如下:

1、ref表示这样页被页表的引用记数,应该就是映射此物理页的虚拟页个数。一旦某页表中有一个页表项设置了虚拟页到这个Page管理的物理页的映射关系,就会把Page的ref加一。反之,若是解除,那就减一。
2、 flags表示此物理页的状态标记,有两个标志位,第一个表示是否被保留,如果被保留了则设为1(比如内核代码占用的空间)。第二个表示此页是否是free的。如果设置为1,表示这页是free的,可以被分配;如果设置为0,表示这页已经被分配出去了,不能被再二次分配。
3、property用来记录某连续内存空闲块的大小,这里需要注意的是用到此成员变量的这个Page一定是连续内存块的开始地址(第一页的地址)。
4、page_link是便于把多个连续内存空闲块链接在一起的双向链表指针,连续内存空闲块利用这个页的成员变量page_link来链接比它地址小和大的其他连续内存空闲块

然后就是管理所有连续的空闲内存空间块的数据结构free_area_t,这个数据结构的定义也在memlayout中可以找到

操作系统lab2实验报告_第5张图片
free_list是一个list_entry结构的双向链表指针
nr_free则记录当前空闲页的个数

接下来我们根据提示来对default_pmm.c中的相关函数进行修改

1.default_init()

在这里插入图片描述根据提示,我们暂时可以不用对default_init()函数进行修改,可以直接使用,代码如下:
在这里插入图片描述这个函数的功能是对free_area_t的双向链表和空闲块的数目进行初始化

2.default_init_memmap()

这个函数是用来初始化空闲页链表的,初始化每一个空闲页,然后计算空闲页的总数

具体的过程就是​先传入物理页基地址和物理页的个数(个数必须大于0),然后对每一块物理页进行设置:先判断是否为保留页,如果不是,则进行下一步。将标志位清0,连续空页个数清0,然后将标志位设置为1,将引用此物理页的虚拟页的个数清0。然后再加入空闲链表。最后计算空闲页的个数,修改物理基地址页的property的个数为n

根据英文注释,修改后代码如下:

操作系统lab2实验报告_第6张图片3.default_alloc_pages()

这个函数的主要功能就是主要就是从空闲页块的链表中去遍历,找到第一块大小大于n的块,然后分配出来,把它从空闲页链表中除去,然后如果有多余的,把分完剩下的部分再次加入会空闲页链表中即可

具体过程如下:

首先判断空闲页的大小是否大于所需的页块大小。

如果需要分配的页面数量n,已经大于了空闲页的数量,那么直接return NULL分配失败。

过了这一个检查之后,遍历整个空闲链表。如果找到合适的空闲页,即p->property >= n(从该页开始,连续的空闲页数量大于n),即可认为可分配,重新设置标志位。具体操作是调用SetPageReserved(pp)和ClearPageProperty(pp),设置当前页面预留,以及清空该页面的连续空闲页面数量值。

然后从空闲链表,即free_area_t中,记录空闲页的链表,删除此项。

如果当前空闲页的大小大于所需大小。则分割页块。具体操作就是,刚刚分配了n个页,如果分配完了,还有连续的空间,则在最后分配的那个页的下一个页(未分配),更新它的连续空闲页值。如果正好合适,则不进行操作。

最后计算剩余空闲页个数并返回分配的页块地址。

详细代码如下:

操作系统lab2实验报告_第7张图片操作系统lab2实验报告_第8张图片4.default_free_pages()

default_free_pages函数的主要功能是完成的是对于页的释放操作,首先有一个assert语句断言这个基地址所在的页是否为预留,如果不是预留页,那么说明它已经是free状态,无法再次free,也就是之前所述,只有处在占用的页,才能有free操作。

接着,声明一个页p,p遍历一遍整个物理空间,直到遍历到base所在位置停止,开始释放操作。

找到了这个基地址之后呢,就可以将空闲页重新加进来(之前在分配的时候,删除了),之后就是一系列与初始化空闲页一样的设置标记位操作了。

接着,如果插入基地址附近的高地址或低地址可以合并,那么需要更新相应的连续空闲页数量,向高合并和向低合并。

详细代码如下:
操作系统lab2实验报告_第9张图片操作系统lab2实验报告_第10张图片操作系统lab2实验报告_第11张图片思考:firstfit算法能否有进一步改进的空间

回答:有改进空间,目前算法的分配和释放的复杂度都是o(n)的,效率十分低,可以采用深度优先搜索DFS算法将效率提高为o(logn)

练习2:实现寻找虚拟地址对应的页表项

这里我们需要实现的是get_pte函数,函数找到一个虚地址对应的二级页表项的内核虚地址,如果此二级页表项不存在,则分配一个包含此项的二级页表。

由于我们已经具有了一个物理内存页管理器default_pmm_manager,我们就可以用它来获得所需的空闲物理页。
在二级页表结构中,页目录表占4KB空间,ucore就可通过default_pmm_manager的default_alloc_pages函数获得一个空闲物理页,这个页的起始物理地址就是页目录表的起始地址。同理,ucore也通过这种方式获得各个页表所需的空间。页表的空间大小取决与页表要管理的物理页数n,一个页表项(32位,即4字节)可管理一个物理页,页表需要占 n/1024
个物理页空间(向上取整)。这样页目录表和页表所占的总大小约为 4096+4∗n 字节。
根据LAZY,这里我们并没有一开始就存在所有的二级页表,而是等到需要的时候再添加对应的二级页表。当建立从一级页表到二级页表的映射时,需要注意设置控制位。这里应该设置同时设置 上PTE_U、PTE_W 和 PTE_P(定义可在mm/mmu.h)。如果原来就有二级页表,或者新建立了页表,则只需返回对应项的地址即可。
如果 create参数为 0,则get_pte返回NULL;如果 create参数不为 0,则 get_pte 需要申请一个新的物理页
然后根据注释一步步操作,代码展示如下:
操作系统lab2实验报告_第12张图片pde_t 全称为page directory entry,也就是一级页表的表项
pte_t 全称为page table entry,表示二级页表的表项。
uintptr_t 表示为线性地址,由于段式管理只做直接映射,所以它也是逻辑地址。
PTE_U: 位3,表示用户态的软件可以读取对应地址的物理内存页内容
PTE_W: 位2,表示物理内存页内容可写
PTE_P: 位1,表示物理内存页存在

  • 请描述页目录项(Pag Director Entry)和页表(Page Table Entry)中每个组成部分的含义和以及对ucore而言的潜在用处:

页目录项是指向储存页表的页面的, 所以本质上与页表项相同, 结构也应该相同. 每个页表项的高20位, 就是该页表项指向的物理页面的首地址的高20位(当然物理页面首地址的低12位全为零), 而每个页表项的低12为, 则是一些功能位, 可以通过在mmu.h中的一组宏定义发现。
对于实现页替换算法来说,页目录项(pgdir)作为一个双向链表存储了目前所有的页的物理地址和逻辑地址的对应,即在实内存中的所有页,替换算法中被换出的页从pgdir中选出。页表(pte)则存储了替换算法中被换入的页的信息,替换后会将其映射到一物理地址。

  • 如果ucore执行过程中访问内存,出现了页访问异常,请问硬件要做哪些事情?

产生页访问异常后,CPU把引起页访问异常的线性地址装到寄存器CR2中,并给出了出错码errorCode,说明了页访问异常的类型。ucore OS会把这个值保存在struct trapframe 中tf_err成员变量中。而中断服务例程会调用页访问异常处理函数do_pgfault进行具体处理。

练习3:释放某虚地址所在的页并取消对应二级页表项的映射

当释放一个包含某虚地址的物理内存页时,需要让对应此物理内存页的管理数据结构Page做相关的清除处理,使得此物理内存页成为空闲;另外还需把表示虚地址与物理地址对应关系的二级页表项清除。请仔细查看和理解page_remove_pte函数中的注释。为此,需要补全在 kern/mm/pmm.c中的page_remove_pte函数。page_remove_pte函数的调用关系图如下所示:

在这里插入图片描述
这里主要是page_remove_pte的补全及完善。
思路主要就是先判断该页被引用的次数,如果只被引用了一次,那么直接释放掉这页, 否则就删掉二级页表的该表项,即该页的入口
代码如下:
操作系统lab2实验报告_第13张图片
具体流程是这样的:
判断页表中该表项是否存在
判断是否只被引用了一次
如果只被引用了一次,那么可以释放掉此页
如果被多次引用,则不能释放此页,只用释放二级页表的表项
更新页表

  • 数据结构Page的全局变量(其实是一个数组)的每一项与页表中的页目录项和页表项有无对应关系?如果有,其对应关系是啥?

有关系。页目录项保存的物理页面地址(即某个页表)以及页表项保存的物理页面地址都对应于Page数组中的某一页。

  • 如果希望虚拟地址与物理地址相等,则需要如何修改lab2,完成此事? 鼓励通过编程来具体完成这个问题

将物理地址和虚地址之前的偏移量即映射关系改为0就可以实现。

运行结果

执行make qemu 后结果:
操作系统lab2实验报告_第14张图片
执行make grade
操作系统lab2实验报告_第15张图片

实验心得

这次实验是在lab1的基础上进行的,没有lab1的代码,lab2的编译是无法通过的,meld工具的重要性就体现出来了,通过用meld可以直接将lab1的代码复制到lab2中,十分便捷,并且提高了效率。
物理内存管理,主要是从物理内存分配和建立页表两个方面来设计实验的,首先要了解如何发现系统中的物理内存,然后了解如何对物理内存进行初步的管理,最后关于了解页表的相关操作还是有一定难度的,很多的编程代码都需要提示才能完成,并且要参考许多相关代码,对参考答案进行分析,这样才能深入理解实验内涵,通过这次实验也能深入理解段页式内存管理机制,为原理课打下基础。

你可能感兴趣的:(操作系统lab2实验报告)