【xv6 源码窥探(1)】中断

前言

  • 本篇是关于 MIT 6.S081-2020-Lab4 的实现;
  • 如果内容上发现有什么问题请不要吝啬您的键盘。
Backtrace 实验指导书上给出了 RISC-V 栈帧布局:
【xv6 源码窥探(1)】中断
文章图片

基本上只要看懂这幅图就可以过关了:
/* 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 的第四章也一起看了,所以自己一个人从头到做到尾不到半天时间。
【【xv6 源码窥探(1)】中断】Lab3 跟 Lab4 相比,主要还是要想的东西太多,出异常中断了也不好排错……

    推荐阅读