MIT xv6 2020 系列实验:Lab4 traps
主要是一个内核调度alarm,定时中断并输出alarm。
任务一:backtrace
首先取出内核栈栈顶指针fp,对应到xv6的寄存器名是s0。
static inline uint64
r_fp()
{
uint64 x;
asm volatile("mv %0, s0" : "=r" (x) );
return x;
}
backtrace:内核栈大小一页也就是4KB,其中fp往下依次存储上次的ra,与上次的栈指针pre。
进行循环遍历打印
void backtrace()
{
uint64 fp = r_fp();
uint64 up = PGROUNDUP(fp);
uint64 down = PGROUNDDOWN(fp);
while(fp < up && fp > down){
uint64 ra = fp - 8;
uint64 pre = fp - 16;
printf("%p\n",*(uint64*)ra);
fp = *(uint64*)pre;
}
}
ra代表函数调用的位置。
然后即可在sys_sleep时实现backtrace。
任务二:sigalarm
uint64
sys_sigalarm(void){
int ntick;
uint64 f_ptr;
if(argint(0, &ntick) < 0)
return -1;
if(argaddr(1,&f_ptr) < 0)
return -1;
acquire(&alarmlock);
myproc()->interval = ntick;
myproc()->handler = f_ptr;
myproc()->xticks = 0;
release(&alarmlock);
return 0;
}
alarm传入两个参数:alarm周期与所调用的函数指针。这里直接赋值,对ntick为0的情况之后考虑。
这里一起考虑test0/1/2的部分:
系统调用处,在usertrap中进行修改:
if(p->killed)
exit(-1);
/*--------------------修改部分--------------------*/
// give up the CPU if this is a timer interrupt.
if(which_dev == 2){
p->xticks += 1;
if(p->xticks == p->interval){
p->trapframe_bak = (struct trapframe*) kalloc();
memmove(p->trapframe_bak,p->trapframe,sizeof(struct trapframe));
p->trapframe->epc = p->handler;
}
yield();
}
/*--------------------修改结束--------------------*/
usertrapret();
如果p->interval为0,这里就不会有alarm调用,因为xticks直接永远不等于p->interval。
否则在触发定时器的过程中,每触发一次xticks递增1,在xticks与interval相等时就进行alarm函数的调用。
同时保存栈帧,用于对线程进行恢复。
最后还有还原的过程,在sigreturn中,要将线程恢复,则将p的trapframe_bak部分全部复制回p的trapframe中即可。
uint64
sys_sigreturn(void){
struct proc *p = myproc();
p->xticks = 0;
memmove(p->trapframe,p->trapframe_bak,sizeof(struct trapframe));
return 0;
}
如上,完成实验。