前言

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

Backtrace

试验领导书上给出了 RISC-V 栈帧布局:

基本上只有看懂这幅图就能够过关了:

/* kernel/printf.c */voidbacktrace(){  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 statestruct proc {  ...  int ticks;                   // alarm interval  void (*handler)();           // the pointer to the handler function  int ticks_passed;};

test0: invoke handler

/* kernel/sysfile.c */uint64sys_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 */voidusertrap(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 voidfreeproc(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 */voidusertrap(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 */uint64sys_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 相比,次要还是要想的货色太多,出异常中断了也不好排错……