xv6 lab5 lazy 1.eliminate allocation from sbrk()uint64 sys_sbrk(void) { int addr; int n; if(argint(0, n) 0) return -1; addr myproc()-sz; myproc()-sz n; //虚拟增加size而不实际分配物理内存 //if(growproc(n) 0) //return -1; return addr; }hart 1 starting init: starting sh $ echo hi usertrap(): unexpected scause 0x000000000000000f pid3 sepc0x00000000000012ac stval0x0000000000004008 panic: uvmunmap: not mapped QEMU: Terminatedelse { 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; } //用户态发生异常进入usertrap对比scause不是系统调用和硬件中断进入这里讲一下异常链路shell 调用 sbrk 只改 p-sz无物理页映射用户写未映射地址 → 硬件抛出 scause15 写缺页usertrap 无缺页处理分支打印 unexpected scause调用exit-1会销毁当前进程全部资源关闭文件、释放内存、回收 PCB。调用uvmfree-uvmunmap检测是否有效位等打印not mapped执行panic。2.lazy allocation要求在usertrap 添加缺页处理分支在出错地方映射新的物理内存然后返回用户态继续执行。可以通过查看kernel/trap.c中的usertrap()中的r_scause()是13还是15来检查错误是否是页面错误r_stval()返回stval寄存器的值该寄存器存放着导致页面错误的虚拟地址参考vm.c中的uvmalloc()函数代码通过调用kalloc()和mappages()来实现本实验使用PGROUNDDOWN(va)将错误虚拟地址舍入到页面边界uvmunmap()将崩溃修改这个函数使得在某些页面未映射的时候不会崩溃如果内核崩溃在kernel/kernel.asm中查找sepc使用pgtbl实验中vmprint函数打印页表的内容如果产生“incomplete type proc”的错误请先include “spinklock.h再proc.h”要在usertrap加额外的逻辑处理缺页异常。syscall(); }else if(r_scause()13 || r_scause()15){ //13是读异常15是写异常 printf(page fault trap: signal %d at address %p\n, r_scause(), r_stval()); uint64 va PGROUNDDOWN(r_stval());//获取缺页异常的虚拟地址页起始地址 uint64 pa (uint64)kalloc();//分配物理内存地址 if (pa 0)//分配失败则杀死进程 { p-killed 1; }else{ memset((void*)pa, 0, PGSIZE); //将分配的物理内存清零 if (mappages(p-pagetable, va, PGSIZE, pa, PTE_W|PTE_R|PTE_U) ! 0) //在页表建立映射用户态可读可写 { kfree((void*)pa); //映射失败则释放物理内存并杀死进程 p-killed 1; } } }else if((which_dev devintr()) ! 0){还得在vm.c的uvmunmap函数无效位以及walk获取pte去除因为sbrk可能size虚拟的较大而惰性分配只在真正访问某一页时才创建映射。而进程退出时会调用uvmunmap释放页表。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;//添加 //panic(uvmunmap: walk); if((*pte PTE_V) 0) continue;//添加 //panic(uvmunmap: not mapped); if(PTE_FLAGS(*pte) PTE_V) panic(uvmunmap: not a leaf); if(do_free){ uint64 pa PTE2PA(*pte); kfree((void*)pa); } *pte 0; } }3.Lazytests and Usertests要求继续修改代码修改sbrk()以应对负参数的情况如果进程出现页面错误的虚拟地址高于sbrk分配的虚拟内存地址那么中止该进程正确处理fork中的父子内存副本这里应该是实现写时复制的fork;进程能成功应对当sbrk分配的有效虚拟地址被传递给系统调用但尚未分配该物理地址内存的情况。处理物理内存不足的情况当klloc()在页面错误情况下内存分配失败则终止当前进程处理页面错误虚拟地址出现在栈下方时候的情况这个时候的虚拟地址是无效1.处理sbrk负数情况sys_sbrk(void) { int addr; int n; if(argint(0, n) 0) return -1; addr myproc()-sz; if(n 0){ myproc()-sz n; //虚拟增加size而不实际分配物理内存 }else if (myproc()-sz n 0) { return -1; }else{ int sz uvmdealloc(myproc()-pagetable, myproc()-sz, myproc()-sz n); //uvmdealloc解除映射返回新的进程size myproc()-sz sz; } //if(growproc(n) 0) //return -1; return addr; }2.usertrap分支再加判断syscall(); }else if(r_scause()13 || r_scause()15){ //13是读异常15是写异常 //printf(page fault trap: signal %d at address %p\n, r_scause(), r_stval()); uint64 va PGROUNDDOWN(r_stval());//获取缺页异常的虚拟地址页起始地址 if(va PGSIZE - 1 p-sz va PGROUNDDOWN(p-trapframe-sp)){ //保证页面报错的虚拟地址只能在栈上面和p-sz的下面 uint64 pa (uint64)kalloc();//分配物理内存地址 if (pa 0)//分配失败则杀死进程 { p-killed 1; }else{ memset((void*)pa, 0, PGSIZE); //将分配的物理内存清零 if (mappages(p-pagetable, va, PGSIZE, pa, PTE_W|PTE_R|PTE_U) ! 0) //在页表建立映射用户态可读可写 { kfree((void*)pa); //映射失败则释放物理内存并杀死进程 p-killed 1; } } }else{ p-killed 1; //不在范围内则杀死进程 } }else if((which_dev devintr()) ! 0){ // ok } 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; }3.fork里的uvmcopy出现wlak获取pte为0及无效位continueint 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) //panic(uvmcopy: page not present); 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;