1. 程式人生 > 其它 >6.S081-2021-Lab4 Traps學習筆記

6.S081-2021-Lab4 Traps學習筆記

RISC-V assembly

  1. a0-a7存放參數,13放在了a2

  2. 編譯器內聯了這兩個函式,從li a1,12可以看出來,編譯器直接算出了結果,做了內聯優化

  3. 0x630

  4. 0x38,即函式的返回地址

  5. 57616=0xE110
    0x00646c72小端儲存則為72-6c-64-00
    對照ASCII碼錶

    72:r 6c:l 64:d 00:字串結束標識

    輸出為:HE110 World

  6. 取決於呼叫函式之前暫存器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();