先说结论过了所有的测试点,我是能够过那个 usertests
,但是 lazytests
会一直卡在 out of mem
这个测试点,而且怎么也没法过,折腾了好久真的无语。
这个原因是因为 sbrk
是懒加载,我们直接用 addr+n
表示 proc 的 size。这样有个问题,xv6 最大的虚内存实际上是一个 MAXVA
,如果不加限制的话,walk 阶段就会 walk 到 MAXVA 导致一直 panic。解决办法其实很简单,在 uvmunmap 代码加个限定条件,强制 va 不能超过 MAXVA。
懒加载代码:
int lazy_alloc(uint64 addr){ | |
uint64 va = PGROUNDDOWN(addr); | |
struct proc* p = myproc(); | |
if(addr >= p->sz || addr < p->trapframe->sp ){ //addr 如果大于 size 或者越栈了就报错 | |
return -1; | |
}else { | |
char* mem = kalloc(); | |
if(mem == 0){ | |
return -1; | |
}else{ | |
memset(mem, 0, PGSIZE); | |
if(mappages(p->pagetable,va ,PGSIZE , (uint64)mem, PTE_W|PTE_X|PTE_R|PTE_U)!=0){ | |
kfree(mem); | |
uvmunmap(p->pagetable,va, 1, 1); | |
return -1; | |
} | |
} | |
return 0; | |
} | |
return -1; | |
} |
usertrap 修改:
else if(r_scause() == 15 || r_scause() == 13){ | |
uint64 addr = r_stval(); | |
if(lazy_alloc(addr) != 0){ | |
printf("usertrap(): unexpected scause %p pid=%d\n", r_scause(), p->pid); | |
printf(" sepc=%p stval=%p\n", r_sepc(), r_stval()); | |
p->killed = 1; | |
} |
修改 uvmunmap 代码:
void | |
uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free) | |
{ | |
... | |
for(a = va; a < va + npages*PGSIZE; a += PGSIZE){ | |
if(a >=MAXVA)break; // 一定要加这个条件,a 不可能超过最大虚内存 | |
if((pte = walk(pagetable, a, 0)) == 0) | |
/* panic("uvmunmap: walk"); */ | |
continue; | |
if((*pte & PTE_V) == 0) | |
/* panic("uvmunmap: not mapped"); */ | |
continue; | |
... | |
} | |
} |
walkaddr 修改,使得传入合法地址时候进行懒加载,讲道理这一步其实可以不用写的,我试了一下没有写这个 walkaddr 修改,照样也能跑通所有测试。证明了我的猜想,因为缺页中断发生在 load 等防存指令,如果地址不能被 walkaddr 说明发生了缺页,这时候硬件就会陷入 trap,然后进行处理。所以我们不在 walkaddr 中处理缺页也是没问题的,直接让 trap 来进行处理。但是 trap 是很贵的,每次我们要执行几百条甚至上千条指令,所以一个有效的解决办法就是 walkaddress 的时候也执行 lazy_alloc。而且有的函数比如 read、write 如果可能它没有在 walkaddress 中得到正确的 pa 可能也不会执行 trap 而是直接返回?因为不确定是否真的有这个页。。。所以还是在 walkaddress 这里吧 lazy_alloc 写一下:
if(pte == 0 || (*pte & PTE_V) ==0 ){ | |
if(lazy_alloc(va) != 0){ | |
return 0; | |
} | |
pte = walk(pagetable, va, 0); | |
} |
uvmcopy 修改,使得满足 fork:
if((pte = walk(old, i, 0)) == 0) | |
/* panic("uvmcopy: pte should exist"); */ | |
continue; | |
if((*pte & PTE_V) == 0) | |
/* panic("uvmcopy: page not present"); */ | |
continue; |
最后就是 sbrk 代码了:
uint64 | |
sys_sbrk(void) | |
{ | |
int addr; | |
int n; | |
if(argint(0, &n) < 0) | |
return -1; | |
addr = myproc()->sz; | |
if(n > 0){ | |
myproc()->sz = addr + n; | |
}else{ | |
uvmdealloc(myproc()->pagetable, addr, addr +n); | |
myproc()->sz = addr + n; | |
} | |
/* if(growproc(n) < 0) */ | |
/* return -1; */ | |
return addr; | |
} |
结果:
这个 lab 虽然只是中等难度但是我写的时候很挣扎。。。。