Xv6 Lab: lazy page allocation

854 阅读1分钟

「这是我参与11月更文挑战的第25天,活动详情查看:2021最后一次更文挑战

Meaning Unknown's Head Image

Lab: xv6 lazy page allocation

pdos.csail.mit.edu/6.S081/2020…

新的 2020 版哦。

$ git fetch
$ git checkout lazy
$ make clean

Eliminate allocation from sbrk()

这道题就是把 sys_sbrk 里的 growproc 调用删了,等用到的时候再去分配内存。如果是空间减小,要取消分配。

uint64
sys_sbrk(void)
{
  int addr;
  int n;
  struct proc *p = myproc();  //(+)

  if(argint(0, &n) < 0)
    return -1;
  addr = p->sz;  // old sz
  p->sz += n;
  if (n < 0) {  // 空间减小: 取消分配
    uvmdealloc(p->pagetable, addr, p->sz);
  }
  return addr;
}

Lazy allocation

vm.c 里面实现惰性分配(莫忘在 defs.h 中声明函数):

#include "spinlock.h" //(+)
#include "proc.h"     //(+)

// lazy allocation memory va for proc p: handle page-fault.
// return allocated memory (pa), 0 for failed 
uint64 lazyalloc(struct proc * p, uint64 va){
  if(va >= p->sz || va < PGROUNDUP(p->trapframe->sp)){
    return 0;
  }
  char * mem;
  uint64 a = PGROUNDDOWN(va);
  mem = kalloc();
  if(mem == 0){
    return 0;
  }
  memset(mem, 0, PGSIZE);  
    if(mappages(p->pagetable, a, PGSIZE, (uint64)mem, PTE_W|PTE_X|PTE_R|PTE_U) != 0){
      kfree(mem);
      return 0;
    }

  return (uint64)mem;
}

在 usertrap (trap.c) 里处理缺页错误,尝试惰性分配(掉上面写的那个函数,失败就杀掉进程):

void
usertrap(void)
{
  ...
  if(r_scause() == 8){
    // system call
    ...
  } else if((which_dev = devintr()) != 0){
    // ok
  } else if((r_scause() == 13) || (r_scause() == 15)){  // page fault: (+)
  if (lazyalloc(myproc(), r_stval()) <= 0) {
    p->killed = 1;
  }
  } else {
    ...
  }
  ...
}

最后改一点点细节,把各种缺页会爆出的 panic 干掉(vm.c里)。以前这些情况正常是不会发生的,但现在惰性分配会带来缺页,所以要告诉操作系统遇到这些事情时 don't panic,继续往下跑就行了:

(这里的代码我懒得改结构就上goto了,但你应该去改if结构,而不是goto)

// vm.c
...

//(+)
uint64
walkaddr(pagetable_t pagetable, uint64 va)
{
  ...

  if(pte == 0)
    goto lzac;
  if((*pte & PTE_V) == 0)
    goto lzac;
  if((*pte & PTE_U) == 0)
    goto lzac;
  pa = PTE2PA(*pte);
 
  if (0) {
lzac:
    if ((pa = lazyalloc(myproc(), va)) <= 0)
      pa = 0;
  }
  
  return pa;
}

int
mappages(pagetable_t pagetable, uint64 va, uint64 size, uint64 pa, int perm)
{
  ...
  for(;;){
    ...
    if(*pte & PTE_V)
      // panic("remap");
      ;
    ...
}

void
uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free)
{
  ...
  for(a = va; a < va + npages*PGSIZE; a += PGSIZE){
    if((pte = walk(pagetable, a, 0)) == 0) {
      // panic("uvmunmap: walk");
      continue;
    }
    if((*pte & PTE_V) == 0) {
      // panic("uvmunmap: not mapped");
      continue;
    }
    ...
  }
}
    
int
uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
{
  ...
  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;
    }
    ...
  }
  ...
}

测试

写的时候可以按题目跑这些测试:

xv6-labs-2020 $ make qemu
$ echo hi
$ lazytests
$ usertests

diff

详细的 git diff (可以用来做 patch 喔):

diff --git a/kernel/defs.h b/kernel/defs.h
index 4b9bbc0..7ff16c4 100644
--- a/kernel/defs.h
+++ b/kernel/defs.h
@@ -171,6 +171,7 @@ uint64          walkaddr(pagetable_t, uint64);
 int             copyout(pagetable_t, uint64, char *, uint64);
 int             copyin(pagetable_t, char *, uint64, uint64);
 int             copyinstr(pagetable_t, char *, uint64, uint64);
+uint64          lazyalloc(struct proc * p, uint64 va);
 
 // plic.c
 void            plicinit(void);
diff --git a/kernel/sysproc.c b/kernel/sysproc.c
index e8bcda9..b799722 100644
--- a/kernel/sysproc.c
+++ b/kernel/sysproc.c
@@ -43,12 +43,15 @@ sys_sbrk(void)
 {
   int addr;
   int n;
+  struct proc *p = myproc();
 
   if(argint(0, &n) < 0)
     return -1;
-  addr = myproc()->sz;
-  if(growproc(n) < 0)
-    return -1;
+  addr = p->sz;  // old sz
+  p->sz += n;
+  if (n < 0) {
+    uvmdealloc(p->pagetable, addr, p->sz);
+  }
   return addr;
 }
 
diff --git a/kernel/trap.c b/kernel/trap.c
index a63249e..fc08231 100644
--- a/kernel/trap.c
+++ b/kernel/trap.c
@@ -67,6 +67,11 @@ usertrap(void)
     syscall();
   } else if((which_dev = devintr()) != 0){
     // ok
+  } else if((r_scause() == 13) || (r_scause() == 15)){  // page fault
+	// lazy allocation
+	if (lazyalloc(myproc(), r_stval()) <= 0) {
+	  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());
diff --git a/kernel/vm.c b/kernel/vm.c
index bccb405..f3235c3 100644
--- a/kernel/vm.c
+++ b/kernel/vm.c
@@ -5,6 +5,8 @@
 #include "riscv.h"
 #include "defs.h"
 #include "fs.h"
+#include "spinlock.h"
+#include "proc.h"
 
 /*
  * the kernel's page table.
@@ -102,12 +104,19 @@ walkaddr(pagetable_t pagetable, uint64 va)
 
   pte = walk(pagetable, va, 0);
   if(pte == 0)
-    return 0;
+	goto lzac;
   if((*pte & PTE_V) == 0)
-    return 0;
+    goto lzac;
   if((*pte & PTE_U) == 0)
-    return 0;
+    goto lzac;
   pa = PTE2PA(*pte);
+ 
+  if (0) {
+lzac:
+	  if ((pa = lazyalloc(myproc(), va)) <= 0)
+		pa = 0;
+  }
+  
   return pa;
 }
 
@@ -157,7 +166,8 @@ mappages(pagetable_t pagetable, uint64 va, uint64 size, uint64 pa, int perm)
     if((pte = walk(pagetable, a, 1)) == 0)
       return -1;
     if(*pte & PTE_V)
-      panic("remap");
+      // panic("remap");
+	  ;
     *pte = PA2PTE(pa) | perm | PTE_V;
     if(a == last)
       break;
@@ -180,10 +190,14 @@ uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free)
     panic("uvmunmap: not aligned");
 
   for(a = va; a < va + npages*PGSIZE; a += PGSIZE){
-    if((pte = walk(pagetable, a, 0)) == 0)
-      panic("uvmunmap: walk");
-    if((*pte & PTE_V) == 0)
-      panic("uvmunmap: not mapped");
+    if((pte = walk(pagetable, a, 0)) == 0) {
+      // panic("uvmunmap: walk");
+	  continue;
+	}
+    if((*pte & PTE_V) == 0) {
+      // panic("uvmunmap: not mapped");
+	  continue;
+	}
     if(PTE_FLAGS(*pte) == PTE_V)
       panic("uvmunmap: not a leaf");
     if(do_free){
@@ -314,10 +328,14 @@ uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
   char *mem;
 
   for(i = 0; i < sz; i += PGSIZE){
-    if((pte = walk(old, i, 0)) == 0)
-      panic("uvmcopy: pte should exist");
-    if((*pte & PTE_V) == 0)
-      panic("uvmcopy: page not present");
+    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)
@@ -440,3 +458,40 @@ copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max)
     return -1;
   }
 }
+
+
+// lazy allocation memory va for proc p: handle page-fault.
+// return allocated memory (pa), 0 for failed 
+uint64 lazyalloc(struct proc * p, uint64 va){
+#define lazyalloc_debug 0
+#define lazyalloc_warn(info) { \
+	  printf("lazyalloc(): %s", info); \
+      printf("             scause %p pid=%d\n", r_scause(), p->pid); \
+      printf("             sepc=%p stval=%p\n", r_sepc(), r_stval()); \
+}
+	if(va >= p->sz || va < PGROUNDUP(p->trapframe->sp)){
+	  #if lazyalloc_debug
+	    lazyalloc_warn("vm addr higher then any allocated with sbrk\n");
+      #endif
+	  return 0;
+	}
+	char * mem;
+	uint64 a = PGROUNDDOWN(va);
+	mem = kalloc();
+	if(mem == 0){
+	  #if lazyalloc_debug
+	    lazyalloc_warn("kalloc() == 0\n");
+      #endif
+	  return 0;
+	}
+	memset(mem, 0, PGSIZE);  
+    if(mappages(p->pagetable, a, PGSIZE, (uint64)mem, PTE_W|PTE_X|PTE_R|PTE_U) != 0){
+	  #if lazyalloc_debug
+ 	    lazyalloc_warn("mappages() != 0\n");
+      #endif
+	  kfree(mem);
+	  return 0;
+    }
+
+	return (uint64)mem;
+}

结果

最后 make grade 看成绩了:

xv6-labs-2020 $ make grade
...
== Test running lazytests == 
$ make qemu-gdb
(7.7s) 
== Test   lazy: map == 
  lazy: map: OK 
== Test   lazy: unmap == 
  lazy: unmap: OK 
== Test usertests == 
$ make qemu-gdb
(145.8s) 
== Test   usertests: pgbug == 
  usertests: pgbug: OK 
== Test   usertests: sbrkbugs == 
  usertests: sbrkbugs: OK 
== Test   usertests: argptest == 
  usertests: argptest: OK 
== Test   usertests: sbrkmuch == 
... OK ...
== Test   usertests: dirfile == 
  usertests: dirfile: OK 
== Test   usertests: iref == 
  usertests: iref: OK 
== Test   usertests: forktest == 
  usertests: forktest: OK 
== Test time == 
time: OK 
Score: 119/119

CDFMLR

顶部图片来自于网络,系随机选取的图片,仅用于检测屏幕显示的机械、光电性能,与文章的任何内容及观点无关,也并不代表本人局部或全部同意、支持或者反对其中的任何内容及观点。如有侵权,联系删除。