Skip to content

Simplify copyin/copyinstr(hard)

将定义在kernel/vm.c中的copyin的主题内容替换为对copyin_new的调用

(在kernel/vmcopyin.c中定义)

对copyinstr和copyinstr_new执行相同的操作。

diff --git a/kernel/vm.c b/kernel/vm.c
index 0984442..ad8ca06 100644
--- a/kernel/vm.c
+++ b/kernel/vm.c

@@ -427,23 +427,7 @@ copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len)
 int
 copyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len)
 {
  uint64 n, va0, pa0;

  while(len > 0){
    va0 = PGROUNDDOWN(srcva);
    pa0 = walkaddr(pagetable, va0);
    if(pa0 == 0)
      return -1;
    n = PGSIZE - (srcva - va0);
    if(n > len)
      n = len;
    memmove(dst, (void *)(pa0 + (srcva - va0)), n);

    len -= n;
    dst += n;
    srcva = va0 + PGSIZE;
  }
  return 0;
  return copyin_new(pagetable, dst, srcva, len);
 }
 
 // Copy a null-terminated string from user to kernel.
@@ -453,40 +437,7 @@ copyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len)
 int
 copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max)
 {
  uint64 n, va0, pa0;
  int got_null = 0;

  while(got_null == 0 && max > 0){
    va0 = PGROUNDDOWN(srcva);
    pa0 = walkaddr(pagetable, va0);
    if(pa0 == 0)
      return -1;
    n = PGSIZE - (srcva - va0);
    if(n > max)
      n = max;

    char *p = (char *) (pa0 + (srcva - va0));
    while(n > 0){
      if(*p == '\0'){
        *dst = '\0';
        got_null = 1;
        break;
      } else {
        *dst = *p;
      }
      --n;
      --max;
      p++;
      dst++;
    }

    srcva = va0 + PGSIZE;
  }
  if(got_null){
    return 0;
  } else {
    return -1;
  }
  return copyinstr_new(pagetable, dst, srcva, max);
 }
 
 void

在内核更改进程的用户映射的每一处,都以相同的方式更改进程的内核页表。

包括fork(), exec(), 和sbrk().

既然如此使用相同的方式,先写一个复制函数

复制函数

diff --git a/kernel/vm.c b/kernel/vm.c
index 0984442..ad8ca06 100644
--- a/kernel/vm.c
+++ b/kernel/vm.c

@@ -524,4 +475,27 @@ void
 vmprint(pagetable_t pagetable){
   printf("page table %p\n", pagetable);
   vmprint_sub(pagetable, 0);
}

int u2kvmcopy(pagetable_t upagetable, pagetable_t kpagetable, uint64 begin, uint64 end) {
    pte_t *pte;
    uint64 pa, i;
    uint flags;
    uint64 begin_page = PGROUNDUP(begin);    // 向上取整
    for(i = begin_page; i < end; i += PGSIZE){
        if((pte = walk(upagetable, i, 0)) == 0)
            panic("uvmcopy2kvm: pte should exist");
        if((*pte & PTE_V) == 0)
            panic("uvmcopy2kvm: page not present");
        pa = PTE2PA(*pte);
        flags = PTE_FLAGS(*pte) & (~PTE_U); // clear PTE_U flag
        if(mappages(kpagetable, i, PGSIZE, pa, flags) != 0){
            goto err;
        }
    }
    return 0;

err:
    uvmunmap(kpagetable, begin_page, (i- begin_page) / PGSIZE, 0);
    return -1;
 }
\ No newline at end of file

添加到fork()

diff --git a/kernel/proc.c b/kernel/proc.c
index ad394df..9e246cd 100644
--- a/kernel/proc.c
+++ b/kernel/proc.c

@@ -315,6 +326,12 @@ fork(void)
   }
   np->sz = p->sz;
 
  if(u2kvmcopy(np->pagetable, np->prockpt, 0, np->sz) < 0) {
    freeproc(np);
    release(&np->lock);
    return -1;
  }

   np->parent = p;
 
   // copy saved user registers.

添加到exec()

diff --git a/kernel/exec.c b/kernel/exec.c
index fc832f7..f97826b 100644
--- a/kernel/exec.c
+++ b/kernel/exec.c

@@ -116,6 +116,11 @@ exec(char *path, char **argv)
   p->trapframe->sp = sp; // initial stack pointer
   proc_freepagetable(oldpagetable, oldsz);
 
  uvmunmap(p->prockpt, 0, PGROUNDUP(oldsz)/PGSIZE, 0);
  if(u2kvmcopy(p->pagetable, p->prockpt, 0, p->sz) < 0){
      goto bad;
  }

   if(p->pid==1) {
     vmprint(p->pagetable);
   }

修改 growproc()

sbrk() 函数即系统调用 sys_brk() 函数, 最终会调用 kernel/proc.c 中的 growproc() 函数, 用来增长或减少虚拟内存空间

diff --git a/kernel/proc.c b/kernel/proc.c
index ad394df..9e246cd 100644
--- a/kernel/proc.c
+++ b/kernel/proc.c

@@ -283,11 +285,20 @@ growproc(int n)
 
   sz = p->sz;
   if(n > 0){
    if ((sz + n) > PLIC){
      return -1;
    }
     if((sz = uvmalloc(p->pagetable, sz, sz + n)) == 0) {
       return -1;
     }
    if(u2kvmcopy(p->pagetable, p->prockpt, p->sz, sz) < 0){
      return -1;
    }
   } else if(n < 0){
     sz = uvmdealloc(p->pagetable, sz, sz + n);
    if (PGROUNDUP(sz) < PGROUNDUP(p->sz)) {
      uvmunmap(p->prockpt, PGROUNDUP(sz), (PGROUNDUP(p->sz) - PGROUNDUP(sz)) / PGSIZE, 0);
    }
   }
   p->sz = sz;
   return 0;

修改userinit()

userinit() 的作用是初始化 xv6 启动的第一个用户进程, 进程的加载是独立的, 需要将其用户页表拷贝到内核页表.

diff --git a/kernel/proc.c b/kernel/proc.c
index ad394df..9e246cd 100644
--- a/kernel/proc.c
+++ b/kernel/proc.c
@@ -261,6 +261,8 @@ userinit(void)
   uvminit(p->pagetable, initcode, sizeof(initcode));
   p->sz = PGSIZE;
 
  u2kvmcopy(p->pagetable, p->prockpt, 0, p->sz);

   // prepare for the very first "return" from kernel to user.
   p->trapframe->epc = 0;      // user program counter
   p->trapframe->sp = PGSIZE;  // user stack pointer

将需要的函数定义添加到 kernel/defs.h 中

diff --git a/kernel/defs.h b/kernel/defs.h
index 2e302d5..33f1ff5 100644
--- a/kernel/defs.h
+++ b/kernel/defs.h
@@ -182,6 +182,11 @@ int             copyout(pagetable_t, uint64, char *, uint64);
 int             copyin(pagetable_t, char *, uint64, uint64);
 int             copyinstr(pagetable_t, char *, uint64, uint64);
 void            vmprint(pagetable_t);
int             u2kvmcopy(pagetable_t, pagetable_t, uint64, uint64);

// vmcopyin.c
int             copyin_new(pagetable_t, char *, uint64, uint64);
int             copyinstr_new(pagetable_t, char *, uint64, uint64);
 
 // plic.c
 void            plicinit(void);

diff --git a/kernel/vm.c b/kernel/vm.c
index 0984442..ad8ca06 100644
--- a/kernel/vm.c
+++ b/kernel/vm.c
@@ -62,7 +62,7 @@ prockptinit()
   uvmmap(prockpt, VIRTIO0, VIRTIO0, PGSIZE, PTE_R | PTE_W);
 
   // CLINT
  uvmmap(prockpt, CLINT, CLINT, 0x10000, PTE_R | PTE_W);
  // uvmmap(prockpt, CLINT, CLINT, 0x10000, PTE_R | PTE_W);
 
   // PLIC
   uvmmap(prockpt, PLIC, PLIC, 0x400000, PTE_R | PTE_W);