MIT6.828-Lab2-Part1-Exercise1-实现pmap.c中的物理内存页管理函数

 

物理页面管理相关的函数

物理内存页管理

  1. 完善 kern/pmap.c 中的函数:boot_alloc(), mem_init(), page_init(), page_alloc(), page_free()check_page_free_list()check_page_alloc() 两个函数将会检测你写的页分配器代码是否正确。

entry.S 中可以看到,当进入 kernel 分配好堆栈位置(movl $0x0,%ebp; movl $(bootstacktop),%esp)后,紧接着就调用了 i386_init(),它定义在 kern/init.c 中,可以看到经过几步之后就开始调用 mem_init()


在lab中出现了 page table 和 page directory 的概念,但是单看lab笔者也没有弄明白它们之间的区别。但后来发现其实就是多级页表,更详细可以看一下补充材料–多级页表

mem_init()

首先调用 i386_detect_memory(),分析可知其功能为检测内存空间,得到了两个重要的全局变量 npages, npages_basemem 分别代表总内存有多少 ,base(0x0~0xA0000)内存有多少

接下来通过 boot_alloc() 分配一 大小的空间用于存放页表目录(page directory),并返回一个 pde_t * 指向它。

boot_alloc()

// mem_init 中:
kern_pgdir = (pde_t *) boot_alloc(PGSIZE);
memset(kern_pgdir, 0, PGSIZE); // 可以看到只分配了一页的大小用来存储页目录指针,先将这部分清零。

// 完善 boot_alloc()
static void *
boot_alloc(uint32_t n)
{
	static char *nextfree;	// virtual address of next byte of free memory
	char *result;

	// 只有第一次需要初始化nextfree
  // 并且将其对其至 PGSIZE 整数倍的位置
	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;  // 分配的空间的首地址交与 result
	nextfree = ROUNDUP((char*)((uint32_t)nextfree + n), PGSIZE);  // 分配n个字节的空间,依旧需要对齐!
	if ((uint32_t)nextfree - KERNBASE > (npages * PGSIZE)) {
    // 判断nextfree是否合法,利用inc/assert.h 里定义的panic函数,方便log信息。
		panic("Out of memory!\n");
	}
	return result; // 输出分配的空间的首地址

	// return NULL;
}

接下来 kern_pgdir[PDX(UVPT)] = PADDR(kern_pgdir) | PTE_U | PTE_P; 是为这个页目录(page directory)增加第一个页目录表项。且此表项代表的是此页目录本身。PDX 用于将虚拟地址 UVPT 转成页目录索引,PADDR用于将虚拟地址转成物理地址。PTE_U | PTE_P 是标志位,代表页目录项存在,运行权限为用户态。

根据注释,需要为npages个记录页面信息的 struct PageInfo 分配空间并清零:

// mem_init()
//////////////////////////////////////////////////////////////////////
// Allocate an array of npages 'struct PageInfo's and store it in 'pages'.
// The kernel uses this array to keep track of physical pages: for
// each physical page, there is a corresponding struct PageInfo in this
// array.  'npages' is the number of physical pages in memory.  Use memset
// to initialize all fields of each struct PageInfo to 0.
// Your code goes here:
pages = (struct PageInfo *) boot_alloc(npages * sizeof(struct PageInfo));
memset(pages, 0, npages * sizeof(struct PageInfo));

接下来 page_init() 用于初始化pages数组,并初始化pages_free_list(它代表由空闲页组成的链表的head指针),

page_init()

为了完成这个函数,无非就是分析到底pages数组的每个索引值代表的页面,到底是空闲还是忙碌。注释中也给了我们提示哪些是空闲/忙碌。

// page_init()
// 0 页面是忙碌
// [1, npages_basemem) 是空闲
// [IOPHYSMEM / PGSIZE, EXTPHYSMEM / PGSZIE) 是忙碌,是一个从640KB~1M范围内,大小为384KB的IO hole
// [EXTPHYSMEM / PGSIZE, nextfree) 是忙碌,这些页面目前通过boot_alloc()分配掉了。

// Pageinfo结构有两个成员
// 一个 struct Pageinfo * 指涉下一个页面
// 一个 uint16_t pp_ref 记录指向此页面的指针的个数,用于判断空闲/忙碌
	size_t i;
	page_free_list = NULL;

	int num_allocated = ((uint32_t) boot_alloc(0) - EXTPHYSMEM) / PGSIZE;
	int num_iohole = (EXTPHYSMEM - IOPHYSMEM) / PGSIZE;

	for (i = 0; i < npages; i++) {
		if (i == 0 || (i >= npages_basemem && i < (npages_basemem + num_allocated + num_allocated))) {
			pages[i].pp_ref = 0;
			pages[i].pp_link = page_free_list;
			page_free_list = &pages[i];
		} else {
			pages[i].pp_ref = 1;
			pages[i].pp_link = NULL;
		}
	}

接下来运行 check_page_free_list() 检查页面空闲链表的合理性之后,就要运行 check_page_alloc() 来检查 page_alloc(), page_free() 两个子函数能否正确运行:

page_alloc(), page_free()

// page_alloc(int alloc_flags) 实现分配页面的功能,alloc_flags 表示是否清零此页面
// 根据如下的提示完善函数
// Allocates a physical page.  If (alloc_flags & ALLOC_ZERO), fills the entire
// returned physical page with '\0' bytes.  Does NOT increment the reference
// count of the page - the caller must do these if necessary (either explicitly
// or via page_insert).
//
// Be sure to set the pp_link field of the allocated page to NULL so
// page_free can check for double-free bugs.
//
// Returns NULL if out of free memory.
//
// Hint: use page2kva and memset
struct PageInfo *
page_alloc(int alloc_flags)
{
	struct PageInfo *result;
	if (page_free_list == NULL) {
		return NULL;  // 没有空闲页面,返回NULL
	}
	result = page_free_list;
	page_free_list = result->pp_link;
	result->pp_link = NULL;

	if (alloc_flags & ALLOC_ZERO) {
		memset(page2kva(result), 0, PGSIZE);  // 如果清零,就memset一下
	}
	return result;
	// return 0;
}

// page_free(struct PageInfo *pp) 实现释放pp的资源,将其加入空闲页面
// 需要通过assert断言pp页面的状态
// Return a page to the free list.
// (This function should only be called when pp->pp_ref reaches 0.)
//
void
page_free(struct PageInfo *pp)
{
	// Fill this function in
	// Hint: You may want to panic if pp->pp_ref is nonzero or
	// pp->pp_link is not NULL.
	assert(pp->pp_link == NULL);
	assert(pp->pp_ref == 0);
	pp->pp_link = page_free_list;
	page_free_list = &pp;
}

参考资料

  1. cnblog-fatsheep9146