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?

函数内除极个别需要使用,其余的函数内都不需要使用

在参数上使用即可!

 

posted @ 2014-07-28 13:27  乾卦  阅读(121)  评论(0)    收藏  举报