Eliminate allocation from sbrk()
这部分很简单
直接修改函数即可
但要考虑到传入的n是负数的情况
uint64
sys_sbrk(void)
{
int addr;
int n;
if(argint(0, &n) < 0)
return -1;
struct proc* p=myproc();
addr = p->sz;
if(addr+n>MAXVA || addr+n<0)
return addr;
p->sz=addr+n;
if(n<0)
{
uvmdealloc(p->pagetable, addr, p->sz);
}
// if(growproc(n) < 0)
// return -1;
return addr;
}
处理负数情况不能直接调用growproc() 会改变p->sz
Lazy allocation
这部分的 hint其实只是给出遇到问题后去怎么解决 总体思路就是实现lazy allocation
我建议就是想到一个得修改的地方 写上 然后调试去找新的错误
lab的最后一部分
Lazytests and Usertests
其实就是要你一步步实现完善的
所以第二个和第三个要一起做
第二个hints是叫你遇到故障怎么班
第三个hints就是你要handle的地方 也就是坑
下面贴出要修改的函数
这是usertrap的部分
} else if (r_scause() == 13 || r_scause() == 15)
{
uint64 va = r_stval();
if(va > p->sz || va <=PGROUNDDOWN( p->trapframe->sp))
{
p->killed=1;
}
//printf("page fault: %p\n",va);
else{
uint64 ka = (uint64)kalloc();
if(ka == 0)
{
p->killed=1;
} else{
memset((void*)ka,0,PGSIZE);
va=PGROUNDDOWN(va);
if(mappages(p->pagetable,va,PGSIZE,ka,PTE_W|PTE_U|PTE_R)!=0)
{
kfree((void*)ka);
p->killed=1;
}
}}
}
注意 va 边界条件 然后每个可以会返回错误值的函数一定要判断
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)
{
//vmprint(pagetable);
// printf("%p\n",a);
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;
}
}
这里是是把pte为0的情况 和pte无效的情况变成continue
以为如果不是lazy allocation的话 根本不会去unmap这样的pte
只有lazy allocation 才能去unmap没有分配空间而导致pte无效的情况
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)
// panic("uvmcopy: pte should exist");
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;
}
修改原因同上 主要是处理fork()
int
mappages(pagetable_t pagetable, uint64 va, uint64 size, uint64 pa, int perm)
{
uint64 a, last;
pte_t *pte;
a = PGROUNDDOWN(va);
last = PGROUNDDOWN(va + size - 1);
for(;;){
if((pte = walk(pagetable, a, 1)) == 0)
return -1;
if(*pte & PTE_V)
// panic("remap");
continue;
*pte = PA2PTE(pa) | perm | PTE_V;
if(a == last)
break;
a += PGSIZE;
pa += PGSIZE;
}
return 0;
}
这是跑测试用例的时候发现的 从别的函数中调用的mappages()
void
freewalk(pagetable_t pagetable)
{
// there are 2^9 = 512 PTEs in a page table.
for(int i = 0; i < 512; i++){
pte_t pte = pagetable[i];
if((pte & PTE_V) && (pte & (PTE_R|PTE_W|PTE_X)) == 0){
// this PTE points to a lower-level page table.
uint64 child = PTE2PA(pte);
freewalk((pagetable_t)child);
pagetable[i] = 0;
} else if(pte & PTE_V){
// panic("freewalk: leaf");
continue;
}
}
kfree((void*)pagetable);
}
同理
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 ka = (uint64)kalloc();
if(ka == 0)
{
return 0;
//p->killed=1;
} else{
memset((void*)ka,0,PGSIZE);
//va=PGROUNDDOWN(va);
if(mappages(p->pagetable,va,PGSIZE,ka,PTE_W|PTE_U|PTE_R|PTE_X)!=0)
{
kfree((void*)ka);
p->killed=1;
}
}
return PTE2PA(*walk(pagetable, va, 0));
}
if((*pte & PTE_U) == 0)
return 0;
pa = PTE2PA(*pte);
return pa;
}
这是write 和 read 系统调用的底层函数
需要重新写分配内存空间的代码
因为不会进入到trap中处理
注意我们这里的虚拟地址是具体的 一定不要舍入
这部分代码还是需要思考的
下面是做lab的思考和调试手段
跟着测试用例来去修改函数确实很省时间 总有思考不到的地方会panic
没有用到hints说的 vmprint
kernel 也没有crash过
主要就是调内核和用户代码
usertests代码太长 可以把没通过的函数单独放.c文件来调
其实可以用backtrace 但我没用
总体来说用例还是很严格的