MIT_6.828_Lab2 Part2

实验地址: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.

现对这几个函数依次给出实现方法。

pgdir_walk()

该函数的作用为:给出页目录的地址、虚拟地址和一个标志,返回该虚拟地址的页表项的地址。
实现如下:

	// 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()这个宏先将物理地址页对齐。

boot_map_region()

	// 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;
	}

page_lookup()

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);
}

page_remove()

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!则代表成功了。

总结:
关于虚拟地址和物理地址,要分清二者的区别和联系,才能够比较好地完成这部分。

你可能感兴趣的:(操作系统)