任务:补全这五个函数
在同学的帮助下,得到了这样一张图:
有这张图之后再来做这部分的练习就会轻松一些。
虚拟内存主要的好处是:
- 让每个程序都以为自己独占计算机内存空间,概念清晰,方便程序的编译和装载。
- 通过将部分内存暂存在磁盘上,可以让程序使用比物理内存大得多的虚拟内存,突破物理内存的限制。
- 通过对不同进程设置不同页表,可以防止进程访问其他进程的地址空间。通过在不同进程之间映射相同的物理页,又可以提供进程间的共享。
谢谢仁兄
boot_alloc函数补全:
boot_alloc(n)
是个简单的内存分配器,只有当JOS在建立他的虚拟内存系统的时候才使用。page_alloc()才是真的分配器。这个函数的核心思想就是维护一个静态变量nextfree
,里面存放着下一个可以使用的空闲内存空间的虚拟地址。(我感觉除了修改nextfree值并没干什么)
PGSIZE定义在inc/mmu.h中,大小为4096
//实际作用就是将sz向上取整(对齐)成PGSIZE的倍数,如sz=5369,PGSIZE=4096,那么addr=8192
#define PGROUNDUP(sz) (((sz)+PGSIZE-1) & ~(PGSIZE-1))
//实际作用就是将sz向下取整成PGSIZE的倍数,如sz=5369,PGSIZE=4096,那么addr=4096
#define PGROUNDDOWN(a) (((a)) & ~(PGSIZE-1))
boot_alloc函数补全:
inc/memlayout.h中:
mem_init()函数补全:
kern_pgdir = (pde_t *) boot_alloc(PGSIZE);
分配了一个页的内存,这个页就是紧跟着操作系统内核之后,因为end
指向the end of the kernel’s bss segment。
kern_pgdir[PDX(UVPT)] = PADDR(kern_pgdir) | PTE_U | PTE_P;
为页目录表添加第一个页目录表项。UVPT的定义是一段虚拟地址的起始地址,0xef400000,从这个虚拟地址开始,存放的就是这个操作系统的页表kern_pgdir,所以我们必须把它和页表kern_pgdir的物理地址映射起来
分配一块内存,用来存放一个struct PageInfo的数组。系统内核就是通过这个数组来追踪所有内存页的使用情况的:
void * 型指针,任何类型的指针都可以直接赋值给它,无需进行强制类型转换
补充page_init()函数:
page_init()
Initialize page structure and memery free list
paddr = PADDR(vaddr)
将vaddr转换成物理地址paddr?
由于 boot_alloc
返回的是内核虚拟地址 (kernel virtual address),一定要利用 PADDR
转为物理地址
page_alloc()函数补全
page2kva
函数的作用就是通过物理页获取其内核虚拟地址。补全如下:
page_free()函数补全
page_free()就好写多了,不过我还犯低级错误。。。真是太久没编程了
改错
可以看到我的check_page_free_list(1)结果出错了
void assert( int expression );
作用是现计算表达式 expression ,如果其值为假(即为0),那么它先向stderr打印一条出错信息
可以看到是nfree_basemem=0才出错的,是上面的for循环没有改变该值,也就是说,我分配的地址空间的basemem里一个free page都没有。所以我认为是我的page_init()函数出错了
又查了下kern/pmap.h中的几个函数的定义:
其中PTXSHIFT与PGSHIFT的值都是12
可以看到page2pa()
与pa2page()
是将 &pages[i] 与 i页面物理首地址相互转换的函数(见最上面的地址空间图会比较清晰点)
果然经过我的仔细排查,我发现我的错误,
size_t extend_alloced = PADDR(boot_alloc(0))/PGSIZE;
}else if(i <= extend_alloced){
pages[i].pp_ref = 1;
//this pages are already in ues inextended memory
}
有一个这样的else if在,所有小于extend_alloced(extend memory被分配好的最后page的物理地址)的页都会被设为已分配,赶紧改
else if(i >= (EXTPHYSMEM / PGSIZE) && i <= extend_alloced)
改成这样后,倒是可以succeed,但是qemu却崩了
我发现应该是
check_page_alloc()
的时候出错了,那就是我的page_alloc()函数写错了,我一看,果然
if(page_free_list == NULL) //out of free memory
return NULL;
//one=NULL; 本来这么写的,这没意义呀,函数没停止,可不就后面会空指针转来转去出错么
改过来再运行,还是出错,这次明确告诉我page_free()函数写错了。。。
我仔细检查了一遍我的page_free()函数,我觉得就是没错,我再看下原因,发现是因为要free的page的ref值不等于0,而我想到确实在page_alloc()中我动过这个值,我alloc了页面one后还把one->pp_ref=1,去掉这个就可以check_page_alloc() succeed了,但是我认为这个是有必要加上才对,搞不懂了。。。
(个人感觉是ref=0标志着这个页可不可以被分配,像BIOS那些页就属于不能分配的,然后link=NULL才是标志这页分没分配完,因此ref不需随便改,link要时时记得改,只有当ref==0&&link==NULL
代表该页可被分配且已被分配,该页才能被free)
上述个人感觉纯属瞎扯,首先函数上方的hint已经明确说了不要改pp_ref,还去改我是真瞎,其次,现在我知道了pp_ref是物理页到虚拟地址的映射次数,同一个物理页可以映射到多个虚拟地址,当没有映射时,pp_ref为0就可以free掉这个物理页,之后再有需要重新分配。所以修改pp_ref的关键时建立到虚拟地址的映射,操作是赋值页表项