MIT_6.828_Lab2 Part1

实验地址:Lab2

Lab2的第一部分为物理地址的管理,其中包括物理内存的初始化,分配以及释放。

首先,在inc/mmu.h中定义了几个宏:

#define NPDENTRIES	1024		// page directory entries per page directory
#define NPTENTRIES	1024		// page table entries per page table

#define PGSIZE		4096		// bytes mapped by a page
#define PGSHIFT		12		// log2(PGSIZE)

其中PGSIZE表示一页的大小。

其次需要明确的是,当前运行在高地址阶段,即内核运行在KERNBASE之上的虚拟内存中。在kern/entry.S中已经将[KERNBASE,KERNBASE+4MB)的虚拟内存映射到了物理内存[0,4MB)里了。

现在来完成Exercise 1。

Exercise 1. In the file kern/pmap.c, you must implement code for the following functions (probably in the order given).

boot_alloc()
mem_init() (only up to the call to check_page_free_list(1))
page_init()
page_alloc()
page_free()

check_page_free_list() and check_page_alloc() test your physical page allocator. You should boot JOS and see whether check_page_alloc() reports success. Fix your code so that it passes. You may find it helpful to add your own assert()s to verify that your assumptions are correct.

题目的意思是补全那几个函数,使其能够正常地工作。

boot_alloc()

函数的要求:分配足够的虚拟内存页,使得这些页能够容纳n个字节的内容。
函数中的nextfree指针指向可以分配的内存地址。
实现代码如下:

	if (!nextfree) {
		extern char end[];
		nextfree = ROUNDUP((char *) end, PGSIZE);
	}

	// Allocate a chunk large enough to hold 'n' bytes, then update
	// nextfree.  Make sure nextfree is kept aligned
	// to a multiple of PGSIZE.
	//
	// LAB 2: Your code here.
	
	result = nextfree;
	nextfree = ROUNDUP(result + n, PGSIZE);

	if (PADDR(nextfree)>=0x400000 || nextfree < result)
	{
		cprintf("nextfree:%u\n",nextfree);
		panic("nextfree's value is not correct\n");
	}

	return result;

首先使用ROUNDUP()这个宏向上取整,获取足够的虚拟页,然后判断分配的空间是否超出了4MB的物理空间范围,如果超出了,就报错(因为现在只映射了4MB的物理空间)。另外注意end这个变量,这是在kernel.ld这个链接脚本里面声明的变量,用来记录内核所有代码和数据的边界,在这个边界之外分配新的空间则不会破坏内核的代码和数据:

	.bss : {
		PROVIDE(edata = .);
		*(.bss)
		PROVIDE(end = .);
		BYTE(0)
	}

	/DISCARD/ : {
		*(.eh_frame .note.GNU-stack)

mem_init()

这个函数里面写的东西很少,而且简单:

	// Your code goes here:	
	pages = (struct PageInfo *)boot_alloc(npages*sizeof(struct PageInfo));
	memset(pages,0,npages*sizeof(struct PageInfo));

为pages分配npages个结构体的空间,并将这个数组初始化为’\0’。

page_init()

这个函数是这几个函数中稍微麻烦一点的一个函数,完成了它,后面就简单了。
根据提示:
// The example code here marks all physical pages as free.
// However this is not truly the case. What memory is free?
// 1) Mark physical page 0 as in use.
// This way we preserve the real-mode IDT and BIOS structures
// in case we ever need them. (Currently we don’t, but…)
// 2) The rest of base memory, [PGSIZE, npages_basemem * PGSIZE)
// is free.
// 3) Then comes the IO hole [IOPHYSMEM, EXTPHYSMEM), which must
// never be allocated.
// 4) Then extended memory [EXTPHYSMEM, …).
// Some of it is in use, some is free. Where is the kernel
// in physical memory? Which pages are already in use for
// page tables and other data structures?
//
// Change the code to reflect this.
// NB: DO NOT actually touch the physical memory corresponding to
// free pages!

  • Mark physical page 0 as in use.
    将物理页第零页标记为已经使用,以作后用。

  • The rest of base memory, [PGSIZE, npages_basemem * PGSIZE)
    这一个区间里面的页是没有使用的。

  • Then comes the IO hole [IOPHYSMEM, EXTPHYSMEM), which must never be allocated.
    保留为IO使用,标记为已经使用。

  • Then extended memory [EXTPHYSMEM, …)
    这一个区间分为两个子区间:(1)内核与上面使用boot_alloc()已经分配使用了的空间 (2)未使用的空间。所以我们的任务就是确定分割二者的边界。

先说实现的代码:

	size_t i;
	pages[0].pp_ref = 1;
	pages[0].pp_link = NULL;

	for (i = 1; i<npages_basemem; i++)
	{
		pages[i].pp_ref = 0;
		pages[i].pp_link = page_free_list;
		page_free_list = &pages[i];
	}

	for (i=IOPHYSMEM/PGSIZE; i<EXTPHYSMEM/PGSIZE; i++)
	{
		pages[i].pp_ref = 1;
		pages[i].pp_link = NULL;
	}

	for (; i<PADDR(pages+npages*sizeof(struct PageInfo))/PGSIZE; i++)
	//for (; i<PADDR(boot_alloc(0))/PGSIZE; i++)
	{
		pages[i].pp_ref = 1;
		pages[i].pp_link = NULL;
	}

	for (; i<npages; i++)
	{
		pages[i].pp_ref = 0;
		pages[i].pp_link = page_free_list;
		page_free_list = &pages[i];
	}

可以看到,[EXTPHYSMEM, …)分割为两个子区间的边界为pages+npages*sizeof(struct PageInfo)或者boot_alloc(0),后者比较简练。这都是虚拟地址,要将其转化为物理地址。

page_alloc()

根据提示进行实现就行了。

	// Fill this function in
	struct PageInfo *result = page_free_list;
	if (!result)	
	{
		return NULL;
	}
	else
	{

		page_free_list = result->pp_link;

		result->pp_link = NULL;

		if (alloc_flags & ALLOC_ZERO)
			memset(page2kva(result),'\0',PGSIZE);
		return result;
	}

需要注意的是page2kva(result)的使用。page2kva()函数返回KERNBASE+(result-pages)<<PGSHIFT,就可以经过地址变换,就可以操作真实的物理页了。

page_free()

	if (pp->pp_ref || pp->pp_link)
		panic("This page should not be free!(pp_ref or pp_link is not zero)\n");
	
	pp->pp_link = page_free_list;
	page_free_list = pp;

这个不必多说。

结果

使用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!
kernel panic at kern/pmap.c:751: assertion failed: page_insert(kern_pgdir, pp1, 0x0, PTE_W) < 0
Welcome to the JOS kernel monitor!
Type ‘help’ for a list of commands.

出现

check_page_free_list() succeeded!
check_page_alloc() succeeded!

则表示当前的任务成功完成。

总结:
内核在虚拟内存中是从KERBASE+1MB开始的,所以虚拟内存的[KERBASE,KERBASE+1MB)空间保留给硬件等使用。从kern/kernel.ld中也可以看出。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值