代码:https://github.com/JasenChao/xv6-labs.git
backtrace
题目要求实现backtrace
来对堆栈上调用发生错误的地方进行跟踪。寄存器s0
包含指向当前堆栈帧的指针,那么返回地址就位于帧指针的固定偏移量-8,前一个fp地址的偏移量为-16。
在riscv.h
文件中增加提示中的代码:
static inline uint64
r_fp()
{
uint64 x;
asm volatile("mv %0, s0" : "=r" (x) );
return x;
}
在printf.c
文件中实现backtrace()
函数:
void
backtrace(void)
{
uint64 fp = r_fp();
uint64 high = PGROUNDUP(fp), low = PGROUNDDOWN(fp);
printf("backtrace:\n");
while(fp >= low && fp < high)
{
printf("%p\n", *((uint64*)(fp - 8)));
fp = *((uint64*)(fp - 16));
}
}
在defs.h
文件中增加函数声明void backtrace(void)
:
测试程序是运行sleep
函数调用backtrace,因此在sysproc.c
文件中的sys_sleep
函数末尾调用backtrace()
。
使用make GRADEFLAGS=backtrace grade
测试结果是否正确。
alarm
题目要求基于给出的alarmtest
函数,实现两个系统调用sigalarm
和sigreturn
,在程序消耗n个ticks之后调用处理程序。
首先在Makefile
中添加测试程序$U/_alarmtest\
。
在user.h
中添加声明,同样在usys.pl
、syscall.h
和syscall.c
中添加相应的内容:
// user.h
int sigalarm(int ticks, void (*handler)());
int sigreturn(void);
// usys.pl
entry("sigalarm");
entry("sigreturn");
// syscall.h
#define SYS_sigalarm 22
#define SYS_sigreturn 23
// syscall.c
extern uint64 sys_sigalarm(void);
extern uint64 sys_sigreturn(void);
static uint64 (*syscalls[])(void) = {
...
[SYS_sigalarm] sys_sigalarm,
[SYS_sigreturn] sys_sigreturn,
};
在proc.h
中的struct proc
中增加成员:
struct trapframe *pre_trapframe;
int interval; // 触发alarm的中断数量
int ticks; // 时钟中断数量
uint64 handler; // 处理alarm的函数地址
为了让pre_trapframe
保存上一个trapframe
,需要对proc.c
中的allocproc
和freeproc
进行修改:
// allocproc
if((p->pre_trapframe = (struct trapframe *)kalloc()) == 0){
freeproc(p);
release(&p->lock);
return 0;
}
// freeproc
if(p->pre_trapframe)
kfree((void*)p->pre_trapframe);
p->pre_trapframe = 0;
在sysproc.c
文件中实现sigalarm
和sigreturn
函数:
uint64
sys_sigalarm(void){
int interval;
uint64 f_addr;
struct proc *p = myproc();
argint(0,&interval);
argaddr(1,&f_addr);
// 保存触发次数和函数地址
p->interval = interval;
p->handler = f_addr;
p->ticks = 0;
return 0;
}
uint64
sys_sigreturn(void){
struct proc* p = myproc();
*p->trapframe = *p->pre_trapframe;
p->ticks = 0;
return p->pre_trapframe->a0;
}
修改trap.c
中的usertrap
函数,当触发时间中断时进行判断,如果满足触发次数则执行触发函数:
// give up the CPU if this is a timer interrupt.
if(which_dev == 2) {
p->ticks++;
if(p->ticks == p->interval && 0 < p->interval) {
*p->pre_trapframe = *p->trapframe;
p->trapframe->epc = p->handler;
} else {
yield();
}
}
使用make GRADEFLAGS=alarmtest grade
命令测试是否通过。
测试结果
使用make grade
测试,结果如下:
== Test backtrace test ==
$ make qemu-gdb
backtrace test: OK (1.6s)
== Test running alarmtest ==
$ make qemu-gdb
(3.6s)
== Test alarmtest: test0 ==
alarmtest: test0: OK
== Test alarmtest: test1 ==
alarmtest: test1: OK
== Test alarmtest: test2 ==
alarmtest: test2: OK
== Test alarmtest: test3 ==
alarmtest: test3: OK
== Test usertests ==
$ make qemu-gdb
usertests: OK (39.9s)
这部分题目太难了,参考了 https://zhuanlan.zhihu.com/p/624606664 的大神详解。
标签:uint64,fp,traps,backtrace,void,XV6,trapframe,alarmtest From: https://www.cnblogs.com/JasenChao/p/18015449