MIT 6.828 Homework: Xv6 lazy page allocation

Part One: Eliminate allocation from sbrk()

任务

将 sbrk(n) 系统调用实现中的分配 page 部分删除,在 sysproc.c 中的 sys_sbrk() 中。在删除之后,新的 sbrk(n) 应该仅仅将进程的内存大小的指示符 (myproc()->sz) 增加 n,但不实际分配内存。

代码修改情况:

int
sys_sbrk(void)
{
  int addr;
  int n;

  if(argint(0, &n) < 0)
    return -1;
  addr = myproc()->sz;
  myproc()->sz += n;
  // if(growproc(n) < 0)
  //   return -1;
  return addr;
}

修改后重新编译,引发异常

$ echo hi
pid 3 sh: trap 14 err 6 on cpu 0 eip 0x1220 addr 0x4004--kill proc

Part Two: Lazy allocation

任务

修改 trap.c 的代码使在处理缺页异常时,将一个新分配的页返回到用户空间。

根据题目的提示,在输出错误信息的 cprintf 语句前添加代码,模仿 vm.c 中的 allocuvm 函数分配空间,修改后的 trap 函数如下:

注意:

  • 要根据提示将 mappages 函数在前面进行一次声明,并将 vm.c 中函数定义前面的 static 删去。
  • 观察 proc.c 中对 allocuvm 的调用,sys_sbrk() 返回值即 oldsz 存在 %eax 中,即 myproc()->tf->eax。
int mappages(pde_t *pgdir, void *va, uint size, uint pa, int perm);
void trap(struct trapframe *tf)
{
  if (tf->trapno == T_SYSCALL)
  {
    if (myproc()->killed)
      exit();
    myproc()->tf = tf;
    syscall();
    if (myproc()->killed)
      exit();
    return;
  }

  switch (tf->trapno)
  {
  case T_IRQ0 + IRQ_TIMER:
    if (cpuid() == 0)
    {
      acquire(&tickslock);
      ticks++;
      wakeup(&ticks);
      release(&tickslock);
    }
    lapiceoi();
    break;
  case T_IRQ0 + IRQ_IDE:
    ideintr();
    lapiceoi();
    break;
  case T_IRQ0 + IRQ_IDE + 1:
    // Bochs generates spurious IDE1 interrupts.
    break;
  case T_IRQ0 + IRQ_KBD:
    kbdintr();
    lapiceoi();
    break;
  case T_IRQ0 + IRQ_COM1:
    uartintr();
    lapiceoi();
    break;
  case T_IRQ0 + 7:
  case T_IRQ0 + IRQ_SPURIOUS:
    cprintf("cpu%d: spurious interrupt at %x:%x\n",
            cpuid(), tf->cs, tf->eip);
    lapiceoi();
    break;

  // PAGEBREAK: 13
  default:
    if (myproc() == 0 || (tf->cs & 3) == 0)
    {
      // In kernel, it must be our mistake.
      cprintf("unexpected trap %d from cpu %d eip %x (cr2=0x%x)\n",
              tf->trapno, cpuid(), tf->eip, rcr2());
      panic("trap");
    }

    char *mem = kalloc();
    uint a = PGROUNDDOWN(rcr2());
    if (mem != 0)
    {
      memset(mem, 0, PGSIZE);
      if (mem == 0)
      {
        cprintf("allocuvm out of memory\n");
        // sys_sbrk() 返回值即 oldsz 存在 %eax 中,即 myproc()->tf->eax
        deallocuvm(myproc()->pgdir, myproc()->sz, myproc()->tf->eax);
        return;
      }
      extern int mappages(pde_t * pgdir, void *a, uint size, uint pa, int perm);
      if (mappages(myproc()->pgdir, (void *)a, PGSIZE, V2P(mem), PTE_W | PTE_U) >= 0)
      {
        break;
      }
      else
      {
        cprintf("allocuvm out of memory (2)\n");
        deallocuvm(myproc()->pgdir, myproc()->sz, myproc()->tf->eax);
        kfree(mem);
        return;
      }
    }

    // In user space, assume process misbehaved.
    cprintf("pid %d %s: trap %d err %d on cpu %d "
            "eip 0x%x addr 0x%x--kill proc\n",
            myproc()->pid, myproc()->name, tf->trapno,
            tf->err, cpuid(), tf->eip, rcr2());
    myproc()->killed = 1;
  }

  // Force process exit if it has been killed and is in user space.
  // (If it is still executing in the kernel, let it keep running
  // until it gets to the regular system call return.)
  if (myproc() && myproc()->killed && (tf->cs & 3) == DPL_USER)
    exit();

  // Force process to give up CPU on clock tick.
  // If interrupts were on while locks held, would need to check nlock.
  if (myproc() && myproc()->state == RUNNING &&
      tf->trapno == T_IRQ0 + IRQ_TIMER)
    yield();

  // Check if the process has been killed since we yielded
  if (myproc() && myproc()->killed && (tf->cs & 3) == DPL_USER)
    exit();
}

编译后 echo hi 能够正确运行:

$ echo hi
hi
posted @ 2022-12-18 09:52  雪中的茶  阅读(286)  评论(0编辑  收藏  举报