JOS的物理页表分配实现
JOS的物理页表分配实现
最近将JOS的启动流程学习了一遍,细化了系统从real mode到32-bit的转变。 但是在系统真正要接管内存,分配真正页表之前,要有一个简单的页表将kernel code载入到内存中。
我们先使用一个static void i386_detect_memory(void) 函数来探测物理内存一共有多少pages,一个page为4K. 另外我们看一下JOS的memery map就可以发现我们做的工作,写这部分一定要有好的大局观。知道每部分初始化位于内存的哪个位置。
我先画了一个JOS初始化的内存page分配图。
下面这个函数是在没有建立系统分页机制前,一个临时简易的物理页表,直接是在物理内存上进行的,用的也是虚拟地址,比如kernel code 起始代码的虚拟内存在0xf0100000,实际位置要剪掉一个一个KERNBASE的宏(0xf0000000).这个部分就要有初始的物理页表进行管理。
boot_alloc(uint32_t n)
{
static char *nextfree;
char *result = NULL;
if (!nextfree) {
extern char end[];
nextfree = ROUNDUP((char *) end, PGSIZE);
// cprintf("end = %p\n ",end);
// cprintf("nextfree = %p\n ",nextfree);
}
result = nextfree;
if((uint32_t)(result - KERNBASE+n) > (uint32_t)(npages*PGSIZE))
{
panic("boot_alloc():out of physical memory\n");
}
nextfree = ROUNDUP(result+n,PGSIZE);
//这里使用两个指针来标记将要使用的空间和下一次要分配的空间,
//其中result代表将要使用的n个byte空间 ,nextfree是指下次要使用的空间的首地址
// cprintf("n = %d\n",n);
// cprintf("result = %p\n",result);
// cprintf("nextfree = %p\n\n",nextfree);
return result;
}
然后我们要建立一个映射整条物理内存的页表,前面我们已经说明。因为我们已经得到了所有的npages,然后针对每个pages对应一个pages,这样就实现了所有物理内存的映射。
通过上面的图,我们发现已被占用的物理内存有两块:
_1. 0至4KB(第0页);
2. 640KB开始,至当前可用物理地址(利用boot_alloc(0)获得,小心要通过PADDR转化成物理地址); 另外,我们认为boot loader、Kernel可执行代码的ELF文件头这两块内存的内容是可以覆盖的。
所以我们初始寻找出IOPHYSMEM EXTPHYSEME。这部分代码主要是BIOS,CMOS的固件代码,不能覆盖,需要跳过。 但是首先我们需要找到IOPHYSMEM EXTPHYSEME对应的pages页。然后跳过,将空余的pages链接到page_free_list 。如果之后有需要,就要从page_free_list 上面取下一个page,进行分配,说白了就是链表节点的链接与删除。
void
page_init(void)
{
size_t i;
size_t IOPHYSMEM_begin = ROUNDDOWN(IOPHYSMEM,PGSIZE)/PGSIZE;
size_t EXTPHYSEME_end = PADDR(boot_alloc(0)) / PGSIZE;
// cprintf("IO = %d EXT = %d \n",IOPHYSMEM_begin,EXTPHYSEME_end);
// cprintf("npages_basemem:%d\n",npages_basemem);
// cprintf("PGNUM : %d\n",PGNUM(PADDR(boot_alloc(0) ) ) );
// cprintf("npages = %d\n",npages);
page_free_list = NULL;
for (i = 0; i < npages; i++) {
pages[i].pp_ref = 0;
if(0 == i || ( (IOPHYSMEM_begin <= i) && ( i < EXTPHYSEME_end ) ) )
{
// cprintf("IOHOLE = %p\n",&pages[i]);
}
else
{
pages[i].pp_link = page_free_list;
page_free_list = &pages[i];//等于链表实现
//cprintf("Normal = %p\n",page_free_list);
}
}
}
struct PageInfo *
page_alloc(int alloc_flags)
{
// Fill this function in
struct PageInfo *page_return;
if(page_free_list != NULL)
{
page_return = page_free_list;
page_free_list = (struct PageInfo *)page_free_list->pp_link;
if(alloc_flags & ALLOC_ZERO)
{
memset(page2kva(page_return),'\0',PGSIZE);
}
}
else
page_return =NULL;
return page_return;
}
void
page_free(struct PageInfo *pp)
{
// Fill this function in
pp->pp_link = page_free_list;
page_free_list = pp;
return;
}