Lab 3 练习补充
在练习开始之前 先讲讲 两个数据结构
| 12
 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 描述了 进程中的一小部分虚拟内存空间
![mm_vma]()
练习0:填写已有实验
本实验依赖实验1/2。请把你做的实验1/2的代码填入本实验中代码中有“LAB1”,“LAB2”的注释相应部分。
| 12
 
 | 就下面三个 复制过去就好pmm.c default_pmm.c trap.c
 
 | 
练习1:给未被映射的地址映射上物理页
完成do_pgfault(mm/vmm.c)函数,给未被映射的地址映射上物理页。设置访问权限 的时候需要参考页面所在 VMA 的权限,同时需要注意映射物理页时需要操作内存控制 结构所指定的页表,而不是内核的页表。注意:在LAB3 EXERCISE 1处填写代码。执行
后,如果通过check_pgfault函数的测试后,会有“check_pgfault() succeeded!”的输出,表示练习1基本正确。
| 12
 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 则发起缺页异常)
![pagetable_entries]()
- 如果ucore的缺页服务例程在执行过程中访问内存,出现了页访问异常,请问硬件要做哪些事情?
| 12
 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基本正确。
请在实验报告中简要说明你的设计实现过程。
| 12
 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中如何判断具有这样特征的页?
- 何时进行换入和换出操作?
 
| 12
 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页替换算法
| 12
 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.cstruct 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;
 }
 }
 
 | 
最后放上 运行结果
![lab3_finish]()