Lab 5
删除 sbrk的原调用,并添加 lazy allocated,也就是只增长(减小) p->sz,不做立即的页面分配。
uint64
sys_sbrk(void)
{
int addr;
int n;
if(argint(0, &n) < 0)
return -1;
struct proc* p = myproc();
uint64 sz = p->sz;
addr = p->sz;
if(n > 0) {
p->sz += n;
} else if(sz + n > 0) {
// 这种情况是 n < 0,但n + sz > 0的情况,是合法的,需要减小页面
sz = uvmdealloc(p->pagetable, sz, sz + n);
p->sz = sz;
} else {
return -1;
}
return addr;
}
修改usertrap()来解决 sbrk()调用页错误的情况:也就是r_scause() == 13 || 15的情况
void
usertrap(void)
{
int which_dev = 0;
if((r_sstatus() & SSTATUS_SPP) != 0)
panic("usertrap: not from user mode");
// send interrupts and exceptions to kerneltrap(),
// since we're now in the kernel.
w_stvec((uint64)kernelvec);
struct proc *p = myproc();
// save user program counter.
p->trapframe->epc = r_sepc();
if(r_scause() == 8){
// system call
if(p->killed)
exit(-1);
// sepc points to the ecall instruction,
// but we want to return to the next instruction.
p->trapframe->epc += 4;
// an interrupt will change sstatus &c registers,
// so don't enable until done with those registers.
intr_on();
syscall();
} else if((which_dev = devintr()) != 0){
// ok
} else if(r_scause() == 15 || r_scause() == 13) {
// char *mem;
// uint64 errAddr = r_stval();
// mem = (char *)kalloc();
// if(mem == 0) {
// printf("usertrap:kalloc()");
// p->killed = 1;
// } else if(errAddr < p->sz){
// memset(mem,0,PGSIZE);
// uint64 va = PGROUNDDOWN(errAddr);
// if(mappages(p->pagetable, va, PGSIZE, (uint64)mem, PTE_W|PTE_X|PTE_R|PTE_U) != 0){
// kfree(mem);
// printf("usertrap(): mappages\n");
// p->killed = 1;
// }
// } else {
// p->killed = 1;
// kfree(mem);
// printf("usertrap(): unknow error\n");
// }
char *mem;
uint64 errAddr = r_stval();
if(errAddr < p->sz && PGROUNDUP(p->trapframe->sp) - 1 < errAddr && (mem = kalloc()) != 0) {
memset(mem,0,PGSIZE);
uint64 va = PGROUNDDOWN(errAddr);
if(mappages(p->pagetable, va, PGSIZE, (uint64)mem, PTE_W|PTE_X|PTE_R|PTE_U) != 0){
kfree(mem);
printf("usertrap(): mappages\n");
p->killed = 1;
}
} else {
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;
}
if(p->killed)
exit(-1);
// give up the CPU if this is a timer interrupt.
if(which_dev == 2)
yield();
usertrapret();
}
按照错误提示来修改 xv6即可:
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){
// 因为是lazy allocate,这个页可能还没被分配,因此不能panic
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;
}
}
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;
}
处理这种情形:进程从
sbrk()
向系统调用(如read
或write
)传递有效地址,但尚未分配该地址的内存。
int
argaddr(int n, uint64 *ip)
{
*ip = argraw(n);
struct proc *p = myproc();
if(walkaddr(p->pagetable,*ip) == 0) {
if (*ip < p->sz) {
char *mem = (char *)kalloc();
if(!mem) {
return -1;
}
memset(mem,0,PGSIZE);
uint64 va = PGROUNDDOWN(*ip);
if(mappages(p->pagetable, va, PGSIZE, (uint64)mem, PTE_W|PTE_X|PTE_R|PTE_U) != 0){
kfree(mem);
return -1;
}
} else {
return -1;
}
}
return 0;
}
Last updated