• MIT6.S081 Lab traps


    Lab traps

    RISC-V assembly

    阅读 call.asmf(),g() 函数,回答一些问题:

    Ans

    Backtrace

    backtrace 对于 debug 很有用:在 error 发生时输出一列栈上的函数调用链。
    编译器在每个 stack frame 中存放有一个 frame 指针(To Prev.Frame(fp)),这个 fp 指针指向调用者的 frame pointer 。本实验需要使用这些 fp 遍历 stack 区域然后打印每个 stack frame 保存的返回地址(return address)。

    stack frames 的布局:

     --->Return Address
     |   To Prev.Frame(fp)
     |   Saved Registers
     |   Local Variables
     |   ...
     |   Return Address
     ----To Prev.Frame(fp)
         Saved Registers
         Local Variables
         ...
    

    $sp 指向的是当前 stack frame 的底部(低地址)
    $fp 指向的是当前 stack frame 的顶部(高地址)
    当前 $fp - 8 的位置是 Return Address , $fp - 16 的位置是 To Prev.Frame(fp)。

    void
    backtrace(void)
    {
      printf("backtrace:\n");
    
      uint64 fp = r_fp(); // top address of current stack frame
      uint64 stackpagetop = PGROUNDUP(fp); // top address of stack page
    
      while (fp < stackpagetop)
      {
        printf("%p\n", *(uint64 *)(fp - 8)); // Saved registers: $ra
        fp = *(uint64 *)(fp - 16); // Saved registers: To Prev.Frame
      }
    }
    

    Alarm

    为xv6增加一个特性:定期向一个使用CPU时间的进程发出警报。对于想限制占用CPU时间的进程很有用,对于想进行计算又想执行周期任务的进程很有用。
    更一般的说:实现一个原始的用户级 interrupt/fault handlers 。也可以使用类似的方法处理应用中的缺页。

    需要增加一个新的 sigalarm(interval, handler) 系统调用。如果一个应用调用 sigalarm(n, fn),那么应用程序每消耗 \(n\) 个ticks,内核需要让应用程序执行函数 fn(test0)。当 fn 返回时,应用程序需要恢复 fn 函数执行之前的代码执行(test1/test2的实验目标)。tick 是 xv6 的时间单元,由硬件时钟产生中断决定。如果应用程序调用 sigalarm(0,0),内核需要停止生成周期时钟调用。

    kernel/proc.h 中定义alarm相关属性。

    • histrapframe 用于在 \(n\) 个 ticks 后,执行 fn 前,在内核中保存用户态的用户寄存器,因为此次返回用户态并不是为了恢复正常代码的执行,而是为了执行 fn。在 fn 中必须有 sigreturn 系统调用再次进入内核,同时用 histrapframe 恢复原用户态的寄存器状态,从而在返回用户态时恢复正常执行。流程为:
      原流程代码 --> 调用sigalarm系统调用 -->
      进入内核设置alarm相关数据-->
      返回用户态执行原流程代码 -->
      ticks 到,因为时钟中断,所以此时在内核态,设置 $sepc 指向 fn,保存原 trapframe -->
      返回用户态,但是执行的是fn,而不是原流程代码 -->
      在 fn 中通过 sigreturn 再次进入内核态,恢复原流程代码的trapframe -->
      返回用户态执行原流程代码
      
    • alarmisreturn 用来标记 handler 是否返回,如果 handler 没有返回,则内核不能再次调用它。如 handler 的处理时间大于给定时间 \(n\) ticks 的时候:在用户态处理 handler 时,因为 ticks 到期再次 trap 进入内核,尽管时钟满足条件,但是 handler 没有返回,所以内核不应该再次调用 handler。
      分析下,实验这样限制的原因:当一个handler没有结束,再响应一个handler时,新的handler会污染原handler的PC和trapframe,造成程序执行异常。
    • alarminterval 表示周期数 \(n\)alarmpassed 表示当前经过的时间, (*alarmhandler)() 表示fn的函数指针。
    int alarminterval;               // sigalarm alarm interval
    int alarmpassed;                 // sigalarm passed time
    void (*alarmhandler)();          // sigalarm handler
    struct trapframe histrapframe;   // save trapframe to retrieve to original code from alarm handler.
    int alarmisreturn;               // 0 is not return so that can not re-enter handler. 1 can do.
    

    kernel/sysproc.c:取出系统调用的参数,配置 proc 的 alarm 相关属性。

    // After every n "ticks" of CPU time that the program consumes,
    // the kernel should call fn.
    uint64
    sys_sigalarm(void)
    {
      int n;
      uint64 addr;
    
      if (argint(0, &n) < 0 || argaddr(1, &addr) < 0)
        return -1;
    
      myproc()->alarminterval = n;
      myproc()->alarmpassed = 0;
      myproc()->alarmisreturn = 1;
      myproc()->alarmhandler = (void(*)())addr;
    
      return 0;
    }
    
    uint64
    sys_sigreturn(void)
    {
      *(myproc()->trapframe) = myproc()->histrapframe;
      myproc()->alarmpassed = 0;
      myproc()->alarmisreturn = 1;
      return 0;
    }
    

    kernel/trap.c:判断是否应该执行 alarm handler(时钟周期到且上一个alarm handler已经返回)。保存 trapframe 以便 sigreturn 中返回,记录当前 alarm handler 没有返回,防止后续 handler 的“覆盖“执行。

    // give up the CPU if this is a timer interrupt.
      if (which_dev == 2) {
        if (p->alarminterval > 0) {
          p->alarmpassed++;
          if (p->alarmisreturn && p->alarmpassed >= p->alarminterval) {
            p->histrapframe = *(p->trapframe);
            p->trapframe->epc = (uint64)p->alarmhandler;
            p->alarmisreturn = 0;
          }
        }
    
    

    Code

    Code:Lab traps

  • 相关阅读:
    log4j2 配置详解
    MANIFEST.MF文件详解
    assembly.xml
    firewall 和 iptables 常用命令
    Spring boot 使用 configuration 获取的属性为 null
    使用 maven-assembly-plugin 打包项目
    使用 Maven 插件将 class(字节码文件),resource(资源文件),lib(依赖的jar包)分开打包
    HttpClient 传输文件的两种方式
    IDEA中读取 resource目录下文件
    3.2、Android Studio在物理设备中运行APP
  • 原文地址:https://www.cnblogs.com/seaupnice/p/15827116.html
Copyright © 2020-2023  润新知