前置知识
虚拟存储、页面置换算法、中断机制。
改动点
相比于 lab2
源代码,lab3
主要是添加部分代码以实现虚拟存储功能,仅见的改动点在于页描述结构 page
和 alloc_pages
函数的具体实现,两者源码如下:
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
| struct Page { int ref; uint32_t flags; unsigned int property; list_entry_t page_link; list_entry_t pra_page_link; uintptr_t pra_vaddr; };
struct Page * alloc_pages(size_t n) { struct Page *page=NULL; bool intr_flag; while (1) { local_intr_save(intr_flag); { page = pmm_manager->alloc_pages(n); } local_intr_restore(intr_flag);
if (page != NULL || n > 1 || swap_init_ok == 0) break; extern struct mm_struct *check_mm_struct; swap_out(check_mm_struct, n, 0); } return page; }
|
练习一
此练习用于了解页面替换算法 FIFO 的具体实现。
对于虚拟内存管理而言,另外涉及两大数据结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 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; };
struct mm_struct { list_entry_t mmap_list; struct vma_struct *mmap_cache; pde_t *pgdir; int map_count; void *sm_priv; };
|
为实现 FIFO,我们需要使用 list_entry_t pra_list_head
所指链表来有序 (对于 FIFO 而言,指代页面被分配的时间) 保存某程序所分配的物理页面集 (集内页面使用 Page.pra_page_link
进行链接)。
当分配某程序物理页面后,需要将刚分配的页面放置于链首以显式更新 pra_list_head
链表;当需要选择待替换物理页面时,直接选择链尾页面即可。
被替换的物理页面会被存放至 swap 分区内部 (此称为交换技术)。因为此时 PTE 表项无效,因此 ucore
使用此存放该页面所在位置的起始扇区编号。
因为 ucore
将物理页面线性映射至 swap 对应扇区,因此其交换技术的实现十分简单。而实际系统之中,这种映射实现是比较复杂的。
基于 FIFO 实现思想及上述数据结构,很容易写出相关代码,故不再赘述。
练习二
此练习用于了解并实现缺页中断的异常处理程序。
缺页中断的异常处理程序的具体调用流程如下:__alltraps
–> trap
–> trap_dispatch
–> pgfault_handler
–>do_pgfault
。这里直接基于 do_pgfault
的实现,说明如何处理缺页中断。
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 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| int do_pgfault(struct mm_struct *mm, uint32_t error_code, uintptr_t addr) { int ret = -E_INVAL; struct vma_struct *vma = find_vma(mm, addr);
pgfault_num++; if (vma == NULL || vma->vm_start > addr) { goto failed; } switch (error_code & 3) { default: case 2: if (!(vma->vm_flags & VM_WRITE)) { goto failed; } break; case 1: goto failed; case 0: if (!(vma->vm_flags & (VM_READ | VM_EXEC))) { goto failed; } }
uint32_t perm = PTE_U; if (vma->vm_flags & VM_WRITE) { perm |= PTE_W; } addr = ROUNDDOWN(addr, PGSIZE);
ret = -E_NO_MEM;
pte_t *ptep=NULL; ptep = get_pte(mm->pgdir, addr, 1); if (ptep == NULL) { goto failed; } if (*ptep == 0) { if (pgdir_alloc_page(mm->pgdir, addr, perm) == NULL) { goto failed; } } else { if(swap_init_ok) { struct Page *page=NULL; if ((ret = swap_in(mm, addr, &page)) != 0) { goto failed; } page_insert(mm->pgdir, page, addr, perm); swap_map_swappable(mm, addr, page, 1); page->pra_vaddr = addr; } else { goto failed; } } ret = 0; failed: return ret; }
|
扩展练习一
该练习用于了解并实现 extended clock
页面替换算法。
基于硬件实现以及现有数据结构,该算法比较容易实现。
当分配某程序物理页面后,需要将其放置于 list_entry_t pra_list_head
所示链表的尾部;程序访问过程中,硬件会自动设置 PTE 表项中的相关标志位 (所示页面是否发生修改,所示页面是否访问);当需要选择待替换物理页面时,遍历链表,并动态更新 pra_list_head
指针以及相应页面对应页表项的标志位,如果某页面对应的标志位为 00 (即,自上次至此,尚未被访问,也尚未被修改),则选择此页面进行替换。
扩展练习二
该练习用于了解并实现 LRU
页面替换算法。
当某页面被访问后,似乎无法更新 LRU 所维护的页面链表。