MIT xv6 2020系列实验:Lab5 lazy
这个和cow实验联系很紧密,并且cow比这个实验恶心一些的样子。
任务一:sbrk假分配
在sbrk中取消随栈指针变化对内存的分配/回收,只变化相应的栈指针。
这个比较简单,进入sbrk调用的growproc中去掉uvmalloc与uvmdealloc的部分即可。
任务二:Lazy allocation
提示:
13为读缺页,15为写缺页
r_stval() 返回寄存器 staval 的值, 该寄存器存储着引起缺页的虚拟地址
对于页面分配,你可以参考 growproc() 和 uvmalloc() . 你将会调用 kalloc() 和 mappages()使用 PGROUNDDOWN(va) 将出错的虚拟地址向下舍入到页面边界。
如果一个缺页发生在高于 sys_sbrk() 分配的虚拟地址,杀死这个进程只处理用户空间下的缺页错误
思路:
1.先处理缺页异常,在缺页异常中进行uvmalloc为页表项分配内存块
2.去除缺页异常
3.处理sbrk的负增长情况
4.其他事情
为缺页位置分配映射的物理页:
else if(r_scause() == 13 || r_scause() == 15){
uint64 page_loc = r_stval();
struct proc* p = myproc();
if(p->sz <= page_loc || page_loc <= PGROUNDDOWN(p->trapframe->sp)){
p->killed = 1;
}
else{
// vmprint(p->pagetable);
char *mem;
page_loc = PGROUNDDOWN(page_loc);
mem = kalloc();
if(mem == 0) p->killed = 1;
else{
memset(mem, 0, PGSIZE);
// printf("?ohmy %d",r_scause());
if(mappages(p->pagetable, page_loc, PGSIZE, (uint64)mem, PTE_W|PTE_X|PTE_R|PTE_U) != 0)
p->killed = 1;
// printf("%x ?? %p %p %p\n",r_scause(),page_loc,(uint64)*walk(p->pagetable,page_loc,0),((uint64)*walk(p->pagetable,page_loc,0)&(uint64)(PTE_W)));
}
}
// vmprint(p->pagetable);
}
因为现在允许缺页,因此uvmunmap处的panic需要清除:
void
uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free)
{
uint64 a;
pte_t *pte;
if((va % PGSIZE) != 0)
panic("uvmunmap: not aligned");
for(a = va; a < va + npages*PGSIZE; a += PGSIZE){
if((pte = walk(pagetable, a, 0)) == 0)
continue;
if((*pte & PTE_V) == 0)
continue;
if(PTE_FLAGS(*pte) == PTE_V)
panic("uvmunmap: not a leaf");
if(do_free){
uint64 pa = PTE2PA(*pte);
kfree((void*)pa);
}
*pte = 0;
}
}
两处改为conitnue。
int
uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
{
pte_t *pte;
uint64 pa, i;
uint flags;
char *mem;
for(i = 0; i < sz; i += PGSIZE){
if((pte = walk(old, i, 0)) == 0)
continue;
if((*pte & PTE_V) == 0)
continue;
pa = PTE2PA(*pte);
flags = PTE_FLAGS(*pte);
if((mem = kalloc()) == 0)
goto err;
memmove(mem, (char*)pa, PGSIZE);
if(mappages(new, i, PGSIZE, (uint64)mem, flags) != 0){
kfree(mem);
goto err;
}
}
return 0;
err:
uvmunmap(new, 0, i / PGSIZE, 1);
return -1;
}
uvmcopy处缺页也需要修改
处理sbrk负增长:
uint64
sys_sbrk(void)
{
int addr;
int n;
if(argint(0, &n) < 0)
return -1;
addr = myproc()->sz;
struct proc *p = myproc();
uint64 newaddr = addr+n;
if(newaddr >= MAXVA || newaddr <= 0)
return addr;
if(n<0){
if (uvmdealloc(p->pagetable, addr, newaddr) != newaddr)
return -1;
}
p->sz = newaddr;
return addr;
}
最后一个坑点是,因为只在usertrap中处理了缺页异常,如果是系统调用,那么这部分异常是没有办法处理的。
这部分缺页只在read,write系统调用中会出现,也就是copyout处,这里修改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 || (*pte & PTE_V) == 0)
{
struct proc *p = myproc();
if (va >= p->sz || va < PGROUNDUP(p->trapframe->sp)) return 0;
uint64 pa = (uint64)kalloc();
if (pa == 0) return 0;
if (mappages(p->pagetable, va, PGSIZE, pa, PTE_W|PTE_X|PTE_R|PTE_U) != 0)
{
kfree((void*)pa);
return 0;
}
return pa;
}
if((*pte & PTE_U) == 0)
return 0;
pa = PTE2PA(*pte);
return pa;
}
然后就可通过测试了。