实验地址:Lab2
虚拟地址翻译为物理地址的流程如下:
Selector +--------------+ +-----------+
---------->| | | |
| Segmentation | | Paging |
Software | |-------->| |----------> RAM
Offset | Mechanism | | Mechanism |
---------->| | | |
+--------------+ +-----------+
Virtual Linear Physical
具体的细节参看文档: Intel 80386 Reference Manual中的第五、第六章。
Question
Assuming that the following JOS kernel code is correct, what type should variable x have, uintptr_t or physaddr_t?
mystery_t x;
char value = return_a_pointer();
value = 10;
x = (mystery_t) value;
答:因为指针value是虚拟地址,所以x是虚拟地址。
Exercise 4. In the file kern/pmap.c, you must implement code for the following functions.
pgdir_walk()
boot_map_region()
page_lookup()
page_remove()
page_insert()
check_page(), called from mem_init(), tests your page table management routines. You should make sure it reports success before proceeding.
现对这几个函数依次给出实现方法。
该函数的作用为:给出页目录的地址、虚拟地址和一个标志,返回该虚拟地址的页表项的地址。
实现如下:
// Fill this function in
//在页目录中的偏移量
uint32_t pdx=PDX(va);
//在页表中的偏移量
uint32_t ptx=PTX(va);
pte_t *pte;
pde_t *pde=&pgdir[pdx];
struct PageInfo* new_pg;
//页目录中有此虚拟地址的项
if ((*pde) & PTE_P)
{
//返回值为虚拟地址
pte = KADDR(PTE_ADDR(*pde));
return pte+ptx;
}
else//否则
{
//不进行分配
if (!(*pde) && !create)
return NULL;
//分配一个新的页表
new_pg = page_alloc(ALLOC_ZERO);
if (!new_pg)
return NULL;
//页目录、页表中的项都是物理地址
*pde =PTE_ADDR(page2pa(new_pg))|PTE_P|PTE_W|PTE_U;
//返回值为虚拟地址
pte = KADDR(PTE_ADDR(*pde));
//pte = KADDR(*pde);是错误的
new_pg->pp_ref++;
return pte+ptx;
}
我在完成时,出现了一个错误:pte = KADDR(*pde);
。因为pte是页对齐的,所以要用PTE_ADDR()这个宏先将物理地址页对齐。
// Fill this function in
// 向上取整
uint32_t page_num =(size+PGSIZE-1)/PGSIZE;
uintptr_t now_virual;
physaddr_t now_phy;
pte_t *pte;
int i;
for (i=0; i<page_num; i++)
{
now_virual = va + PGSIZE*i;
now_phy = pa + PGSIZE*i;
pte = pgdir_walk(pgdir,(void*)now_virual,true);
if (!pte)
panic("in function boot_map_region:pte is null\n");
*pte = PTE_ADDR(now_phy) | perm | PTE_P;
}
struct PageInfo *
page_lookup(pde_t *pgdir, void *va, pte_t **pte_store)
{
// Fill this function in
pte_t *pte=pgdir_walk(pgdir,va,false);
if (!pte)
return NULL;
if (pte_store)
pte_store = &pte;
return pa2page(*pte);
}
void
page_remove(pde_t *pgdir, void *va)
{
// Fill this function in
pte_t *pte = pgdir_walk(pgdir,va,false);
struct PageInfo *pg;
if (!(*pte))
return;
pg = pa2page(*pte);
*pte = 0;
//处理TLB
tlb_invalidate(pgdir,va);
page_decref(pg);
}
TLB的处理已经有相关的函数了,我们直接调用就行,可以不用去理解相关的实现。
int
page_insert(pde_t *pgdir, struct PageInfo *pp, void *va, int perm)
{
// Fill this function in
pte_t *pte;
pte = pgdir_walk(pgdir,va,true);
if (!pte)
return -E_NO_MEM;
if (*pte & PTE_P)
{
//如果插入的同一物理地址,同一虚拟地址,则只需注意pp_ref的变化
if (PTE_ADDR(*pte) == page2pa(pp))
pp->pp_ref--;
else
page_remove(pgdir,va);
}
pp->pp_ref++;
*pte = page2pa(pp)|perm|PTE_P;
return 0;
}
运行make;make qemu
,得到的信息如下:
qemu -drive file=obj/kern/kernel.img,index=0,media=disk,format=raw -serial mon:stdio -gdb tcp::26000 -D qemu.log
6828 decimal is 15254 octal!
Physical memory: 131072K available, base = 640K, extended = 130432K
check_page_free_list() succeeded!
check_page_alloc() succeeded!
check_page() succeeded!
kernel panic at kern/pmap.c:761: assertion failed: check_va2pa(pgdir, UPAGES + i) == PADDR(pages) + i
Welcome to the JOS kernel monitor!
Type 'help' for a list of commands.
在check_page()
里面有很多的测试用例。出现check_page() succeeded!
则代表成功了。
总结:
关于虚拟地址和物理地址,要分清二者的区别和联系,才能够比较好地完成这部分。