MIT6.828 LAB2:http://pdos.csail.mit.edu/6.828/2014/labs/lab2/
LAB2里面主要讲的是系统的分页过程,还有就是简单的虚拟地址到物理地址的过程。关于系统分页,在MIT6.828 虚拟地址转化为物理地址——二级分页:http://blog.csdn.net/fang92/article/details/47320747中有讲。
下面主要是lab2的几个exercise的解题过程。
1.第一个boot_alloc()函数:
static void *
boot_alloc(uint32_t n)
{
static char *nextfree; // virtual address of next byte of free memory
char *result;
if (!nextfree) {
extern char end[];<span style="white-space:pre"> </span>//end points to the end of the kernel's bss segment:
nextfree = ROUNDUP((char *) end, PGSIZE);
}
if(n==0)
return nextfree;
result = nextfree;
nextfree += n;
nextfree = ROUNDUP( (char*)nextfree, PGSIZE);
return result;
}
boot_alloc(unit32_t n)主要是申请n个字节的地址空间,返回申请空间的首地址。如果n是0,则返回nextfree(未分配空间的首地址)。其中,分配的地址是页对齐的,即4K对齐。
这个函数,巧妙应用了未初始化的全局变量和静态变量会被自动初始化为0这个特性。函数以首先定义静态局部变量nextfree,它指向空闲内存空间的首地址。由于未初始化,所以变量自动初始化为0,所以首次调用boot_alloc()函数的时候,nextfree的值是0,会执行下面的语句:
if (!nextfree) {
//end points to the end of the kernel's bss segment
nextfree = ROUNDUP((char *) end, PGSIZE);
}
end指向的是bss段的末尾,通过调用objdump -h kernel 可以看程序的各个段的信息。
从上面的图片可以看出,在整个程序里面,.bss段的位置是最下面的,注意.comment段是一些程序的注释信息,是不进内存的。所以end未初始化的全局变量,在.bss段中。所以end是指向的是整个内存的最后一个地址。(.bss段到底是怎么回事)。
接下来的ROUNDUP就是一个宏定义,用来4K对齐的,最后会返回一个地址,指向end的下一个空闲页的首地址。
所以在系统第一次调用boot_alloc()这个函数的时候,首先nextfree会被指向第一个空闲页的首地址。接下来,根据输入的n,来分配地址。如果n=0,则返回nextfree,否则分配n字节的地址,返回分配地址的首地址。注意,整个过程中,需要4K对齐。
2.mem_init()函数
这个函数只需要补充一部分就可以了。主要是要为struct PageInfo的结构体的指针pages申请一定的地址空间。首先来看struct PageInfo的定义:
struct PageInfo
{ //Next page on the free list.
struct PageInfo *pp_link;
uint16_t pp_ref;
}
这个结构体,主要是用来保存内存中的所有物理页面的信息的。每一个PageInfo对应一个物理页面。
pageInfo主要有两个变量:
pp_link表示下一个空闲页,如果pp_link=0,则表示这个页面被分配了,否则,这个页面未被分配,是空闲页面。
pp_ref表示页面被引用数,如果为0,表示是空闲页。(这个变量类似于智能指针中指针的引用计数)。
补充的代码比较简单,就是位pages申请足够的空间(npages的页面),来存放这些结构体,并且用memset来初始化:
pages = boot_alloc(npages * sizeof (struct PageInfo));
memset(pages, 0, npages*sizeof(struct PageInfo));
关于pages:
pages是用来管理物理内存页的一个结构体。首先,pages是整个系统物理内存的第0页,pages的值是一个虚拟地址。由于结构体是连续的,所以通过pages[i]-pages可以得到页的编号i,在通过i<<12就可以得到pages[i]所对应的页的物理内存,由于实现系统的物理内存和虚拟内存的转换比较简单,虚拟内存=物理内存+ 0xF0000000.所以通过pages这个结构体,在知道具体的物理页时,就可以很容易得到物理页对应的物理地址和虚拟地址,如图。
3.page_init()
page_init()函数主要就是结构体pages的初始化,就是在系统刚刚开始运行时,对物理内存分配进行初始化操作,按照上面的提示一步一步写就可以了,比较简单。