好了代码已经呼之欲出了
cprintf("npages_basemem is %d\n",npages_basemem); cprintf("pages addr is %0x8 \n", pages); size_t i; //all low memeory is free expect 0 page for (i = 1; i < npages_basemem; i++) { cprintf("pages addr is %0x8 \n", pages[i]); pages[i].pp_ref = 0; pages[i].pp_link = page_free_list; page_free_list = &pages[i]; } i = PADDR(boot_alloc(0)) / PGSIZE; for (; i < npages; i++) { pages[i].pp_ref = 0; pages[i].pp_link = page_free_list; page_free_list = &pages[i]; } } 1.4 实现page_free这个就比较简单了。只需要简单的把要释放的页加入到free_page_list中就好。
根据实验中给出的提示,可以很容易的写出来
void page_free(struct PageInfo *pp) { // Fill this function in // Hint: You may want to panic if pp->pp_ref is nonzero or // pp->pp_link is not NULL. if (pp->pp_ref || pp->pp_link) { panic("no shoule page free"); } //head insert pp -> pp_link = page_free_list; page_free_list = pp; } 2. Part2: Virtual Memory跳过中间的一些废话,直接开始exercise4。这里要求我们编写代码来管理页面表。要插入和删除linear-to-physical(其实就是虚拟地址和物理地址之间的mappings,并按需分配页。
这个是JOS所用的32位虚拟地址的分布
下面就是虚拟地址的翻译过程。这个学过os的应该非常熟悉了吧。这里的page_dir和page_table其实就是一个二级页表。的多级索引非常简单。
整体过程就是我们先通过CR3寄存器找到PAGE_DIR所在的位置,然后通过虚拟地址的前10位在PAGE_DIR中获取到下一级页表,也就是PAGE_TABLE的地址。随后通过虚拟地址的12-21这10位去找到对应的PAGE_FRAME的地址。从里面获取到ppa的地址结合OFFSET就可以得到最终的物理地址了。
好了搞清楚大概逻辑之后,下面开始完成第二部分的代码
2.1 pgdir_walk这个代码是后面四个代码的基础,因此一定要小心认真,这里的意思就是说给你pgdir的地址。和虚拟地址va你要返回一个指向pte的指针。pte就是页表条目在最后一层页表对应位置处。
首先我们通过几个宏定义把虚拟地址分解
如果page_dir对应的PTE_P也就是有效位为0的话则表明相关的页表条目并不存在
如果不存在的话需要通过create标记来判断是否需要创建对应的页
这里根据提示我们需要把新创建页的物理地址存储在对应的page_dir位置处
然后就是在page_table中找到对应的PTE返回
pte_t * pgdir_walk(pde_t *pgdir, const void *va, int create) { // Fill this function in uintptr_t dir = PDX(va); //表示对应的page_dir索引 uintptr_t page = PTX(va); // 表示对应的page_table索引 uintptr_t offset = PGOFF(va); // 表示在page中对应的页内偏移 pde_t dir_entry = pgdir[dir]; // 首先要判断这个虚拟地址是否有映射 if (!(dir_entry & PTE_P)) { if (create) { // allocate struct PageInfo *newPage = page_alloc(ALLOC_ZERO); if (newPage == NULL) { // allocation failed return NULL; } newPage->pp_ref++; pgdir[dir] = (pde_t)page2pa(newPage)|PTE_P|PTE_U|PTE_W; } else { return NULL; } } // pte_t *ptab = (pte_t *)KADDR(PTE_ADDR(pgdir[dir])); return &ptab[page]; } 2.2 boot_map_region这个函数的实现就比较简单了。要求是把虚拟地址[va, va+size)映射到物理地址[pa, pa+size],使用权限位为perm|PTE_P,只是在UTOP上方做静态映射,所以不能修改pp_ref字段.
基本上就是通过上面实现的pgdir_walk函数找到给定虚拟地址对应的pte。然后修改pte条目即可
static void boot_map_region(pde_t *pgdir, uintptr_t va, size_t size, physaddr_t pa, int perm) { // Fill this function in uintptr_t start = 0; for ( ; start < size; start += PGSIZE, va += PGSIZE, pa += PGSIZE) { pte_t *pte = pgdir_walk(pgdir,(void *) va, 1); *pte = pa | perm | PTE_P; } } 2.3 Page_lookup返回一个虚拟地址va映射的页面。如果pte_store不是0,那么将对应的页表项地址存到pte_store的地址里(用于结果返回)。如果没有页面映射在va那么返回NULL。提示:使用pgdir_walk和pa2page
struct PageInfo * pgee_lookup(pde_t *pgdir, void *va, pte_t **pte_store) { // Fill this function in pte_t *pte = pgdir_walk(pgdir,(void *) va, 0); if (!pte) { return NULL; } if (*pte && !(*pte & PTE_P)) { return NULL; } if (pte_store) { *pte_store = pte; } struct PageInfo* page = pa2page(PTE_ADDR(*pte)); return page; }基本上通过给的提示就可以实现这个函数
2.4 Page_remove也是通过提示。移除给定的va对应的映射。
如果给定的va在页表中没有对应映射则直接返回。