前言

  • 本篇是关于 MIT 6.S081-2020-Lab4 的实现;
  • 如果内容上发现有什么问题请不要吝啬您的键盘。

Backtrace

实验指导书上给出了 RISC-V 栈帧布局:

基本上只要看懂这幅图就可以过关了:

/* kernel/printf.c */

void
backtrace()
{
  uint64 *fp = (uint64*)r_fp(), *ra;

  while ((uint64)fp != PGROUNDUP((uint64)fp) && (uint64)fp != PGROUNDUP((uint64)fp)) {
    ra = (uint64*)(*(fp - 1));
    fp = (uint64*)(*(fp - 2));
    printf("%p\n", ra);
  }
}

Alarm

虽然是 hard,但仔细地看清楚并理解要求的话还是能够收掉的。

先按照提示给 struct proc 添加几个必要的字段进去:

// Per-process state
struct proc {
  ...
  int ticks;                   // alarm interval
  void (*handler)();           // the pointer to the handler function
  int ticks_passed;
};

test0: invoke handler

/* kernel/sysfile.c */

uint64
sys_sigalarm(void)
{
  int ticks;
  void (*handler)();
  if (argint(0, &ticks) < 0)
    return -1;
  if (argaddr(1, (uint64*)&handler) < 0)
    return -1;

  struct proc *p = myproc();

  p->ticks = ticks;
  p->handler = handler;
  return 0;
}
/* kernel/trap.c */

void
usertrap(void)
{
  ...
  // give up the CPU if this is a timer interrupt.
  if(which_dev == 2) {
    if (p->ticks != 0 && p->ticks == ++p->ticks_passed) {
        p->trapframe->epc = (uint64)p->handler;
    }
    yield();
  }
  ...
}

test1/test2(): resume interrupted code

如果要保存中断的现场,kernel 的做法是将它们放进 trapframe 当中。

在这里,我们也做类似的操作。

不过因为 trapframe 被占用了,我们再给 struct proc 添加一个字段 struct trapframe *utrapframe;

由于这是个指针变量,在 allocproc() 的时候还得给它 kalloc() 一页内存,回头 freeproc() 的时候还得 kfree() 掉它,要不然 usertest 过不去。

/* kernel/proc.c */

static struct proc*
allocproc(void)
{
  ...
  // Allocate a trapframe page.
  if((p->trapframe = (struct trapframe *)kalloc()) == 0){
    release(&p->lock);
    return 0;
  }
    
  if((p->utrapframe = (struct trapframe *)kalloc()) == 0){
    release(&p->lock);
    return 0;
  }
  ...
}
/* kernel/proc.c */

static void
freeproc(struct proc *p)
{
  ...
  if(p->trapframe)
    kfree((void*)p->trapframe);
  p->trapframe = 0;
  
  if(p->utrapframe)
    kfree((void*)p->utrapframe);
  p->utrapframe = 0;
  ...
}

我们要在适当的时机去拿 trapframe 里的内容去初始化 utrapframe

/* kernel/trap.c */

void
usertrap(void)
{
  ...
  // give up the CPU if this is a timer interrupt.
  if(which_dev == 2) {
    if (p->ticks != 0 && p->ticks == ++p->ticks_passed) {
        memmove(p->utrapframe, p->trapframe, sizeof(*(p->trapframe)));
        p->trapframe->epc = (uint64)p->handler;
    }
    yield();
  }
  ...
}

最后别忘了还要返回现场:

/* kernel/sysfile.c */

uint64
sys_sigreturn(void)
{
  struct proc *p = myproc();
  p->ticks_passed = 0;
  memmove(p->trapframe, p->utrapframe, sizeof(*(p->trapframe)));
  usertrapret();
  return 0;
}

后记

Lab4 比 Lab3 相比实在顺畅很多。

因为做 Lab3 的时候把 xv6 book 的第四章也一起看了,所以自己一个人从头到做到尾不到半天时间。

Lab3 跟 Lab4 相比,主要还是要想的东西太多,出异常中断了也不好排错……


Thinkingear
1 声望4 粉丝

Best effort to 摸鱼.