lab4
准备
uservec做的事情:
1.交换a0和 sscratch,然后a0就是TRAPFRAME了。(这里没懂,先记录下)
2.将寄存器的值保存到trapframe中。
3. 将a0的值(现在存在了sscratch中)也保存到trapframe
4. 从trapframe中恢复内核的一些数据。(设置kernel pagetable等)
5. 跳转到usertrap()
疑惑a0寄存器有啥用。
解答:在uservec执行前,所有的寄存器都保存了进程被中断时候的值。
usertrap():
1.首先看trap是不是来自user mode,然后将stvec设置为kernelvec。
2. 保存当前进程的program counter。
3. 判断是系统调用,设备中断,还是exception。然后采取相应措施。注意,如果是系统调用的话,program counter要加4,因为希望执行下一条指令。
4. 调用usertrapret。
usertrapret:
1.首先将stvec设置为uservec
2.设置trapframe里的一些参数
3.设置ssstatus寄存器
3.设置sepc寄存器
4.调用userret
userret:
1.切换页表
2.从trapfram中复原保存的寄存器的值,并设置sscratch为TRAPFRAME
3.调用sret返回user mode
backtrace
void backtrace(void){
uint64 st, ra,f,end;
st = r_fp();
end=PGROUNDUP(st);
printf("backtrace:\n");
while (st!=end)
{
ra = st - 8;
f= st-16;
printf("%p\n",*(uint64 *)ra);
st=*(uint64 *)(f);
}
}
alarm
这个实验包括两个系统调用sigalarm(interval, handler)和sigreturn( ),要我们每n个时间片调用一次handler函数。
首先我们来通过test0。有关系统调用和proc.h的一些配置这里就不赘述了。这里只keenel/trap.c的代码。
void
usertrap(void)
{
int which_dev = 0;
if((r_sstatus() & SSTATUS_SPP) != 0)
panic("usertrap: not from user mode");
// send interrupts and exceptions to kerneltrap(),
// since we're now in the kernel.
w_stvec((uint64)kernelvec);
struct proc *p = myproc();
// save user program counter.
p->trapframe->epc = r_sepc();
if(r_scause() == 8){
// system call
if(p->killed)
exit(-1);
// sepc points to the ecall instruction,
// but we want to return to the next instruction.
p->trapframe->epc += 4;
// an interrupt will change sstatus &c registers,
// so don't enable until done with those registers.
intr_on();
syscall();
} else if((which_dev = devintr()) != 0){
// ok
} else {
printf("usertrap(): unexpected scause %p pid=%d\n", r_scause(), p->pid);
printf(" sepc=%p stval=%p\n", r_sepc(), r_stval());
p->killed = 1;
}
if(p->killed)
exit(-1);
// give up the CPU if this is a timer interrupt.
if(which_dev == 2){
if(p->ticks!=0){
p->left--;
if(p->left==0){
//p->left=p->ticks;
//p->ticks=0;
//copy_trapframe(p->save_trapframe,p->trapframe);
// memmove(p->save_trapframe,p->trapframe,sizeof(struct trapframe));
p->trapframe->epc = (uint64)p->handler;
}
}
yield();
}
usertrapret();
}
第一次我做到这里的时候有个疑惑,既然在这里只改了epc,没有做别的处理。那么进程在跳转到hander函数后,怎么会把alarmtest剩余的部分执行完呢,是怎么跳回去的?
然后
用gdb查看寄存器信息,pc调到handler函数,但是ra寄存器保存的还是原来指令执行的地址,handler执行完后,ra的值存入pc,然后程序继续执行。
然后test1,和test2就是要保存切换到handler时候的寄存器信息,然后再调用sigreturn的时候复原。这个地方在proc结构中,增加个trapframe就可以了。开始的时候我自己写了个copy frame函数,后面发现直接用memmove函数就可以了,这是因为 p->trapframe和p->save_trapframe都是kalloc函数分配的,是物理地址,所以可以在内核直接用吗?
然后还有要注意要free。