第一个任务是去阻止sysproc.c
中的sys_sbrk()
函数真的分配内存,只需要增p->sz
即可
一行代码+注释即可
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;
}
这时候会发生两个错误
r_scause
为页错误相关的标志修改trap.c
中的代码,使其能够对用户空间的页错误进行反应
通过映射一个新分配的物理内存页到出错的地址,并返回到用户空间使得进程继续执行
视情况修改其他的内核代码
r_scause
是13还是15来判断是否是一个页错误r_stval()
存储了引起错误的虚拟地址uvmalloc
函数学习,你需要调用kalloc
和mappages
PGROUNDDOWN(va)
去得到虚拟页面的地址uvmunmap()
使其在碰到一个页面没有被mapped时不要panicvmprint
函数去打印页表incomplete type proc
,去includespinlock.h
和proc.h
usertrap
中增加一个else if分支,关键操作就是kalloc和mappages,即申请一个操作,再增加一个映射
} else if (r_scause() == 13 || r_scause() == 15) {
uint64 va = r_stval();
// 虚拟地址本来就不合法
if (va >= p->sz) {
p->killed = 1;
} else {
uint64 pa = (uint64)kalloc();
// 内存用完了, 杀死进程
if (pa == 0) {
p->killed = 1;
} else {
va = PGROUNDDOWN(va);
// 增加映射,如果失败了
if (mappages(p->pagetable, va, PGSIZE, pa, PTE_W | PTE_X | PTE_R | PTE_U) != 0) {
kfree((void *)pa);
p->killed = 1;
}
}
}
} else {
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
中修改为
if ((*pte & PTE_V) == 0)
continue;
就成功了
lazytests
和usertests
fork
中父进程到子进程的内存拷贝一个一个hins来
第1个是参数为负数,特判一下就行,如果是负数,就按原来的方式处理
if (n > 0) {
myproc()->sz += n;
} else {
if (growproc(n) < 0)
return -1;
}
第2个和第6个通过这个判断就可以搞定
va >= p->sz || va < p->trapframe->sp
有个之前没搞清楚的点,那就是通过p->trapframe->sp取得的是栈顶指针,栈指针又是向下走的。而栈指针往下,除了一个guardpage是没有映射之外,其他的page都应该是有映射的,即pte的有效位为0。因此,如果引发了页故障并且地址还低于栈顶指针,说明肯定是碰到了guardpage的
第3个其实说的是uvmcopy
函数,这个函数就是将父进程的页表原模原样的拷贝给子进程,但是有的虚拟页面连父节点自己都没有映射,子节点哪来的映射,因此碰到找不到pte或者pte有问题的情况,直接continue就行了
if ((pte = walk(old, i, 0)) == 0)
continue;
// panic("uvmcopy: pte should exist");
if ((*pte & PTE_V) == 0)
continue;
// panic("uvmcopy: page not present");
第4个的问题在于,如果是系统调用,那它会先在usertrap中进入syscall的处理流程,不会进入我们新添加的流程,所以会有问题
通过对write函数的不断追踪,可以发现,牵扯到的是walkaddr函数。其实就是现在需要使用这个地址了,但是原始的walkaddr不能正确返回一个物理地址,因为压根就没有。所以在walkaddr中增加创建映射的代码即可
if ((*pte & PTE_V) == 0) {
struct proc *p = myproc();
// 地址不合法
if (va >= p->sz || va < p->trapframe->sp) {
return 0;
}
uint64 pa = (uint64)kalloc();
if (pa == 0) {
panic("memory run out\n");
return 0;
}
if (mappages(p->pagetable, va, PGSIZE, pa, PTE_W | PTE_X | PTE_R | PTE_U) != 0) {
kfree((void *)pa);
return 0;
}
// *pte = PA2PTE(pa) | PTE_W | PTE_X | PTE_R | PTE_U | PTE_V;
}
第5点,只要你用了kalloc后判断了它的返回值,然后panic就行