MIT6.828LAB2 (2)

LAB2_Part 2 Virtual Memory


前言

记录一下自己的学习过程
实验内容翻译:
https://gitee.com/cherrydance/mit6.828
该翻译仅供参考

练习2

如果您还没有这样做,请查阅Intel 80386参考手册的第5章和第6章。仔细阅读关于页转换和基于页的保护的部分(5.2和6.4)。我们建议您也浏览一下关于分段的部分;尽管JOS使用分页硬件进行虚拟内存和保护,但在x86上无法禁用段转换和基于段的保护,因此您需要对其有基本的了解。

自行学习理解。

练习3

在设置虚拟内存时,尽管GDB只能通过虚拟地址访问QEMU的内存,但能够检查物理内存通常非常有用。请查看实验工具指南中的QEMU监视器命令,特别是xp命令,它允许您检查物理内存。要访问QEMU监视器,在终端中按下Ctrl-a c(相同的绑定将返回到串行控制台)。
使用QEMU监视器中的xp命令和GDB中的x命令来检查相应的物理地址和虚拟地址的内存,并确保您看到相同的数据。
我们打过补丁的QEMU版本提供了info pg命令,这也可能很有用:它显示当前页表的紧凑但详细的表示,包括所有映射的内存范围、权限和标志。标准的QEMU还提供了info mem命令,显示虚拟地址范围的映射概况以及相应的权限。

先运行make qemu-gdb和make gdb,在gdb窗口按c运行再ctrl + c停止,使用x命令查看0xf0100000处的内容,之后按c继续运行。换到qemu窗口使用ctrl+a c。使用xp查看地址0x00100000的内容。如下:
在这里插入图片描述
在这里插入图片描述
我们可以看到两处的内容一致,因此可以确定虚拟地址0xf0100000映射到了物理地址0x00100000。

问题1:假设以下的 JOS 内核代码是正确的,那么变量 x 应该具有什么类型,是 uintptr_t 还是 physaddr_t?
mystery_t x;
char* value = return_a_pointer();
*value = 10;
x = (mystery_t) value;

变量x是uintptr_t类型的。因为第三行的代码 *value = 10;对value值进行解引用。而解引用对物理地址是没有意义的,同样最后一行将value赋值给x,x和value应当是一种类型。

练习4

在文件 kern/pmap.c 中,您需要实现以下函数的代码:
pgdir_walk()
boot_map_region()
page_lookup()
page_remove()
page_insert()
check_page() 函数是从 mem_init() 中调用的,用于测试您的页表管理例程。在继续之前,您应该确保它报告成功。

与part1一样,根据注释写代码。先看pgdir_walk函数。注释太长就不列出来了,大致就是给你一个指向页目录表的指针,返回va对应的页表项的指针。在mmu.h中有地址的解析方法。
在这里插入图片描述
PDX得到页表项在页目录中的位置,PTX得到页在页表中的位置。
在这里插入图片描述
页表和页目录的各种flags。
实现代码如下,思路在注释中描述:

	pde_t *pgdir_entry = pgdir + PDX(va);
	//得到页表页在页目录中的位置
	if(!(*pgdir_entry & PTE_P)){//该页不存在
		if(!create){//是否创建
			return NULL;
		}else{
			struct PageInfo *temp = page_alloc(1);//创建页
			if(!temp){
				return NULL;
			}
			*pgdir_entry = (page2pa(temp) | PTE_P | PTE_U | PTE_W);
			temp->pp_ref++;
		}
	}
	return (pte_t *)KADDR((PTE_ADDR(*pgdir_entry))) + PTX(va);

boot_map_region函数
其作用是将[va,va+size)映射到[pa,pa+size),代码如下:

	pte_t *pgtable_entry;
	for(;va < va + size; va += PGSIZE, pa += PGSIZE){
		pgtable_entry = pgdir_walk(pgdir, (void *)va, 1);
		//得到对应的页表项地址
		*pgtable_entry = (pa | perm | PTE_P);
		//设置页表项的值,这里要知道pa的最后12位是为0的,
		//所以|运算不会影响这个物理地址的使用
	}

page_lookup函数
作用是返回虚拟地址va对应的页面。

	pte_t *pgtable_entry = pgdir_walk(pgdir, va, 0);//获取页表项地址
	if(!pgtable_entry || !(*pgtable_entry & PTE_P)){//页目录项或页表项为空
		return NULL;
	}
	if(pte_store){//pte_store不为0则存储页表项
		*pte_store = pgtable_entry;
	}
	//返回va对应的页面
	return pa2page(PTE_ADDR(*pgtable_entry));

page_remove函数
作用是取消虚拟地址va映射的物理页。代码如下:

	pte_t *pte_store;
	struct PageInfo *page = page_lookup(pgdir, va, &pte_store);//获取页面
	if(!page){//如果页面不存在则不操作
		return;
	}
	page_decref(page);//该函数会将pp->ref--并判断是否为0,为0则释放该页面
	*pte_store = 0;//对应的页表项设置为0
	tlb_invalidate(pgdir, va);

page_insert函数
其作用是将物理页面pp映射到虚拟地址va。

	pte_t *pgdir_entry = pgdir_walk(pgdir, va, 1);//获取页表项地址
	if(!pgdir_entry){//pgdir_walk的create参数为1,此时返回NULL
		return -E_NO_MEM;//说明不够分配了
	}
	pp->pp_ref++;//页面引用计数加1
	if(*pgdir_entry & PTE_P){//如果这个页表项已经映射了一个页面直接remove
		page_remove(pgdir, va);
		tlb_invalidate(pgdir, va);
	}
	*pgdir_entry = (page2pa(pp) | perm | PTE_P);//设置页表项内容
	*(pgdir + PDX(va)) |= perm;//页目录的权限永远大于等于页表项
	return 0;

全部函数完成后,make qemu查看结果:
在这里插入图片描述
如图,check_page() success。

总结

lab2的part2完结。这里的关键点就在于能不能把页目录,页表,页目录项,页表项搞明白。

  • 21
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值