参考别人博客写的,差不多对着抄的,如侵权则删....
1.Eliminate allocation from sbrk()
sys_sbrk里面删除 growproc代码
uint64
sys_sbrk(void)
{
int addr;
int n;
if(argint(0, &n) < 0)
return -1;
addr = myproc()->sz;
myproc()->sz += n; //记得添加这一行,不然会报错
//if(growproc(n) < 0)
// return -1;
return addr;
}
2. Lazy allocation (moderate)
2.1 hints
您可以通过查看 usertrap() 中的 r_scause() 是 13 还是 15 来检查错误是否是页面错误。
r_stval() 返回 RISC-V stval 寄存器,其中包含导致页面错误的虚拟地址。
从 vm.c 中的 uvmalloc() 窃取代码,这就是 sbrk() 调用的内容(通过growproc())。 您需要调用 kalloc() 和 mappages()。
使用 PGROUNDDOWN(va) 将出错的虚拟地址向下舍入到页面边界。
uvmunmap() 会恐慌; 如果某些页面未映射,请将其修改为不恐慌。
如果内核崩溃,请在 kernel/kernel.asm 中查找 sepc
使用 pgtbl 实验室的 vmprint 函数打印页表的内容。
如果您看到错误“incomplete type proc”,请包含“spinlock.h”,然后包含“proc.h”。
2.2 实验流程
1. 在vm.c文件添加lazyalloc
uint64 lazyalloc(struct proc * p, uint64 va){
if(va >= p->sz || va < PGROUNDUP(p->trapframe->sp)){
return 0;
}
char * mem;
uint64 a = PGROUNDDOWN(va);
mem = kalloc();
if(mem == 0){
return 0;
}
memset(mem, 0, PGSIZE);
if(mappages(p->pagetable, a, PGSIZE, (uint64)mem, PTE_W|PTE_X|PTE_R|PTE_U) != 0){
kfree(mem);
return 0;
}
return (uint64)mem;
}
2. 在usertrap里面添加页处理错误
} else if((which_dev = devintr()) != 0){
// ok
} else if(r_scause() == 13 || r_scause() == 15) {
if (lazyalloc(myproc(), r_stval()) <= 0) {
p->killed = 1;
}
} else {
1.注释掉uvmnmap的panic
for(a = va; a < va + npages*PGSIZE; a += PGSIZE){
if((pte = walk(pagetable, a, 0)) == 0)
panic("uvmunmap: walk");
if((*pte & PTE_V) == 0)
//panic("uvmunmap: not mapped");
continue;
这里为啥要注释掉panic呢,我的理解是,因为是懒分配,所以有一些虚拟页表中的页,实际上并没有分配物理地址的,这个时候PTE_V为0,那么在uvmunmap取消映射的时候,就不该panic,而是跳过。
2.3 PTE 是什么
PTE是Page Table Entry的简称,翻译过来就是页表项,结构如图所示:
3. Lazytests and Usertests (moderate)
3.1 hints
处理负 sbrk() 参数。
如果进程在比 sbrk() 分配的任何内存地址高的虚拟内存地址上出现页面错误,则终止该进程。
正确处理 fork() 中的父对子内存复制。
处理进程将有效地址从 sbrk() 传递给系统调用(例如读取或写入)但尚未分配该地址的内存的情况。
正确处理内存不足:如果 kalloc() 在页面错误处理程序中失败,则终止当前进程。
处理用户堆栈下方无效页面上的错误。
3.1修改sbrk代码
uint64
sys_sbrk(void)
{
int addr;
int n;
struct proc *p = myproc();
if(argint(0, &n) < 0)
return -1;
addr = p->sz;
p->sz += n;
//if(growproc(n) < 0)
// return -1;
if(n < 0) {
uvmdealloc(p->pagetable, addr, p->sz); //删除页
}
return addr;
}
3.2 修改walkaddr代码,实现懒加载
uint64
walkaddr(pagetable_t pagetable, uint64 va)
{
pte_t *pte;
uint64 pa;
if(va >= MAXVA)
return 0;
pte = walk(pagetable, va, 0);
if(pte == 0)
goto lzac;
if((*pte & PTE_V) == 0)
goto lzac;
if((*pte & PTE_U) == 0)
goto lzac;
pa = PTE2PA(*pte);
if (0) { //注意这一行必须要有,因为有些情况是不需要lazyalloc的,不然报错init exiting
lzac:
if((pa = lazyalloc(myproc(), va)) <= 0){
pa = 0;
}
}
return pa;
}
其实我的总结就是,在需要的时候进行页面加载,并且消除掉因为缺页而导致的panic,缺页的情况,可能是用户write操作copyin,导致页错误
3.3 注释掉不必要的panic
所有case都过了
总结
不仔细读题,导致自己死得很难看。。。。 myproc()->sz += n; 这一行忘了加,所以我的教训就是,自己要做的事情,一定要弄得十分清楚,觉得奇怪的地方多问为什么
对事物的背景、条件、提示等,真的要在意
画图能够加深印象,记得更牢,理解更透彻