Lab 3 练习补充
在练习开始之前 先讲讲 两个数据结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| struct mm_struct { list_entry_t mmap_list; struct vma_struct *mmap_cache; pde_t *pgdir; int map_count; void *sm_priv; }; struct vma_struct { struct mm_struct *vm_mm; uintptr_t vm_start; uintptr_t vm_end; uint32_t vm_flags; list_entry_t list_link; };
|
总而言之就是 mm_struct 描述了整个进程的虚拟地址空间 而 vma_struct 描述了 进程中的一小部分虚拟内存空间
练习0:填写已有实验
本实验依赖实验1/2。请把你做的实验1/2的代码填入本实验中代码中有“LAB1”,“LAB2”的注释相应部分。
1 2
| 就下面三个 复制过去就好 pmm.c default_pmm.c trap.c
|
练习1:给未被映射的地址映射上物理页
完成do_pgfault(mm/vmm.c)函数,给未被映射的地址映射上物理页。设置访问权限 的时候需要参考页面所在 VMA 的权限,同时需要注意映射物理页时需要操作内存控制 结构所指定的页表,而不是内核的页表。注意:在LAB3 EXERCISE 1处填写代码。执行
后,如果通过check_pgfault函数的测试后,会有“check_pgfault() succeeded!”的输出,表示练习1基本正确。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| do_pgfault() ptep = get_pte(mm->pgdir, addr, 1); if (*ptep == 0) { if (pgdir_alloc_page(mm->pgdir, addr, perm) == NULL) { goto failed; } } else { if(swap_init_ok) { struct Page *page = NULL; ret = swap_in(mm, addr, &page); if (ret != 0) { cprintf("swap_in in do_pgfault failed\n"); goto failed; } page_insert(mm->pgdir, page, addr, perm); swap_map_swappable(mm, addr, page, 0); page->pra_vaddr = addr; }
|
- 请描述页目录项(Page Directory Entry)和页表项(Page Table Entry)中组成部分对ucore实现页替换算法的潜在用处。
- AVL CPU 不理会这个属性 可以不管 (有可能在32位系统使用大过 4G内存的时候 用到这几位)
- G Global 全局位 表示是否将虚拟地址与物理地址的转换结果缓存到 TLB 中
- D Dirty 脏页位 当 CPU 对这个页进行写操作时 会置 1
- PAT Page Attribute Table 页属性表位 置 0
- A Accessed 访问位 若为 1 则 说明 CPU 访问过了 CPU 会定时清 0 记录被置 1 的频率 当内存不足时 会将 使用频率较低的页面换出到外存 同时将 P位 置 0 下次访问 该页时 会引起 Pagefault 异常 中断处理程序再将此页换上
- PCD Page-level Cache Disable 页级高速缓存位 置 0 即可 读的时候 高速缓存是否有效 若有效则直接从高速缓存中读出 若无效的话 则必须实实在在的从 I/O 端口去读数据
- PWT Page-level Write-Through 页级通写位 控制是先写到高速缓存里再慢慢回写到内存里 还是 直接慢慢写到内存里
- US User/Superviosr 普通用户/超级用户位
- RW Read/Write 读写位
- P Present 存在位 (虚拟页式存储的关键位 若为 0 则发起缺页异常)
- 如果ucore的缺页服务例程在执行过程中访问内存,出现了页访问异常,请问硬件要做哪些事情?
1 2 3 4 5 6
| 页访问异常 会将产生页访问异常的线性地址存入 cr2 寄存器中 并且给出 错误码 error_code 说明是页访问异常的具体原因 uCore OS 会将其 存入 struct trapframe 中 tf_err 等到中断服务例程 调用页访问异常处理函数(do_pgfault()) 时 再判断 具体原因 若不在某个VMA的地址范围内 或 不满足正确的读写权限 则是非法访问 若在此范围 且 权限也正确 则 认为是 合法访问 只是没有建立虚实对应关系 应分配一页 并修改页表 完成 虚拟地址到 物理地址的映射 刷新 TLB 最后再 调用 iret 重新执行引发页访问异常的 那条指令 若是在外存中 则将其换入 内存 刷新 TLB 然后退出中断服务例程 重新执行引发页访问异常的 那条指令
|
练习2:补充完成基于FIFO的页面替换算法
完成vmm.c中的do_pgfault函数,并且在实现FIFO算法的swap_fifo.c中完成map_swappable和swap_out_victim函数。通过对swap的测试。注意:在LAB3 EXERCISE 2处填写代码。执行
后,如果通过check_swap函数的测试后,会有“check_swap() succeeded!”的输出,表示练习2基本正确。
请在实验报告中简要说明你的设计实现过程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 此时完成的是 FIFO 置换算法 因此 每次换出的都应该是 最先进来的 页 static int _fifo_map_swappable(struct mm_struct *mm, uintptr_t addr, struct Page *page, int swap_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); return 0; } static int _fifo_swap_out_victim(struct mm_struct *mm, struct Page ** ptr_page, int in_tick) { list_entry_t *head=(list_entry_t*) mm->sm_priv; assert(head != NULL); assert(in_tick==0);
list_entry_t *le = head->prev; struct Page* page = le2page(le, pra_page_link); list_del(le); *ptr_page = page; return 0; }
|
请在实验报告中回答如下问题:
- 如果要在ucore上实现”extended clock页替换算法”请给你的设计方案,现有的swap_manager框架是否足以支持在ucore中实现此算法?如果是,请给你的设计方案。如果不是,请给出你的新的扩展和基此扩展的设计方案。并需要回答如下问题
- 需要被换出的页的特征是什么?
- 在ucore中如何判断具有这样特征的页?
- 何时进行换入和换出操作?
1 2 3 4 5 6
| 当然能够支持 首选 页表项的 Dirty Bit 为 0 的页 且 Access Bit 为 0 的页 其次是 访问了但没修改的页 最次是 访问了修改了的页 !(*ptep & PTE_A) && !(*ptep & PTE_D) 没被访问过 也没被修改过 (*ptep & PTE_A) && !(*ptep & PTE_D) 被访问过 但没被修改过 !(*ptep & PTE_A) && (*ptep & PTE_D) 没被访问过 但被修改过 换入是在缺页异常的时候 换出是在物理页帧满的时候
|
至此 这两道题就完成了 比之前的 Lab 要简单多了 这次只花了 一天半时间 下面是 Challenge 花了我一个小时(跑出去饭堂吃饭 哈哈哈哈哈 0.0)
扩展练习 Challenge 1:实现识别dirty bit的 extended clock页替换算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| swap_fifo.c struct swap_manager swap_manager_fifo = { .name = "fifo swap manager", .init = &_fifo_init, .init_mm = &_fifo_init_mm, .tick_event = &_fifo_tick_event, .map_swappable = &_fifo_map_swappable, .set_unswappable = &_fifo_set_unswappable, .swap_out_victim = &_extended_clock_swap_out_victim, .check_swap = &_fifo_check_swap, }; static int _extended_clock_swap_out_victim(struct mm_struct *mm, struct Page ** ptr_page, int in_tick) { list_entry_t *head = (list_entry_t*)mm->sm_priv; assert(head != NULL); assert(in_tick == 0); list_entry_t *le = head->prev; assert(head != le);
int i; for (i = 0; i < 2; i++) {
while (le != head) { struct Page *page = le2page(le, pra_page_link); pte_t *ptep = get_pte(mm->pgdir, page->pra_vaddr, 0);
if (!(*ptep & PTE_A) && !(*ptep & PTE_D)) { list_del(le); *ptr_page = page; return 0; } if (i == 0) { *ptep &= 0xFFFFFFDF; } else if (i == 1) { *ptep &= 0xFFFFFFBF; } le = le->prev; } le = le->prev; } }
|
最后放上 运行结果