JOS2-5
对函数page_remove()与函数page_lookup()的疑问
// Return the page mapped at virtual address 'va'. // If pte_store is not zero, then we store in it the address // of the pte for this page. This is used by page_remove and // can be used to verify page permissions for syscall arguments, // but should not be used by most callers. // // Return NULL if there is no page mapped at va. // // Hint: the TA solution uses pgdir_walk and pa2page. // struct PageInfo * page_lookup(pde_t *pgdir, void *va, pte_t **pte_store) { pte_t * entry = pgdir_walk(pgdir, va, false); if (pte_store) *pte_store = entry; if (!entry) return NULL; return pa2page(PTE_ADDR(*entry)); }
// Unmaps the physical page at virtual address 'va'. // If there is no physical page at that address, silently does nothing. // // Details: // - The ref count on the physical page should decrement. // - The physical page should be freed if the refcount reaches 0. // - The pg table entry corresponding to 'va' should be set to 0. // (if such a PTE exists) // - The TLB must be invalidated if you remove an entry from // the page table. // // Hint: The TA solution is implemented using page_lookup, // tlb_invalidate, and page_decref. // void page_remove(pde_t *pgdir, void *va) { struct PageInfo *page; pte_t * entry; page = page_lookup(pgdir, va, &entry); if (!(page && (*entry & PTE_P))) return; page_decref(page); *entry = 0; tlb_invalidate(pgdir, va); }
1.page_lookup的第三个参数pte_t **pte_store,是用来做什么的?
用来保存 pte_t *类型的变量
举个例子:
pte_t * _entry;
page = page_lookup(pgdir, va, &_entry);
那么现在_entry这个pte_t类型的指针变量就被赋值,实质就是 _entry = pgdir_walk(pgdir, va, false);
如果对_entry进行解引用操作
获取值 :x = *_entry
存入值: *_entry = x
page_remove()中的*entry = 0;
意思是把va对应的pte赋值为0
如果一个物理页被多个虚拟页(地址)映射,这样做也无妨。
因为这样只是对va进行"寻址",找到的是0,而不妨碍其他映射到原来va对应pa的页。
2.page_remove()这个函数名听起来像移除一个页,实际是【移除某va的页映射】
实际上后面的remove是关键字,是remove va到pa的映射。
如果是直接删除va对应的物理页,就会用delete了。
下面看insert函数:
// Map the physical page 'pp' at virtual address 'va'. // The permissions (the low 12 bits) of the page table entry // should be set to 'perm|PTE_P'. // // Requirements // - If there is already a page mapped at 'va', it should be page_remove()d. // - If necessary, on demand, a page table should be allocated and inserted // into 'pgdir'. // - pp->pp_ref should be incremented if the insertion succeeds. // - The TLB must be invalidated if a page was formerly present at 'va'. // // Corner-case hint: Make sure to consider what happens when the same // pp is re-inserted at the same virtual address in the same pgdir. // However, try not to distinguish this case in your code, as this // frequently leads to subtle bugs; there's an elegant way to handle // everything in one code path. // // RETURNS: // 0 on success // -E_NO_MEM, if page table couldn't be allocated // // Hint: The TA solution is implemented using pgdir_walk, page_remove, // and page2pa. // int page_insert(pde_t *pgdir, struct PageInfo *pp, void *va, int perm) { pte_t *entry = pgdir_walk(pgdir, va, true); if (!entry) return -E_NO_MEM; if (!(*entry & PTE_P)) goto map_new_page; if (PTE_ADDR(*entry) == page2pa(pp)){ tlb_invalidate(pgdir, va); pp->pp_ref --; }else page_remove(pgdir, va); map_new_page: *entry = page2pa(pp) | perm | PTE_P; pp->pp_ref ++; return 0; }
我们先琢磨一下,这个函数具体是做什么的?怎么用?
我的猜测是 把va映射到pa
1.如果没有映射,那么直接赋值
2.如果有映射,先移除原映射,然后再赋值
看代码,这里的判断很有意思:
首先是判断函数pgdir_walk的返回值,如果内存不够,直接返回失败
然后对返回值解引用,查看pte是否存在,这里使用了& PTE_P
存在映射分为两种情况,一种是va映射的物理页就是参数pp对应的物理页page2pa(pp)
另一种不是。但是没有搞懂他这里刷新快表是什么意思。The TLB must be invalidated if a page was formerly present at 'va' ?
注释掉后在qemu上运行没问题。
快刀斩乱麻,第二种情况不管相等还是不等都使用page_remove()
mem_init()中的线性地址到虚拟地址的映射拐了一个小弯
boot_map_region中先用pa = PADDR(kva) 然后在把 va映射到pa
何时使用 ROUNDUP? ROUNDDOWN?
函数内除极个别需要使用,其余的函数内都不需要使用
在参数上使用即可!

浙公网安备 33010602011771号