Lab4: Traps

1 RISC-V Assembly

主要是回答一下关于汇编的问题,难度不大

2 Backtrace

实现一个函数,能够打印出栈上函数调用链,以帮助出错时的调试。

原理也比较简单:利用栈结构的性质(返回地址和上一个栈帧指针在栈中存放位置是固定的),由当前栈帧指针开始,不断向上得到栈中返回地址,直到到达栈的底部。

void
backtrace(void){
  printf("backtrace:\n");
  // get the current frame pointer
  uint64 fp = r_fp();
  // attention: stack grow from high address to low address
  uint64 stack_bottom = PGROUNDUP(fp); // note the bottom of stack
  while (1) {
    uint64 ret_addr = *((uint64 *)(fp - 8)); // get the return address
    printf("%p\n", ret_addr);
    fp = *((uint64 *)(fp - 16)); // get the previous frame pointer
    if (PGROUNDUP(fp) != stack_bottom) break; // judge the fp cross over the stack_bottom or not
  }
}

3 Alarm

要求实现一个机制,在调用了sigalarm(interval, handler)后,该进程每消耗interval个时间片,就要调用一次handler函数。

3.1 思路

实现的核心是:编写sigalarmsigreturn两个系统调用和修改usertrap中处理时钟中断的部分代码。

下图是核心函数之间的调用关系,也是该部分的重点和难点,因为涉及内核空间和用户空间的频繁切换。

重点需要理解的有两点:

  1. usertrap位于内核空间,而handler function位于用户空间,3号箭头应该如何发生?

    由于usertrap是在处理异常的一个环节中,最后还是会返回到用户空间中,如果不做处理返回的是源代码发生中断的位置,而该位置是由sepc寄存器保存的,在usertrapsepc寄存器中的值被保存在了p->trapframe->epc中,所以只要将p->trapframe->epc的值设置成handler function的地址即可

  2. sigreturn系统调用是handler function调用的,如何使其返回时回到user code原先被时钟中断的那部分代码,即5号箭头如何发生?

    这一部分其实和第一点类似,只要设置p->trapframe->epc就可以使其返回到原先被中断处,但是还需要考虑如何恢复源代码的上下文,就需要恢复所有的寄存器。所以需要在usertrap中保存p->trapframe中的所有值,然后在sys_sigreturn处恢复

3.2 实现

两个系统调用

// return from the handler function to user code which interrupted by time interrupt
int 
sys_sigreturn(void)
{
  struct proc* p = myproc();
  memmove(p->trapframe, p->savedtrapframe, sizeof(struct trapframe)); // restore all the registers
  p->inhandler = 0;
  p->ticksincelast = 0;
  return 0;
}

int
sys_sigalarm(void){
  int ticks; uint64 fn_addr;
  if (argint(0, &ticks) < 0 || argaddr(1, &fn_addr) < 0)
    return -1;
  struct proc* p = myproc();
  if (ticks == 0 && fn_addr == 0) { // stop generating periodic alarm calls. 
    p->ticks = -1;
    p->handler_p = 0;
  } else {
    p->ticks = ticks;
    p->handler_p = fn_addr;
  }
  p->ticksincelast = 0;
  return 0;
}

usertrap中针对时钟中断的的实现

if(which_dev == 2){
    if (ticks >= 0 && !p->inhandler){
        p->ticksincelast ++;
        // kernel cause the user process to call the handler function
        // current is in kernelspace, while the handler function address is in userspace
        // so just set the p->trapframe->epc to the address of the handler function
        // when return to userspace, will call the handler function 
        if (p->ticksincelast == p->ticks) {
            memmove(p->savedtrapframe, p->trapframe, sizeof(struct trapframe));
            p->trapframe->epc = p->handler_p;
            p->inhandler = 1;
        }
    }
    yield();
}

3.3 问题

1、usertrap中只保存了handler函数处理完的返回地址,并没有保存一整套寄存器,导致回到user code之后,上下文改变了,所以发生了异常

2、p->savedtrapframe在初始化时没有分配内存,导致发生了内存store时的缺页异常。

3、p->savedtrapframe没有释放

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值