Lab: Page tables

Speed up system calls

题目的要求比较清晰, 即对每个进程在 allocproc() 时分配一个页面, 随后在 proc_pagetable() 中将这个页面映射到宏 USYSCALL 指定的地址中, 最后在 freeproc() 时将这个页面释放.

首先, 由于 allocproc, proc_pagetable 以及 freeproc 是不同的函数, 因此为了做到 Hint 中所述过程, 在 proc 结构体里添加一个变量用于记录当前进程的加速页面:

struct usyscall *usc;

而后在 allocproc 仿照 trapframe 添加页面的分配和记录:

if((p->usc = (struct usyscall *)kalloc()) == 0){
  freeproc(p);
  release(&p->lock);
  return 0;
}
p->usc->pid = p->pid;

而后在 proc_pagetable 中将分配好的页面映射到对应的虚拟地址上, 这里需要注意的是, 代码需要添加到 TRAPFRAME 映射之后且需要仿照上面的代码添加 mappages 失败时的处理代码:

if (mappages(pagetable, USYSCALL, PGSIZE, 
             (uint64)(p->usc), PTE_U | PTE_R | PTE_V) < 0){
  uvmunmap(pagetable, TRAMPOLINE, 1, 0);
  uvmunmap(pagetable, TRAPFRAME, 1, 0);
  uvmfree(pagetable, 0);
  return 0;
}

最后在 freeproc 中将分配到的页面通过 kfree 释放 (如果成功分配的话). 同时需要注意在 proc_freepagetable 中将映射后的 PTE 释放, 否则会导致 panic("freewalk: leaf") 的错误:

// free a proc structure and the data hanging from it,
// including user pages.
// p->lock must be held.
static void
freeproc(struct proc *p)
{
  if(p->trapframe)
    kfree((void*)p->trapframe);
  p->trapframe = 0;
  if(p->usc)
    kfree((void *)p->usc);
  p->usc = 0;
  if(p->pagetable)
    proc_freepagetable(p->pagetable, p->sz);
  p->pagetable = 0;
  p->sz = 0;
  p->pid = 0;
  p->parent = 0;
  p->name[0] = 0;
  p->chan = 0;
  p->killed = 0;
  p->xstate = 0;
  p->state = UNUSED;
}

// Free a process's page table, and free the
// physical memory it refers to.
void
proc_freepagetable(pagetable_t pagetable, uint64 sz)
{
  uvmunmap(pagetable, TRAMPOLINE, 1, 0);
  uvmunmap(pagetable, TRAPFRAME, 1, 0);
  uvmunmap(pagetable, USYSCALL, 1, 0);
  uvmfree(pagetable, sz);
}

完成以上步骤后运行 pgtbltest 即可得到该部分的通过结果.

Print a page table

题目要求是给定一个页表地址, 将其各级内容打印出来, 那么遍历给定页表其中的 512 个 PTE, 如果这个 PTEPTE_V 为真, 则有效, 在这之上, 若其 PTE_R, PTE_W, PTE_X 均为假, 则说明该 PTE 指向下一级页表, 反之则说明该 PTE 指向一个物理页面, 对页表的每一级都递归执行这个过程并打印即可.

题目建议的解题步骤如下, 首先在 kernel/vm.c 中声明并实现函数 vmprint(pagetable_t pagetable), 而后在 kernel/defs.h 中添加函数的声明便于后续的调用, 最后在 kernel/exec.c 中函数返回前插入 if(p->pid==1) vmprint(p->pagetable)语句以调用函数.

那么首先实现 vmprint 函数, 由于这个函数是被直接调用的, 在其中执行递归多有不便, 因此将函数的主体提出到其他函数中, 实现如下:

void 
printwalk(pte_t addr, int level, int index, char is_pte) 
{
  pagetable_t pagetable;
  
  if (level == 0) {
    printf("page table %p\\n", addr);
    pagetable = (pagetable_t)addr;
  } else {
    for (int i = 0; i < level; ++i)
      printf(" ..");
    printf("%d: pte %p pa %p\\n", index, addr, PTE2PA(addr));

    if (is_pte)
      return;
    pagetable = (pagetable_t)(PTE2PA(addr));
  }

  for (int i = 0; i < 512; ++i) {
    pte_t pte = pagetable[i];
    if ((pte & PTE_V) && (pte & (PTE_R | PTE_W | PTE_X)) == 0)
      printwalk(pte, level + 1, i, 0);
    else if ((pte & PTE_V))
      printwalk(pte, level + 1, i, 1);
  }
}

void
vmprint(pagetable_t pagetable)
{
  printwalk((pte_t)pagetable, 0, 0, 0);
}

kernel/defs.h 中添加声明并在 exec.c 中插入对应语句即可 (不放代码了). 完成步骤后启动 xv6 可以得到和题目一致的结果.

Untitled