-
a0-a7
存放参数,13放在了a2
中 -
编译器内联了这两个函数,从
li a1,12
可以看出来,编译器直接算出了结果,做了内联优化 -
0x630
-
0x38
,即函数的返回地址 -
57616=0xE110
0x00646c72小端存储则为72-6c-64-00
对照ASCII码表72:r 6c:l 64:d 00:字符串结束标识
输出为:HE110 World
-
取决于调用函数之前寄存器
a2
中有什么
栈帧中的fp
指向了前一个栈帧,所以打印出调用栈只需要跟着fp
依此遍历即可。xv6为每个栈分配一页,又因为栈是由高地址向低地址增长,所以只需要检测fp
是否到达栈底即可
void
backtrace(void)
{
printf("backtrace:\n");
uint64 fp = r_fp(); // 根据hits 使用r_fp()获取fp
uint64 base = PGROUNDUP(fp); // 栈是由高地址向低地址,所以UP的高地址才是栈顶
while(fp<base){
printf("%p\n", *((uint64*)(fp - 8)));
fp = *((uint64*)(fp - 16));
}
}
Alarm
首先在 Makefile
把 alarmtest 加上
在 user/user.h 中声明
int sigalarm(int ticks, void (*handler)());
int sigreturn(void);
user/usys.pl
kernel/syscall.h
kernel/syscall.c
这三个文件中也记得同步更新,步骤跟之前的 syscall 一样
// kernel/sysproc.c
// int sigalarm(int ticks, void (*handler)());
uint64
sys_sigalarm(void)
{
int ticks;
uint64 handler;
if( argint(0,&ticks)< 0 || argaddr(1,&handler)<0 ){
return -1;
}
struct proc *p = myproc();
p->alarm_interval = ticks;
p->alarm_handler = (void *) handler;
return 0;
}
// int sigreturn(void);
uint64
sys_sigreturn()
{
return 0;
}
sigalarm 需要接受两个参数,一个是报警的时间间隔,还有一个是报警时要调用的函数的函数指针
同时我们还需要自己额外保存一下距离上次调用 sigalarm 已经过了多久。
// kernel/proc.h
int alarm_interval; // alarm interval
void (*alarm_handler); // alarm function handler
int ticks_since_last_call; // ticks_since_last_call
sigreturn 暂时不用管,就让它返回0就好
在 kernel/proc.c
中做好变量的初始化
// kernel/proc.c
static struct proc*
allocproc(void)
{
...
p->alarm_interval = 0;
p->ticks_since_last_call =0;
return p
}
static void
freeproc(struct proc *p)
{
...
p->alarm_interval = 0;
p->ticks_since_last_call =0;
}
test0 只要求我们打印出 alarm ,打印完之后程序崩溃也无所谓。那我们只需要关注基本的定时警告逻辑:每隔 ticks 时间,alarm 一次,或者说 if(p->ticks_since_last_call >= p->alarm_interval)
,同时要记得 alarm 之后将计时器重置,也就是 p->ticks_since_last_call = 0;
最关键的是如何跳转到handler的位置执行函数
// save user program counter.
p->trapframe->epc = r_sepc();
根据 trap.c 中代码提示,或者说你仔细阅读了 chapter 4,epc寄存器是用来保存 trap 之后用户代码的执行地址的。
从内核放回到用户态的时候,pc 寄存器的值从 epc 中恢复,指向发生 trap 时的用户代码
//
// return to user space
//
void
usertrapret(void)
{
...
// set S Exception Program Counter to the saved user pc.
w_sepc(p->trapframe->epc);
// tell trampoline.S the user page table to switch to.
uint64 satp = MAKE_SATP(p->pagetable);
// jump to trampoline.S at the top of memory, which
// switches to the user page table, restores user registers,
// and switches to user mode with sret.
uint64 fn = TRAMPOLINE + (userret - trampoline);
((void (*)(uint64,uint64))fn)(TRAPFRAME, satp);
}
所以我们只需要将 handler 的地址放入 epc 就可以了。
// kernel/trap.c
//
// handle an interrupt, exception, or system call from user space.
// called from trampoline.S
//
void
usertrap(void)
{
...
if(which_dev == 2){
struct proc *p = myproc();
if(p->alarm_interval != 0){
p->ticks_since_last_call++;
if(p->ticks_since_last_call >= p->alarm_interval){
p->trapframe->epc = (uint64) p->alarm_handler;
p->ticks_since_last_call = 0;
}
}
yield();
}
test1/test2(): resume interrupted code
相信你已经通过了 test0 并且在 test1中遇到了麻烦(手动狗头)
test1/test2 不能通过的原因是,我们在执行完 handler 之后,应该放回原来的用户地址,去执行他的下一行代码。比如
sigalarm(0,periodic);
// 执行alarm触发后,执行完periodic,应该执行下一行的printf
printf("pc should be point here");
但是此时我们的代码没有放回原来的 user pc
void
periodic()
{
count = count + 1;
printf("alarm!\n");
sigreturn(); // return 0 just now
}
所以我们要做的就是再添加一个 flag以及用户寄存器的状态,让内核能够帮我们在执行完handler 之后,恢复原来的用户现场。
// kernel/proc.h
// 要保存的用户寄存器有点多,都是trapframe里面的user register,当然你也可以直接保存一个trapframe
int alarmre_flag;
uint64 saved_epc;
uint64 saved_ra;
uint64 saved_sp;
uint64 saved_gp;
uint64 saved_tp;
uint64 saved_t0;
uint64 saved_t1;
uint64 saved_t2;
uint64 saved_s0;
uint64 saved_s1;
uint64 saved_s2;
uint64 saved_s3;
uint64 saved_s4;
uint64 saved_s5;
uint64 saved_s6;
uint64 saved_s7;
uint64 saved_s8;
uint64 saved_s9;
uint64 saved_s10;
uint64 saved_s11;
uint64 saved_a0;
uint64 saved_a1;
uint64 saved_a2;
uint64 saved_a3;
uint64 saved_a4;
uint64 saved_a5;
uint64 saved_a6;
uint64 saved_a7;
uint64 saved_t3;
uint64 saved_t4;
uint64 saved_t5;
uint64 saved_t6;
// kernel/sysproc.c
// int sigreturn(void);
uint64
sys_sigreturn()
{
struct proc *p = myproc();
p->trapframe->epc = p->saved_epc;
p->trapframe->ra = p->saved_ra;
p->trapframe->sp = p->saved_sp;
p->trapframe->gp = p->saved_gp;
p->trapframe->tp = p->saved_tp;
p->trapframe->t0 = p->saved_t0;
p->trapframe->t1 = p->saved_t1;
p->trapframe->t2 = p->saved_t2;
p->trapframe->t3 = p->saved_t3;
p->trapframe->t4 = p->saved_t4;
p->trapframe->t5 = p->saved_t5;
p->trapframe->t6 = p->saved_t6;
p->trapframe->s0 = p->saved_s0;
p->trapframe->s1 = p->saved_s1;
p->trapframe->s2 = p->saved_s2;
p->trapframe->s3 = p->saved_s3;
p->trapframe->s4 = p->saved_s4;
p->trapframe->s5 = p->saved_s5;
p->trapframe->s6 = p->saved_s6;
p->trapframe->s7 = p->saved_s7;
p->trapframe->s8 = p->saved_s8;
p->trapframe->s9 = p->saved_s9;
p->trapframe->s10 = p->saved_s10;
p->trapframe->s11 = p->saved_s11;
p->trapframe->a0 = p->saved_a0;
p->trapframe->a1 = p->saved_a1;
p->trapframe->a2 = p->saved_a2;
p->trapframe->a3 = p->saved_a3;
p->trapframe->a4 = p->saved_a4;
p->trapframe->a5 = p->saved_a5;
p->trapframe->a6 = p->saved_a6;
p->trapframe->a7 = p->saved_a7;
p->alarmre_flag = 0;
return 0;
}
其中 alarmre_flag 是为了防止 handler 还没有 retrurn 的时候再次发生 alarm
// kernel/trap.c
// give up the CPU if this is a timer interrupt.
if(which_dev == 2){
if(p->alarm_interval != 0){
p->ticks_since_last_call++;
if(p->ticks_since_last_call >= p->alarm_interval && p->alarmre_flag == 0){
// 保存用户寄存器
p->saved_epc = p->trapframe->epc;
p->saved_ra = p->trapframe->ra;
p->saved_sp = p->trapframe->sp;
p->saved_gp = p->trapframe->gp;
p->saved_tp = p->trapframe->tp;
p->saved_t0 = p->trapframe->t0;
p->saved_t1 = p->trapframe->t1;
p->saved_t2 = p->trapframe->t2;
p->saved_t3 = p->trapframe->t3;
p->saved_t4 = p->trapframe->t4;
p->saved_t5 = p->trapframe->t5;
p->saved_t6 = p->trapframe->t6;
p->saved_s0 = p->trapframe->s0;
p->saved_s1 = p->trapframe->s1;
p->saved_s2 = p->trapframe->s2;
p->saved_s3 = p->trapframe->s3;
p->saved_s4 = p->trapframe->s4;
p->saved_s5 = p->trapframe->s5;
p->saved_s6 = p->trapframe->s6;
p->saved_s7 = p->trapframe->s7;
p->saved_s8 = p->trapframe->s8;
p->saved_s9 = p->trapframe->s9;
p->saved_s10 = p->trapframe->s10;
p->saved_s11 = p->trapframe->s11;
p->saved_a0 = p->trapframe->a0;
p->saved_a1 = p->trapframe->a1;
p->saved_a2 = p->trapframe->a2;
p->saved_a3 = p->trapframe->a3;
p->saved_a4 = p->trapframe->a4;
p->saved_a5 = p->trapframe->a5;
p->saved_a6 = p->trapframe->a6;
p->saved_a7 = p->trapframe->a7;
p->trapframe->epc = (uint64) p->alarm_handler;
p->ticks_since_last_call = 0;
p->alarmre_flag = 1;
}
}
yield();