6.S081-2021-Lab4 Traps學習筆記
RISC-V assembly
-
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
中有什麼
Backtrace
棧幀中的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 加上
test0: invoke handler
在 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();