MIT 6.s081 lab5.1&5.2
No.1 写在前面的话
前两节还是挺简单的,frans教授视频中也有演示,不过因为本人技术力不好写的很简单,哈哈。话不多说,开搞!
No.2 实验开始
5.1
根据提示,删除页面分配代码,看代码可知,通过追溯唯一的**growproc()**函数,或者根据函数名可知,这就是页面分配的代码,将其注释:
/*
if(growproc(n) < 0)
return -1;
*/
接下来将进程的内存大小增加n个字节,已知addr赋值为当前进程大小,可是提示必须返回旧的部分,那么不应该修改addr,而是在当前进程的基础上修改:
myproc()->sz += n;
这样就完成了。
5.2
实验是循序渐进的,5.1我们初步得到页面错误,5.2开始尝试解决问题。通过阅读YOUR JOB可知,主要任务有三个:分配一个物理页面,映射到发生错误的地址,返回用户空间。
根据提示,模仿trap中系统调用的语句,补充 else if(r_scause() == 13 || r_scause() == 15){}
,其余代码均在里面完成。
stval保存了页面错误的虚拟地址,因此可以通过分配变量读取:
uint64 va = r_stval();
继续参看提示,看一看uvmalloc()函数:
uint64
uvmalloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz)
{
char *mem;
uint64 a;
if(newsz < oldsz)
return oldsz;
oldsz = PGROUNDUP(oldsz);
for(a = oldsz; a < newsz; a += PGSIZE){
mem = kalloc();
if(mem == 0){
uvmdealloc(pagetable, a, oldsz);
return 0;
}
memset(mem, 0, PGSIZE);
if(mappages(pagetable, a, PGSIZE, (uint64)mem, PTE_W|PTE_X|PTE_R|PTE_U) != 0){
kfree(mem);
uvmdealloc(pagetable, a, oldsz);
return 0;
}
}
return newsz;
}
我们可知它主要负责的是向上增长内存,提示进一步告诉我们需要利用kalloc和mappages,那么就将与之相关的代码沿用过来:
else if(r_scause() == 13 || r_scause() == 15){
uint64 va = r_stval();
char *mem;
mem = kalloc();
memset(mem, 0, PGSIZE);
if(mappages(p->pagetable, va, PGSIZE, (uint64)mem, PTE_W|PTE_X|PTE_R|PTE_U) != 0){
kfree(mem);
p->killed = 1;
}
}
当然,我们还需要将地址向下舍入到页边界,但我不知道这样做的理由是什么【按理来说已经是出错的地址,为什么还要舍入呢?】:
uint64 va = PGROUNDDOWN((uint64)r_stval());
//r_stval()!!!丢了()!难怪一直remap!
现在qemu会出现panic,只需将相对应的panic注释加continue就好。当然,我这些代码可以运行但确实很粗糙,也有些细究的错误;为了解决上面的疑问,在这里贴上frans的代码和他的解释:
“如果有物理内存,首先会将内存内容设置为0,之后将物理内存page指向用户地址空间中合适的虚拟内存地址。具体来说,我们首先将虚拟地址向下取整,这里引起page fault的虚拟地址是0x4008,向下取整之后是0x4000。之后我们将物理内存地址跟取整之后的虚拟内存地址的关系加到page table中。”
【用时半天】