当前位置 : 主页 > 编程语言 > 其它开发 >

6.S081-2021-Lab4 Traps学习笔记

来源:互联网 收集:自由互联 发布时间:2022-05-30
RISC-V assembly a0-a7 存放参数,13放在了 a2 中 编译器内联了这两个函数,从 li a1,12 可以看出来,编译器直接算出了结果,做了内联优化 0x630 0x38 ,即函数的返回地址 57616=0xE110 0x00646c72小端
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();
网友评论