MIT_6.828_lab2_exercise2_3_4_讲解

exercise2、3都是看文档,和调试命令

不要不认真看文档!不然后面肯定会有做不下去的地方,逼着你再从头开始把文档看一遍!

比如exercise2就是详细复习一下intel 80386处理器的页表机制具体实现,虽然以前学了,但是也一定要再认真看一下,不然写不下去的!

exercise4要完成下面这几个函数,在kern/pmap.c中

        pgdir_walk()
        boot_map_region()
        page_lookup()
        page_remove()
        page_insert()

难度比较大。这些是要实现的页表管理的函数,完成对虚拟地址到物理页的对应匹配等。

pgdir_walk()

pte_t *
pgdir_walk(pde_t *pgdir, const void *va, int create)

给出一个指向页表目录的指针,如果va所属的二级页表不在这个目录中,就新建一个二级页表,返回指向va所在页的二级页表项的指针;如果va所属二级页表在这个目录中,就直接返回va对应的二级页表项的指针。

先明白几个概念,不然做起来容易糊涂

page directory 页表目录:一级页表

page directory entry 页表目录项:一级页表项

page table:二级页表

page table entry 页表项:二级页表项

// Given 'pgdir', a pointer to a page directory, pgdir_walk returns
// a pointer to the page table entry (PTE) for linear address 'va'.
// This requires walking the two-level page table structure.
//
// The relevant page table page might not exist yet.
// If this is true, and create == false, then pgdir_walk returns NULL.
// Otherwise, pgdir_walk allocates a new page table page with page_alloc.
//    - If the allocation fails, pgdir_walk returns NULL.
//    - Otherwise, the new page's reference count is incremented,
//	the page is cleared,
//	and pgdir_walk returns a pointer into the new page table page.
//
// Hint 1: you can turn a PageInfo * into the physical address of the
// page it refers to with page2pa() from kern/pmap.h.
//
// Hint 2: the x86 MMU checks permission bits in both the page directory
// and the page table, so it's safe to leave permissions in the page
// directory more permissive than strictly necessary.
//
// Hint 3: look at inc/mmu.h for useful macros that manipulate page
// table and page directory entries.
//
pte_t *
pgdir_walk(pde_t *pgdir, const void *va, int create)
{
	// Fill this function in
	pde_t* tpde = &pgdir[PDX(va)];	//PDX(va)是含va的二级页表在一级页表的索引,即指向va的一级页表项。pgdir是一个页表目录,就是一级页表的初址
	pte_t* tpte=NULL;				//保存包含va的二级页表首地址
	if (*tpde & PTE_P)	{	//如果一级页表中有含va的一级页表项,且这个二级页表页可以分配,
		tpte = (pte_t*) KADDR(PTE_ADDR(*tpde));	//就将tpte指向这个二级页表
	}
	else	{	//如果一级页表中对应项没有已分配的二级页表页
		if (!create)	return NULL;	//如果不可创建,返回空
		else {	//如果可以创建
			struct PageInfo * newpage = page_alloc(true);	//创建一个二级页表页
			if (!newpage)	return NULL;	//如果创建失败
			newpage->pp_ref++;
			*tpde = page2pa(newpage) | PTE_P | PTE_U | PTE_W;	//创建之后,将这个对应的创建的页标志位设为present、user、write
			tpte = page2kva(newpage);	//tpte指向新创建的二级页表页
		}
	}
	return &tpte[PTX(va)];	//PTX得到va在二级页表页内的偏移,以tpte初址寻址后,得到含va的二级页表项,取地址,得到指向va所在的二级页表项的指针。
}

由于一个页大小是4KB,即2^12B,共12位,所以前20位作为索引,后12位作为页内偏移。因为一个页的地址总是4KB的整数倍,所以页表项的前20位可用于存放页地址,后12位用于存放各种标志位。

提示里面有一句,将权限保留在page directory中更安全,所以我们只在pde中设置了权限。不断按位或,可以将各个权限的标志位设为1。

这个函数很重要,能够直接通过虚拟地址定位到它所在的二级页表项,后面还会反复用到。

boot_map_region()

static void
boot_map_region(pde_t *pgdir, uintptr_t va, size_t size, physaddr_t pa, int perm)

将[va, va + size)的虚拟地址映射到物理地址pa处。直接用pgdir_walk()找到对应的二级页表项,将不断更新的pa设置标志位之后填上就行了。

但是我一开始写的不知为什么一直有错误,最后也没改好。

//
// Map [va, va+size) of virtual address space to physical [pa, pa+size)
// in the page table rooted at pgdir.  Size is a multiple of PGSIZE, and
// va and pa are both page-aligned.
// Use permission bits perm|PTE_P for the entries.
//
// This function is only intended to set up the ``static'' mappings
// above UTOP. As such, it should *not* change the pp_ref field on the
// mapped pages.
//
// Hint: the TA solution uses pgdir_walk
static void
boot_map_region(pde_t *pgdir, uintptr_t va, size_t size, physaddr_t pa, int perm)
{
	// Fill this function in
	//注意,英文提示里已经说了,size是PGSIZE的倍数,va和pa都是页对齐的,所以不用移位了。
	// for (uintptr_t endp = va + size; va != endp; va += PGSIZE, pa += PGSIZE)	{
	// 	pte_t* temp_page_table_entry = pgdir_walk(pgdir, (void*)va, true);
	// 	*temp_page_table_entry = pa | perm | PTE_P;
	// }
	//上面注释的会出bug,不知道为什么。
	for(size_t i = 0; i < size; i += PGSIZE){
		pte_t* pte = pgdir_walk(pgdir, (const void *)va + i, 1);
		*pte = (pa + i) | PTE_P | perm;
	}
}

page_lookup()

struct PageInfo *
page_lookup(pde_t *pgdir, void *va, pte_t **pte_store)

返回va所在页对应的页表结构体。先通过pgdirwalk找到二级页表项,其中值的前20位就是va所在的物理页地址。先右移12位,再左移12位,剩下的就是va所在的物理页地址。再根据函数pa2page,就得到了对应的页结构体。

pa2page和page2pa是实现页结构体和物理地址相互转化的函数。

//
// 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)
{
	// Fill this function in
	pte_t* ppte = pgdir_walk(pgdir, va, 0);
	if (!ppte)	return NULL;
	pte_t idx = *ppte>>PTXSHIFT<<PTXSHIFT;
	if (pte_store != 0)	*pte_store = ppte;
	return pa2page(idx);
}

注意提示里面说的,pte_store要在page_remove里面用到。

page_remove()

void
page_remove(pde_t *pgdir, void *va)

删除va和其物理页的匹配。直接使用page_lookup找到页表结构体,page_free即可。

可以看到为什么在page_lookup()中要用一个二重指针了。为了删除这个页匹配,要清除二级页表项中的值。要注意,要存储的是一个指针。如果page_lookup定义中仅仅使用了

pte_t *pte_store

而不是双重指针,那么在作为形参传值,函数退出后,ppp的值并不会被改变。但如果采用的是二重指针,就可以在二重指针指向的地址中存储一个指针。

比如在这里,在&ppp中存储了二级页表项的指针,那么再*ppp就得到了二级页表项指针指向的页表项,可以直接清零。

//
// 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)
{
	// Fill this function in
	pte_t * ppp;
	struct PageInfo * pg1 = page_lookup(pgdir, va, &ppp);
	if (!pg1)	return;
	*ppp = 0;
	pg1->pp_ref--;
	if (pg1->pp_ref == 0)	page_free(pg1);
	tlb_invalidate(pgdir, va);
}

page_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)
{
	// Fill this function in
	pte_t * tpte = pgdir_walk(pgdir, va, true);
	if (!tpte)	return -E_NO_MEM;
	pp->pp_ref++;
	if (*tpte & PTE_P)	page_remove(pgdir, va);
	*tpte = page2pa(pp)	| perm | PTE_P;
	return 0;
}

不是完全按照提示顺序写的,而且我也没有使用tlb_invalidate。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值