MIT 6.S081 Traps
backtrace
利用frame pointer来完成调用追踪。
用户栈结构
// kernel/printf.c
void
backtrace(void)
{
printf("backtrace:\n");
uint64 fp = r_fp();
uint64 pageUpBound = PGROUNDUP(fp);
// uint64 pageDownBound = PGROUNDDOWN(fp);
while (fp < pageUpBound) {
printf("%p\n", *(uint64 *)(fp-8));
fp = *(uint64 *)(fp - 16);
}
}
运行结果
xv6 kernel is booting
hart 1 starting
hart 2 starting
init: starting sh
$ ./bttest
backtrace:
0x000000008000211c
0x0000000080001ff6
0x0000000080001cda
$ QEMU: Terminated
sugar@ubuntuServer:~/xv6-labs-2021$ addr2line -e kernel/kernel
0x000000008000211c
0x0000000080001ff6
0x0000000080001cda/home/sugar/xv6-labs-2021/kernel/sysproc.c:63
/home/sugar/xv6-labs-2021/kernel/syscall.c:140
/home/sugar/xv6-labs-2021/kernel/trap.c:76
Alarm
test0
通过系统调用sigalarm注册timer中断处理程序,时间间隔到了之后,执行用户定义的函数。
首先增加新的系统调用
// user/user.h
int sigalarm(int ticks, void (*handler)());
int sigreturn(void);
// user/usys.pl
entry("sigalarm");
entry("sigreturn");
// kernel/syscall.h
#define SYS_sigalarm 22
#define SYS_sigreturn 23
// kernel/syscall.c
extern uint64 sys_sigalarm(void);
extern uint64 sys_sigreturn(void);
static uint64 (*syscalls[])(void) = {
....
[SYS_sigalarm] sys_sigalarm,
[SYS_sigreturn] sys_sigreturn,
};
uint64
sys_sigalarm(void)
{
int interval;
uint64 handler;
if (argint(0, &interval) < 0)
return -1;
if (argaddr(1, &handler) < 0)
return -1;
struct proc *p = myproc();
p->handler = handler;
p->interval = interval;
p->ticks = 0;
return 0;
}
uint64
sys_sigreturn(void)
{
return 0;
}
修改proc的结构,保存定时器相关的记录成员(间隔,处理函数指针,自上次调用流逝了多少ticks,是否正在处理alarm)
// Per-process state
struct proc {
// for alarm
int interval; // the alarm interval
uint64 handler; // the periodically called function pointer
int ticks; // time since last call
int in_alarm; // while the kernel is processing alarm handler
};
// kernel/proc.c
static struct proc*
allocproc(void)
{
....
found:
p->pid = allocpid();
p->state = USED;
// initialize for alarm
p->ticks = 0;
p->handler = 0;
p->interval = 0;
p->in_alarm = 0;
....
}
最后在trap.c中处理时钟中断,注意,从内核空间返回后,执行的指令地址会从trapframe的epc复制到pc,进而执行相关的handler函数(alarm)
// give up the CPU if this is a timer interrupt.
if(which_dev == 2) {
// timer interrupt
if (p->interval != 0) {
p->ticks++; // 在处理alarm的时候也需要计时
if (p->in_alarm == 0) {
if (p->ticks > p->interval) {
p->ticks = 0;
p->in_alarm = 1;
// printf("Call the handler.\n");
// p->handler();
p->trapframe->epc = p->handler; // 从内核空间返回的时候,执行的地址
}
}
}
yield();
}
运行结果
$ ./alarmtest
test0 start
......alarm!
test0 passed
test1 start
...............................................................QEMU: Terminated
test1
实现从中断处理程序中返回到用户程序,要增加保存的寄存器。
// kernel/proc.h
// Per-process state
struct proc {
......
// for alarm
int interval; // the alarm interval
uint64 handler; // the periodically called function pointer
int ticks; // time since last call
int in_alarm; // while the kernel is processing alarm handler
int user_epc; // user program's pc
struct trapframe user_frame; // saved 32 user's register
};
// kernel/sysproc.c
uint64
sys_sigreturn(void)
{
struct proc *p = myproc();
p->trapframe->epc = p->user_epc; // 原始用户进程的epc
memmove(p->trapframe, &p->user_frame, sizeof (p->user_frame));
p->in_alarm = 0;
return 0;
}
//kernel/trap.c
void
usertrap(void)
{
...
// give up the CPU if this is a timer interrupt.
if(which_dev == 2) {
// timer interrupt
if (p->interval != 0) {
p->ticks++; // 在处理alarm的时候也需要计时
if (p->in_alarm == 0) {
if (p->ticks > p->interval) {
p->ticks = 0;
p->in_alarm = 1;
p->user_epc = p->trapframe->epc;
// memmove(dst, src, size)
memmove(&p->user_frame, p->trapframe, sizeof(p->user_frame));
p->trapframe->epc = p->handler; // 从内核空间返回的时候,执行的地址
}
}
}
yield();
}
...
}