操作系统ucorelab3.docx
- 文档编号:24849618
- 上传时间:2023-06-02
- 格式:DOCX
- 页数:14
- 大小:269.26KB
操作系统ucorelab3.docx
《操作系统ucorelab3.docx》由会员分享,可在线阅读,更多相关《操作系统ucorelab3.docx(14页珍藏版)》请在冰豆网上搜索。
操作系统ucorelab3
操作系统
实验报告
题目:
虚拟内存管理
一、内容
本次实验是在实验二的基础上,借助于页表机制和实验一中涉及的中断异常处理机制,完成PageFault异常处理和FIFO页替换算法的实现,结合磁盘提供的缓存空间,从而能够支持虚存管理,提供一个比实际物理内存空间“更大”的虚拟内存空间给系统使用。
这个实验与实际操作系统中的实现比较起来要简单,不过需要了解实验一和实验二的具体实现。
实际操作系统系统中的虚拟内存管理设计与实现是相当复杂的,涉及到与进程管理系统、文件系统等的交叉访问。
如果大家有余力,可以尝试完成扩展练习,实现extendedclock页替换算法。
练习1:
给未被映射的地址映射上物理页(需要编程)
完成do_pgfault(mm/vmm.c)函数,给未被映射的地址映射上物理页。
设置访问权限的时候需要参考页面所在VMA的权限,同时需要注意映射物理页时需要操作内存控制结构所指定的页表,而不是内核的页表。
注意:
在LAB2EXERCISE1处填写代码。
执行make qemu后,如果通过check_pgfault函数的测试后,会有“check_pgfault()succeeded!
”的输出,表示练习1基本正确。
请在实验报告中简要说明你的设计实现过程。
请回答如下问题:
请描述页目录项(PagDirectorEntry)和页表(PageTableEntry)中组成部分对ucore实现页替换算法的潜在用处。
如果ucore的缺页服务例程在执行过程中访问内存,出现了页访问异常,请问硬件要做哪些事情?
练习2:
补充完成基于FIFO的页面替换算法(需要编程)
完成vmm.c中的do_pgfault函数,并且在实现FIFO算法的swap_fifo.c中完成map_swappable和swap_out_vistim函数。
通过对swap的测试。
注意:
在LAB2EXERCISE2处填写代码。
执行
make qemu
后,如果通过check_swap函数的测试后,会有“check_swap()succeeded!
”的输出,表示练习2基本正确。
请在实验报告中简要说明你的设计实现过程。
请在实验报告中回答如下问题:
如果要在ucore上实现"extendedclock页替换算法"请给你的设计方案,现有的swap_manager框架是否足以支持在ucore中实现此算法?
如果是,请给你的设计方案。
如果不是,请给出你的新的扩展和基此扩展的设计方案。
并需要回答如下问题需要被换出的页的特征是什么?
在ucore中如何判断具有这样特征的页?
何时进行换入和换出操作?
二、目的
了解虚拟内存的PageFault异常处理实现
了解页替换算法在操作系统中的实现
三、实验流程
本次实验主要完成ucore内核对虚拟内存的管理工作。
其总体设计思路还是比较简单,即首先完成初始化虚拟内存管理机制,即需要设置好哪些页需要放在物理内存中,哪些页不需要放在物理内存中,而是可被换出到硬盘上,并涉及完善建立页表映射、页访问异常处理操作等函数实现。
然后就执行一组访存测试,看看我们建立的页表项是否能够正确完成虚实地址映射,是否正确描述了虚拟内存页在物理内存中还是在硬盘上,是否能够正确把虚拟内存页在物理内存和硬盘之间进行传递,是否正确实现了页面替换算法等。
lab3的总体执行流程如下。
首先是初始化过程。
参考ucore总控函数init的代码,可以看到在调用完成虚拟内存初始化的vmm_init函数之前,需要首先调用pmm_init函数完成物理内存的管理,这也是我们lab2已经完成的内容。
接着是执行中断和异常相关的初始化工作,即调用pic_init函数和idt_init函数等,这些工作与lab1的中断异常初始化工作的内容是相同的。
在调用完idt_init函数之后,将进一步调用三个lab3中才有的新函数vmm_init、ide_init和swap_init。
这三个函数设计了本次实验中的两个练习。
第一个函数vmm_init是检查我们的练习1是否正确实现了。
为了表述不在物理内存中的“合法”虚拟页,需要有数据结构来描述这样的页,为此ucore建立了mm_struct和vma_struct数据结构(接下来的小节中有进一步详细描述),假定我们已经描述好了这样的“合法”虚拟页,当ucore访问这些“合法”虚拟页时,会由于没有虚实地址映射而产生页访问异常。
如果我们正确实现了练习1,则do_pgfault函数会申请一个空闲物理页,并建立好虚实映射关系,从而使得这样的“合法”虚拟页有实际的物理页帧对应。
这样练习1就算完成了。
ide_init和swap_init是为练习2准备的。
由于页面置换算法的实现存在对硬盘数据块的读写,所以ide_init就是完成对用于页换入换出的硬盘(简称swap硬盘)的初始化工作。
完成ide_init函数后,ucore就可以对这个swap硬盘进行读写操作了。
swap_init函数首先建立swap_manager,swap_manager是完成页面替换过程的主要功能模块,其中包含了页面置换算法的实现(具体内容可参考5小节)。
然后会进一步调用执行check_swap函数在内核中分配一些页,模拟对这些页的访问,这会产生页访问异常。
如果我们正确实现了练习2,就可通过do_pgfault来调用swap_map_swappable函数来查询这些页的访问情况并间接调用实现页面置换算法的相关函数,把“不常用”的页换出到磁盘上。
四、实验过程与结果分析
(1)给未被映射的地址映射上物理页(需要编程)
具体而言,当启动分页机制以后,如果一条指令或数据的虚拟地址所对应的物理页框不在内存中或者访问的类型有错误(比如写一个只读页或用户态程序访问内核态的数据等),就会发生页错误异常。
产生页面异常的原因主要有:
1、目标页面不存在(页表项全为0,即该线性地址与物理地址尚未建立映射或者已经撤销);
2、相应的物理页面不在内存中(页表项非空,但Present标志位=0,比如在swap分区或磁盘文件上)
3、访问权限不符合(此时页表项P标志=1,比如企图写只读页面).
do_pgfault()函数从CR2寄存器中获取页错误异常的虚拟地址,根据errorcode来查找这个虚拟地址是否在某一个VMA的地址范围内,那么就给它分配一个物理页。
structvma_struct{
//thesetofvmausingthesamePDT
structmm_struct*vm_mm;
uintptr_tvm_start;//startaddrofvma
uintptr_tvm_end;//endaddrofvma
uint32_tvm_flags;//flagsofvma
//linearlistlinkwhichsortedbystartaddrofvma
list_entry_tlist_link;
};
vm_start和vm_end描述的是一个合理的地址空间范围(即严格确保vm_start list_link是一个双向链表,按照从小到大的顺序把一系列用vma_struct表示的虚拟内存空间链接起来,并且还要求这些链起来的vma_struct应该是不相交的,即vma之间的地址空间无交集; vm_flags表示了这个虚拟内存空间的属性,包括 #defineVM_READ0x00000001//只读 #defineVM_WRITE0x00000002//可读写 #defineVM_EXEC0x00000004//可执行 vm_mm是一个指针,指向一个比vma_struct更高的抽象层次的数据结构mm_struct 而这mm_struct包含所有虚拟内存空间的共同属性,如下: structmm_struct{ //linearlistlinkwhichsortedbystartaddrofvma list_entry_tmmap_list; //currentaccessedvma,usedforspeedpurpose structvma_struct*mmap_cache; pde_t*pgdir;//thePDTofthesevma intmap_count;//thecountofthesevma void*sm_priv;//theprivatedataforswapmanager }; mmap_list是双向链表头,链接了所有属于同一页目录表的虚拟内存空间 mmap_cache是指向当前正在使用的虚拟内存空间 pgdir所指向的就是mm_struct数据结构所维护的页表 map_count记录mmap_list里面链接的vma_struct的个数 sm_priv指向用来链接记录页访问情况的链表头 最后实现 if((ptep=get_pte(mm->pgdir,addr,1))==NULL){//目标页面不存在,失败 cprintf("get_pteindo_pgfaultfailed\n"); gotofailed; } if(*ptep==0){//权限不够,也是失败! if(pgdir_alloc_page(mm->pgdir,addr,perm)==NULL){ cprintf("pgdir_alloc_pageindo_pgfaultfailed\n"); gotofailed; } } else{//页表项非空,可以尝试换入页面 if(swap_init_ok){ structPage*page=NULL;//根据mm结构和addr地址,尝试将硬盘中的内容换入至page中 if((ret=swap_in(mm,addr,&page))! =0){ cprintf("swap_inindo_pgfaultfailed\n"); gotofailed; } page_insert(mm->pgdir,page,addr,perm);//建立虚拟地址和物理地址之间的对应关系 swap_map_swappable(mm,addr,page,1);//将此页面设置为可交换的 page->pra_vaddr=addr; } else{ cprintf("noswap_init_okbutptepis%x,failed\n",*ptep); gotofailed; } } 请描述页目录项(PagDirectorEntry)和页表(PageTableEntry)中组成部分对ucore实现页替换算法的潜在用处。 页目录项是指向储存页表的页面的,所以本质上与页表项相同,结构也应该相同.每个页表项的高20位,就是该页表项指向的物理页面的首地址的高20位(当然物理页面首地址的低12位全为零),而每个页表项的低12为,则是一些功能位,可以通过在mmu.h中的一组宏定义发现. #definePTE_P0x001//Present对应物理页面是否存在 #definePTE_W0x002//Writeable对应物理页面是否可写 #definePTE_U0x004//User对应物理页面用户态是否可以访问 #definePTE_PWT0x008//Write-Through对应物理页面在写入时是否写透(即向更低级储存设备写入) #definePTE_PCD0x010//Cache-Disable对应物理页面是否能被放入高速缓存 #definePTE_A0x020//Accessed对应物理页面是否被访问 #definePTE_D0x040//Dirty对应物理页面是否被写入 #definePTE_PS0x080//PageSize对应物理页面的页面大小 #definePTE_MBZ0x180//Bitsmustbezero必须为零的部分 #definePTE_AVAIL0xE00//Availableforsoftwareuse用户可自定义的部分 对于实现页替换算法来说,页目录项(pgdir)作为一个双向链表存储了目前所有的页的物理地址和逻辑地址的对应,即在实内存中的所有页,替换算法中被换出的页从pgdir中选出。 页表(pte)则存储了替换算法中被换入的页的信息,替换后会将其映射到一物理地址。 如果ucore的缺页服务例程在执行过程中访问内存,出现了页访问异常,请问硬件要做哪些事情? 产生页访问异常后,CPU把引起页访问异常的线性地址装到寄存器CR2中,并给出了出错码errorCode,说明了页访问异常的类型。 ucore OS会把这个值保存在structtrapframe中tf_err成员变量中。 而中断服务例程会调用页访问异常处理函数do_pgfault进行具体处理。 实现结果: (2)实现寻找虚拟地址对应的页表项(需要编程) 页错误异常发生时,有可能是因为页面保存在swap区或者磁盘文件上造成的,所以我们需要通过页面分配解决这个问题。 页面替换主要分为两个方面,页面换出和页面换入。 页面换入主要在上述的do_pgfault()函数实现; 页面换出主要在swap_out_vistim()函数实现。 FIFO替换算法会维护一个队列,队列按照页面调用的次序排列,越早被加载到内存的页面会越早被换出。 具体实现的函数如下: 首先是_fifo_map_swappable(),它的主要作用是将最近被用到的页面添加到算法所维护的次序队列。 staticint_fifo_map_swappable(structmm_struct*mm,uintptr_taddr,structPage*page,intswap_in){ list_entry_t*head=(list_entry_t*)mm->sm_priv; list_entry_t*entry=&(page->pra_page_link); assert(entry! =NULL&&head! =NULL); list_add(head,entry);//将最近用到的页面添加到次序队尾 return0; } 然后是_fifo_swap_out_victim()函数是用来查询哪个页面需要被换出,它的主要作用是用来查询哪个页面需要被换出。 staticint _fifo_swap_out_victim(structmm_struct*mm,structPage**ptr_page,intin_tick){ list_entry_t*head=(list_entry_t*)mm->sm_priv; assert(head! =NULL); assert(in_tick==0); list_entry_t*le=head->prev;//用le指示需要被换出的页 assert(head! =le); structPage*p=le2page(le,pra_page_link);//le2page宏可以根据链表元素获得对应的Page指针p list_del(le);//将进来最早的页面从队列中删除 assert(p! =NULL); *ptr_page=p;//将这一页的地址存储在ptr_page中 return0; } 如果要在ucore上实现"extendedclock页替换算法"请给你的设计方案,现有的swap_manager框架是否足以支持在ucore中实现此算法? 本实验的主要设计思路为: 若要完成基于FIFO规则的页面置换算法,首先,我们必须要完善虚拟内存管理函数,保证页面被换出之后,能够再次换入,在本次实验中do_pgfault即为进行页面换入。 其次则是解决FIFO页面置换算法中的两个问题,一是将最近被用到的页面添加到算法所维护的次序队列。 二则是查询哪个页面需要被换出。 这样即可以保证该算法的实现。 在kern/mm/mmu.h文件中有如下定义: /*pagetable/directoryentryflags*/ #definePTE_P0x001//Present #definePTE_W0x002//Writeable #definePTE_U0x004//User #definePTE_PWT0x008//Write-Through #definePTE_PCD0x010//Cache-Disable #definePTE_A0x020//Accessed #definePTE_D0x040//Dirty #definePTE_PS0x080//PageSize #definePTE_MBZ0x180//Bitsmustbezero #definePTE_AVAIL0xE00//Availableforsoftwareuse //ThePTE_AVAILbitsaren'tusedbythekernelorinterpretedbythe //hardware,souserprocessesareallowedtosetthemarbitrarily. #definePTE_USER(PTE_U|PTE_W|PTE_P) 其中PTE_A中的内容的即标志着该页是否被访问过,由此我们可以实现extendedclock算法。 实现方法 对kern/mm/swap_fifo.c做相应的修改,判断是否被访问过即可: staticint _fifo_swap_out_victim(structmm_struct*mm,structPage**ptr_page,intin_tick) { list_entry_t*head=(list_entry_t*)mm->sm_priv; assert(head! =NULL); assert(in_tick==0); list_entry_t*le=head->next; assert(head! =le); while(le! =head) { structPage*p=le2page(le,pra_page_link); pte_t*ptep=get_pte(mm->pgdir,p->pra_vaddr,0); if(! (*ptep&PTE_A)) {//未被访问 list_del(le); assert(p! =NULL); *ptr_page=p; return0; } *ptep^=PTE_A; le=le->next; } le=le->next; while(le! =head) { structPage*p=le2page(le,pra_page_link); pte_t*ptep=get_pte(mm->pgdir,p->pra_vaddr,0); list_del(le); assert(p! =NULL); *ptr_page=p; return0; } } 如果是,请给你的设计方案。 如果不是,请给出你的新的扩展和基此扩展的设计方案。 并需要回答如下问题 需要被换出的页的特征是什么? 最早被换入,且最近没有被访问过的页。 在ucore中如何判断具有这样特征的页? 首先判断其最近有没有被访问过(利用条件*ptep&PTE_A进行判断),若无,则按照FIFO原则进行置换。 何时进行换入和换出操作? 当需要调用的页不在页表中时,并且在页表已满的情况下,需要进行换入和换出操作。 实现结果: 五、实验体会和思考题 本次实验让我了解了物理内存管理。 了解页表创建/查找的管理过程、切换过程。 充分认识到了页表分级的重要性,对于页表的应用有了全面的认识。 实验过程相对顺利,通过查阅资料收获了很多。 学习的一些知识如下: 关键数据结构和相关函数分析 对于第一个问题的出现,在于实验二中有关内存的数据结构和相关操作都是直接针对实际存在的资源--物理内存空间的管理,没有从一般应用程序对内存的“需求”考虑,即需要有相关的数据结构和操作来体现一般应用程序对虚拟内存的“需求”。 一般应用程序的对虚拟内存的“需求”与物理内存空间的“供给”没有直接的对应关系,ucore是通过pagefault异常处理来间接完成这二者之间的衔接。 page_fault函数不知道哪些是“合法”的虚拟页,原因是ucore还缺少一定的数据结构来描述这种不在物理内存中的“合法”虚拟页。 为此ucore通过建立mm_struct和vma_struct数据结构,描述了ucore模拟应用程序运行所需的合法内存空间。 当访问内存产生pagefault异常时,可获得访问的内存的方式(读或写)以及具体的虚拟内存地址,这样ucore就可以查询此地址,看是否属于vma_struct数据结构中描述的合法地址范围中,如果在,则可根据具体情况进行请求调页/页换入换出处理(这就是练习2涉及的部分);如果不在,则报错。 mm_struct和vma_struct数据结构结合页表表示虚拟地址空间和物理地址空间的示意图如下所示: structvma_struct{ //thesetofvmausingthesamePDT structmm_struct*vm_mm; uintptr_tvm_start;//startaddrofvma uintptr_tvm_end;//endaddrofvma uint32_tvm_flags;//flagsofvma //linearlistlinkwhichsortedbystartaddrofvma list_entry_tlist_link; }; 在ucore中描述应用程序对虚拟内存“需求”的数据结构是vma_struct(定义在vmm.h中),以及针对vma_struct的函数操作。 这里把一个vma_struct结构的变量简称为vma变量。 vma_struct的定义如下: #defineVM_READ0x00000001//只读 #defineVM_WRITE0x00000002//可读写 #defineVM_EXEC0x00000004//可执行 vm_start和vm_end描述了一个连续地址的虚拟内存空间的起始位置和结束位置,这两个值都应该是PGSIZE对齐的
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 操作系统 ucorelab3