6.S081 lab4 traps

#Backtrace#

  • kernel/riscv.h中添加hints所给的r_fp函数,这个函数的作用是取出s0寄存器的值,该寄存器保存了当前的帧指针。栈帧的布局图如下:

    image.png

    可以看出返回地址和前一个栈帧的帧指针的保存位置是固定的,分别为fp-8fp-16

  • 弄懂这个图就很容易写出代码了,首先调用r_fp读取当前的帧指针,通过PGROUNDUP对其页面上取整,这里对页面上取整的原因是:可能一个物理页中有多个栈帧嵌套,我们需要打印出所有的返回地址。代码如下:

    复制void backtrace(void)
    {
        printf("backtrace:\n");
        // 读取当前帧指针
        uint64 fp = r_fp();
        uint64 base = PGROUNDUP(fp);
        while (fp < base){
            uint64 ret_adr = *(uint64*)(fp - 8);
            printf("%p\n", ret_adr);
            // 前一个帧指针
            fp = *(uint64*)(fp - 16);
        }
    }
    

#Alarm#

做这个实验之前,一定要弄清楚xv6处理trap的过程,建议好好看一下xv6 book的第四章

  • 加入系统调用原型等操作不再赘述。在struct proc中加入以下字段。

    • alarm_interval:报警间隔
    • alarm_handler:指向处理函数的指针
    • ticks_count:距离上一次定时器中断所经历的ticks
    • alarm_trapframe:发生定时器中断时,进程的trapframe,用来恢复进程的执行
    • flag:标记上一次的报警处理程序是否已经执行完,0表示已处理完报警。

    image.png

  • 更改usertrap以处理计时器中断。当发生计时器中断时,whick_dev的值为2。注意到指向报警处理函数指针可能为空,只有当指针不为空时,才进行相应的处理。代码如下:

    复制void
    usertrap(void)
    {
      ...
      // give up the CPU if this is a timer interrupt.
      if(which_dev == 2){
          if (p->alarm_interval){
              p->ticks_count++;
              if (p->ticks_count == p->alarm_interval && p->flag == 0){
                p->alarm_trapframe = *p->trapframe;
                p->trapframe->epc = (uint64)p->alarm_handler;
                p->flag = 1;
                p->ticks_count = 0;
              }
          }
          yield();
      }
    
      usertrapret();
    }
    

    每发生一次定时器中断,ticks_count就加1,如果此时已经达到了报警间隔并且没有正在处理的报警,则设置相应的参数以转至报警处理函数。注意到上述代码的第11行,进程的trapframe->epc保存了进程下一条指令的地址,把其设置为报警处理函数的地址以便进程跳转到报警处理函数。再看到上述代码的第10行,由于进程执行完报警处理函数之后,应该恢复到原来的执行流,故此处的alarm_trapframe保存了进程被中断时相关的寄存器。设置flag以标记本次报警处理还未完成。

  • 实现sys_sigalarm函数,该函数有两个参数(报警间隔和报警处理函数的指针),分别给进程的两个成员赋值即可。代码如下:

    复制uint64
    sys_sigalarm(void)
    {
        struct proc *p = myproc();
        if (argint(0, &p->alarm_interval) < 0 || argaddr(1, (uint64*)&p->alarm_handler) < 0)
            return -1;
        /* printf("%d %d\n", p->alarm_interval, p->alarm_handler); */
        return 0;
    }
    
  • 实现sys_sigreturn函数,由于报警处理函数结束后,需要恢复进程被定时器中断时的执行流,把p->trapframe恢复到p->alarm_trapframe。该函数被调用时,标志着报警处理函数已经结束,所以应该把flag设置为0。代码如下:

    复制uint64
    sys_sigreturn(void)
    {
        struct proc *p = myproc();
        p->flag = 0;
        *p->trapframe = p->alarm_trapframe;
    
        return 0;
    }
    
  • 在分配进程的allocproc函数中添加新字段的初始化:

    复制static struct proc*
    allocproc(void)
    {
      ...
    found:
      ...
      p->flag = 0;
      p->alarm_interval = 0;
      p->ticks_count = 0;
      ...
      return p;
    }
    
posted @   Kyoz1  阅读(53)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
7
主题色彩
点击右上角即可分享
微信分享提示