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)); } }
首先在 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(); }
相信你已经通过了 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();