物理内存管理实验报告练习0:合并lab1和lab2书上提示使用“diff/merge”工具来合并lab1和lab2的代码,可是没有找到这款工具,但是被推荐使用meld工具,也能很方便地将不同目录的文件异同比较出来,可以一一手动合并,删除,增加代码,避免了不必要的错误。这部分主要合并的文件有kdebug.c、trap.c。练习1:实现firstfit连续物理内存分配算法。完成合并代码的工作之后,makeqemu执行lab2,结果出现错误提示:提示default_pmm.c的第283行出现错误,打开文件看,发现这句话出现在函数staticvoiddefault_check(void)中,这是一个检查函数,并且提示不要修改。当然,为了调试工作,在检查函数中加一些代码还是可以的。例如通过cprintf输出一些调试信息,除此之外,还发现check函数中使用大量assert函数,大概作用是当参数条件不为1的时候就弹出debugminitor。也可以用来调试作用。起初,我仔细看了basic_check函数,它的作用只是做了一些简单的分配释放的操作,并且也没出错,后面看了default_check函数之后也没找到问题所在。再然后是重点分析default_alloc_pages和default_free_pages函数,结合list_add函数看了许久才发现它的空闲块插入顺序有问题:每次插入都是从free_list的头部插入,事实上,应该保持free_list的顺序,地址小的空闲块应该放在前面,地址大的空闲块应该放在后面,以便firstfit算法的从头快速查找。找到问题后大致明白了这个exercise的目标:这个练习主要就是完善default_alloc_pages和default_free_pages。关键变量:#definefree_list(free_area.free_list)//空闲块的链表,但是不指向具体页#definenr_free(free_area.nr_free)//空闲块的个数关键函数:list_init(&free_list);//初始化空闲块链表SetPageProperty(base);ClearPageProperty(base);关键宏:le2page(le,page_link);//由链表指针得到对应页的地址(一)Allocpages:用firstfit算法寻找空闲块list_entry_t*le=&free_list;while((le=list_next(le))!=&free_list){structPage*p=le2page(le,page_link);if(p-property=n){page=p;break;}}(二)Allocpages:删除空闲块,若有剩余则把剩余的部分插入空闲块链表if(page!=NULL){list_del(&(page-page_link));if(page-propertyn){structPage*p=page+n;p-property=page-property-n;//list_add(&free_list,&(p-page_link));//Excise1:MyCodelist_add(page-page_link.prev,&(p-page_link));/////////应该要插在链表合适的位置}nr_free-=n;ClearPageProperty(page);}(三)Freepages:删除指定块相邻的空闲块,合并成大空闲块while(le!=&free_list){p=le2page(le,page_link);le=list_next(le);if(base+base-property==p){base-property+=p-property;ClearPageProperty(p);list_del(&(p-page_link));}elseif(p+p-property==base){p-property+=base-property;ClearPageProperty(base);base=p;list_del(&(p-page_link));}}(四)Freepages:把大空闲块假如空闲块链表//Excise1:MyCode把新的大空闲块插入free_listle=list_next(&free_list);if(le==&free_list)//假如之前的删除操作刚好把空闲块链表清空list_add(&free_list,&(base-page_link));else{//找合适的位置把大空闲块入free_listwhile(le!=&free_list){p=le2page(le,page_link);if(pbase){list_add_before(&(p-page_link),&(base-page_link));break;}le=list_next(le);}if(le==&free_list)//假如找不到比base序号大的页,则放在链表尾部list_add_after(free_list.prev,&(base-page_link));}}练习2、实现寻找虚拟地址对应的页表项关键的函数以及宏函数:*PDX(la)=返回虚拟地址la的页目录索引*KADDR(pa):返回物理地址pa相关的内核虚拟地址*set_page_ref(page,1):设置此页被引用了一次*page2pa(page):得到page管理的那一页的物理地址*structPage*alloc_page():分配一页出来*memset(void*s,charc,size_tn):设置s指向地址的前面n个字节为字节‘c’.pde_t*pdep=&pgdir[PDX(la)];//得到页目录项if(!(*pdep&PTE_P)){//假如页目录项不存在structPage*page;if(!create||(page=alloc_page())==NULL){//假如不需要分配页或者分配页失败returnNULL;}set_page_ref(page,1);//设置该页被引用了一次uintptr_tpa=page2pa(page);//得到该页物理地址memset(KADDR(pa),0,PGSIZE);//物理地址转虚拟地址,然后把该页初始化*pdep=pa|PTE_U|PTE_W|PTE_P;//设置可读,可写,存在位}return&((pte_t*)KADDR(PDE_ADDR(*pdep)))[PTX(la)];//KADDR(PDE_ADDR(*pdep)):这部分是由页目录项地址得到关联的页表物理地址,再转成虚拟地址//PTX(la):返回虚拟地址la的页表项索引//最后返回的是虚拟地址la对应的页表项入口的地址练习3、remove_pte()函数的完善*structPage*pagepte2page(*ptep):得到页表项对应的那一页*free_page:释放一页*page_ref_dec(page):减少该页的引用次数,返回剩下引用此时*tlb_invalidate(pde_t*pgdir,uintptr_tla):当修改的页表是进程正在使用的那些页表,使TLB的那一页无效if(*ptep&PTE_P){//假如页表项存在structPage*page=pte2page(*ptep);//找到页表项的那一页的信息if(page_ref_dec(page)==0){//假如这一页没有被引用了free_page(page);//释放该页}*ptep=0;//该页目录项清零tlb_invalidate(pgdir,la);//当修改的页表是进程正在使用的那些页表,使TLB的那一页无效}