MIT6.828学习之Lab2_Part 2: Virtual Memory

我觉得Part2最关键还是这张图,只有深刻理解了Logical Address经过分段机制转换成Linear Address再经过分页机制转换成Physical Address这个过程以及他们的逆过程,才能顺利完成该部分。当然Lab2只涉及线性地址与物理地址的相互转换。
感谢__七把刀__
在这里插入图片描述PDE(page directory entry页目录表项)
PTE(page table entry页表表项)
二级分页。每个程序都有个Page Directory,包括1k个PDE,每个PDE的前20位又可以指向一个Page Table,包括1k个PTE,每个PTE的前20位与linear address的offset字段合在一起就可以指向一个page的物理地址了。就好像每个程序都能独占4G的内存(1k(amount of PDE)*1k(amount of PTE)*4kB(PageSize))。

PTE_P 代表页表format中的P(present)位 当页表的任意级别的P=0时,该条目不能用于地址转换

在lab2里面可以暂时不要管段翻译,可以看成整个系统是一个段的,即其段基址是0,段范围(limit)是0xffffffff.整个系统就被分成了一个段,所以系统可以看成是纯分页形式的。在分页中,地址的转化也比较简单,JOS被设计成0-256M的物理地址映射到以0xf0000000开始的256M虚拟地址,所以从物理地址转化虚拟地址比较方便简单。
谢谢fang92

补全这些函数并回答自己的问题

pgdir_walk

给定页目录表pgdir(首地址),线性地址va,pgdir_walk()会返回va在二级页表中对应页表表项(PTE)的地址,是个kernel virtual address。
这个二级页表可能不存在,也就是说pgdir中的页目录表项PDE的P bit为0,那么当create=1时可以新建一个二级页表,并将二级页表的物理地址赋给对应位置的页目录表项PDE。
无论二级页表存在或是新创建,最后都是返回va在二级页表中对应表项的地址,转换成虚拟地址

/*Given 'pgdir', a pointer to a page directory, pgdir_walk returns
a pointer(virtual address) to the page table entry (PTE) for linear address 'va'.*/
pte_t *pgdir_walk(pde_t *pgdir, const void *va, int create)
{
   // Fill this function in
   //There are PDX() PET_ADDR() and PTX() in mmu.h
   //but I don't know and don't follow the hint...
   pde_t pde_num = PDX(va);
   pte_t pte_num = PTX(va);
   
   pde_t *pde = pgdir + pde_num;
   pte_t *pte;
   struct PageInfo *new_PT;
   
   if(((*pde) & PTE_P)==0){

   	if(create == 0)
   		return NULL;
   	new_PT=page_alloc(1);// 0 or 1???
   	if(new_PT == NULL)
   		return NULL;
   	new_PT->pp_ref++;
   	
   	//why other bit don't need to set?
   	*pde = page2pa(new_PT) | PTE_P | PTE_U |PTE_W | PTE_AVAIL;
   }
   
   pte = (pte_t *)KADDR(PTE_ADDR(*pde));// The first address of the page table
   return &pte[pte_num]; //kernal virtual address!!! why?
}

为什么pgdir_walk()最后返回的是一个内核虚拟地址?
为什么page_alloc中memset那也是需要kernel virtual address?

From code executing on the CPU, once we’re in protected mode (which we entered first thing in boot/boot.S), there’s no way to directly use a linear or physical address. All memory references are interpreted as virtual addresses and translated by the MMU, which means all pointers in C are virtual addresses.

由此可以看出,凡是C中的指针都是虚拟地址的,所以我认为页目录表表项地址也好、二级页表表项地址也好,在表项内存的都是物理基地址(的前20位?后12位做标志位),拿出来用就都得转成虚拟地址

boot_map_region

把虚拟地址 [va, va+size)映射到物理地址[pa, pa+size),也就是说让va对应的页表项内存物理地址pa。其中va,pa,size都是4K对齐的

主要映射了三个区域:
第一个是 [UPAGES, UPAGES+PTSIZE)映射到页表存储的物理地址 [pages, pages+4M)
第二个是 [KSTACKTOP-KSTKSIZE, KSTACKTOP) 映射到 [bootstack,bootstack+32KB)处。
第三个则是映射整个内核的虚拟空间[KERNBASE, 2*32-KERNBASE) 到 物理地址 [0,256M)
谢谢__七把刀__

//Map [va, va+size) of virtual address space to physical [pa, pa+size) in the page table rooted at pgdir
static void boot_map_region(pde_t *pgdir, uintptr_t va, size_t size, physaddr_t pa, int perm)
{
   // Fill this function in
   for(size_t i=0; i*PGSIZE < size; i++){
   	pte_t *pte = pgdir_walk(pgdir, (void *)va, 1);
   	assert(pte);
   	*pte = pa| perm | PTE_P; //we don't care the offset???
   	va+=PGSIZE;
   	pa+=PGSIZE;
   }
}

为什么在boot_map_region中把虚拟地址va映射到物理地址pa不需要管offset?
正常来讲va包含三个字段(dir | table | offset),va映射的物理地址应该是PTE_ADDR(*(*pgdir[dir])[table]) & offset。我猜想现在讨论的虚拟地址与物理地址针对的是一个物理页page的首地址,12位的offset恰好是一个物理页内的偏移量,所以讨论首地址的时候页内偏移量就可以忽略。

page_lookup

查找虚拟地址va对应的页表项,返回页表项内保存的物理页在PageInfo结构体中的索引值
如果还没有物理页被映射到va,那就返回NULL(包括页目录表项的PTE_P=0或者页表表项的PTE_P=0两种情况)

//Return the page mapped at virtual address 'va'.
struct PageInfo *page_lookup(pde_t *pgdir, void *va, pte_t **pte_store)
{
	// Fill this function in
	pde_t pde_num = PDX(va);
	pde_t *pde = pde_num + pgdir;
	if(((*pde) & PTE_P)==0)
		return NULL;
	pte_t *pte = pgdir_walk(pgdir, va, 0);
	if(!pte)	// I'm always careless!
		return NULL;
	if(pte_store != 0)
		*pte_store=pte; //a little confusion
	return pa2page(PTE_ADDR(*pte));//???do nothing on the offset again?
}
page_remove

取消物理页对虚拟地址va的映射,也就是说删除va在页表中的对应表项(删除表项即取消映射)
如果本来va就不对应一个物理页,那就不用操作
否则,找到va在页表中的表项,表项内容设0,令TLB失效,并把原物理页引用次数pp_ref–,如果pp_ref减为0了就把该物理页free掉,等待再被分配

//Unmaps the physical page at virtual address 'va'.
void page_remove(pde_t *pgdir, void *va)
{
	// Fill this function in
	pte_t *pte;
	
	//there is a hint above, but I'm still wrong about the &pte
	struct PageInfo *un_page=page_lookup(pgdir, va, &pte); 
	
	//because PTE may be nonexistent, so PTE_P is necessary
	if(un_page && (*pte & PTE_P)){ 
		//if(un_page->pp_ref==1) whether the pp_ref is 1 or not, it has removed a PTE here
		tlb_invalidate(pgdir, va);
		*pte=0;
		page_decref(un_page);
	}
}
page_insert

建立PageInfo结构体pp对应物理页与虚拟地址va的映射。实际上就是设置好va在页表中的对应表项内容
如果va已经有映射了(*pte & PTE_P == 1),就remove掉之前的映射

//Map the physical page 'pp' at virtual address 'va'.
int page_insert(pde_t *pgdir, struct PageInfo *pp, void *va, int perm)
{
	// Fill this function in
	
	pte_t *pte = pgdir_walk(pgdir, va, 1);
	if(!pte)
		return -E_NO_MEM;
	
	pp->pp_ref++;
	if(*pte & PTE_P)
		page_remove(pgdir, va);
	
	*pte = PTE_ADDR(page2pa(pp)) | perm | PTE_P; 
	//pp->pp_ref++; why this code has to be up there?
		
	return 0;
}

为什么pp->pp_ref++必须写在page_remove()的前面?
有可能当前要映射的物理页与之前映射的物理页是同一个物理页,那么page_remove()就有可能将这个物理页给free掉,那么这时候再对物理页操作就为时已晚了。

在这里插入图片描述

其他问题

page_allloc(1)的参数什么时候设0,什么时候设1?
page_alloc(flag):从page_free_list中取出一页,当flag=1时,初始化该页内存

为什么page_alloc中pp_ref不需要++,在pdgir_walk与page_insert中却都要++?
应该是每一次page被映射到一个虚拟地址va的时候pp_ref需要increment,而如果取消映射就得decrement,page_alloc只是分配了物理页,并没有与虚拟地址建立映射,故不需要改pp_ref值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值