lab4
trap步骤(跳转到trampoline):
交换 a0
和 sscratch
:
- 使用
csrrw a0, sscratch, a0
指令交换 a0
和 sscratch
的值。这样,a0
现在指向 TRAPFRAME,这是用户空间的 trapframe 地址,而 sscratch
保存了原来的 a0
值(即用户态传递的参数)。
保存用户态寄存器:
- 将所有用户态寄存器的值保存到
a0
指向的 trapframe 中,以便在处理完陷阱后可以恢复这些寄存器的值。
保存用户态的 a0
:
- 将
sscratch
(即原来的 a0
)保存到 trapframe 中,以便在返回用户态时可以恢复 a0
的值。
恢复内核态上下文:
- 从 trapframe 中恢复内核栈指针(
sp
)、当前 hart 的 id(tp
)、usertrap
函数的地址(t0
),以及内核页表(satp
)。
跳转到 usertrap
:
- 使用
jr t0
跳转到 usertrap()
函数,这个函数处理实际的陷阱(如系统调用、中断或异常),并不会返回。
Backtrace
首先,按照指导书的提示将读取fp(s0)
寄存器的指令写入riscv.h
,并添加backtrace()
到sys_sleep
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| static inline uint64 r_fp() { uint64 x; asm volatile("mv %0, s0" : "=r" (x) ); return x; }
uint64 sys_sleep(void) { ... backtrace(); return 0; }
|
在printf.c
定义我们的backtrace()
函数,由于xv6的栈只分配了一页的空间,我们可以直接获取栈底(页开始)的地址,当ret_addr
大于栈底时(栈是反向增长的),即已完成遍历。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| void backtrace(void);
void backtrace() { printf("backtrace:\n"); uint64 fp = r_fp(); uint64 base = PGROUNDUP(fp); while(fp < base) { uint64 Ret_addr = *(uint64*)(fp -8); fp = *(uint64*)(fp -16); printf("%p\n", Ret_addr); } }
|
Alarm
test0
按照添加系统调用的流程,添加系统调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| entry("sigalarm"); entry("sigreturn");
int sigalarm(int ticks, void (*handler)()); int sigreturn(void);
#define SYS_sigalarm 22 #define SYS_sigreturn 23
extern uint64 sys_sigalarm(void); extern uint64 sys_sigreturn(void); static uint64 (*syscalls[])(void) = { ... [SYS_sigalarm] sys_sigalarm, [SYS_sigreturn] sys_sigreturn, };
|
为了实现定时alram,我们需要在proc添加字段来保存handler函数、tick次数和触发handler的ticks次数。同时在创建进程时对其进行初始化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| struct proc { ...
uint64 handler_addr; int ticks; int tickcount; };
void userinit(void) { ... p->tickcount = 0; p->ticks = 0; p->handler_addr = 0; release(&p->lock); }
|
我们添加sys_sigalarm
和sys_sigreturn
函数,由于test0只是测试handler,我们可以将sys_sigreturn
直接返回。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| uint64 sys_sigalarm(void) { int ticks; uint64 handler; if(argint(0, &ticks) < 0||argaddr(1, &handler) < 0) { return -1; } struct proc *p = myproc(); p->tickcount = 0; p->ticks = ticks; p->handler_addr = handler; return 0; }
uint64 sys_sigreturn(void) { return 0; }
|
修改时钟中断,每次中断都将tick加1,当满足tick==ticks
时调用handler
,并将tick
归0。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| if(which_dev == 2) { if(p->ticks){ p->tickcount ++; if(p->tickcount == p->ticks) { p->tickcount = 0; p->ticks = 0; p->trapframe->epc = p->handler_addr; } } yield(); }
|
test1
进入test1,要求我们要恢复寄存器状态,即保存trapframe
,并在调用sys_sigreturn
时恢复寄存器。因此我们要添加old_trapframe
字段,并在初始化时分配内存。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| struct proc { ... uint64 handler_addr; int ticks; int tickcount; struct trapframe *old_trapframe; };
static struct proc* allocproc(void) { ...
found: ... if((p->trapframe = (struct trapframe *)kalloc()) == 0){ freeproc(p); release(&p->lock); return 0; } if((p->old_trapframe = (struct trapframe *)kalloc()) == 0){ freeproc(p); release(&p->lock); return 0; }
}
|
接着修改sys_sigreturn
,恢复寄存器
1 2 3 4 5 6 7 8 9
| uint64 sys_sigreturn(void) { struct proc *p = myproc(); *(p->trapframe) = *(p->old_trapframe);
return 0; }
|
在进入handler函数前保存寄存器。为什么不在调用sys_sigalarm()
时就保存呢?因为我们设置中断,进程还是照常执行,只不过每隔tick时钟执行一次handler。寄存器的状态显然不一样。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| if(which_dev == 2) { if(p->ticks){ p->tickcount ++; if(p->tickcount >= p->ticks) { p->tickcount = 0; *p->old_trapframe = *p->trapframe; p->trapframe->epc = p->handler_addr; } } yield(); } usertrapret(); }
|
test2
test2测试的是当handler函数执行所需的时间大于达到tick==ticks
的时间,此时handler被重入,而我们的中断处理程序是不允许重入的(若重入则不仅保存的寄存器数据被覆盖,handler还会陷入死循环),因此我们需要添加一个字段防止这种事情发生。
添加is_alarm
字段,当handler执行时,另一个handler将不会被调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| void userinit(void) {
p->tickcount = 0; p->ticks = 0; p->handler_addr = 0; p->is_alarm = 0;
release(&p->lock); }
|
修改sys_sigalarm
和sys_sigreturn
函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| uint64 sys_sigalarm(void) { int ticks; uint64 handler; if(argint(0, &ticks) < 0||argaddr(1, &handler) < 0) { return -1; } struct proc *p = myproc(); p->tickcount = 0; p->ticks = ticks; p->handler_addr = handler; p->is_alarm = 0; return 0; }
uint64 sys_sigreturn(void) { struct proc *p = myproc(); *(p->trapframe) = *(p->old_trapframe); p->is_alarm = 0;
return 0; }
|
修改时钟中断。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| if(which_dev == 2) { if(p->ticks){ p->tickcount ++; if(p->tickcount >= p->ticks &&p->is_alarm == 0) { p->tickcount = 0; *p->old_trapframe = *p->trapframe; p->is_alarm = 1; p->trapframe->epc = p->handler_addr; } } yield(); }
|