MIT 6.S081 Traps

backtrace

利用frame pointer来完成调用追踪。

用户栈结构

// kernel/printf.c
void
backtrace(void)
{
    printf("backtrace:\n");
    uint64 fp = r_fp();

    uint64 pageUpBound = PGROUNDUP(fp);
//    uint64 pageDownBound = PGROUNDDOWN(fp);
    while (fp < pageUpBound) {
        printf("%p\n", *(uint64 *)(fp-8));
        fp = *(uint64 *)(fp - 16);
    }
}

运行结果

xv6 kernel is booting

hart 1 starting
hart 2 starting
init: starting sh
$ ./bttest
backtrace:
0x000000008000211c
0x0000000080001ff6
0x0000000080001cda
$ QEMU: Terminated
sugar@ubuntuServer:~/xv6-labs-2021$ addr2line -e kernel/kernel
0x000000008000211c
0x0000000080001ff6
0x0000000080001cda/home/sugar/xv6-labs-2021/kernel/sysproc.c:63
/home/sugar/xv6-labs-2021/kernel/syscall.c:140
/home/sugar/xv6-labs-2021/kernel/trap.c:76

Alarm

test0

通过系统调用sigalarm注册timer中断处理程序,时间间隔到了之后,执行用户定义的函数。

首先增加新的系统调用

// user/user.h
int sigalarm(int ticks, void (*handler)());
int sigreturn(void);

// user/usys.pl
entry("sigalarm");
entry("sigreturn");


// kernel/syscall.h
#define SYS_sigalarm 22
#define SYS_sigreturn 23

// kernel/syscall.c
extern uint64 sys_sigalarm(void);
extern uint64 sys_sigreturn(void);

static uint64 (*syscalls[])(void) = {
 ....
[SYS_sigalarm] sys_sigalarm,
[SYS_sigreturn] sys_sigreturn,
};

uint64
sys_sigalarm(void)
{
    int interval;
    uint64 handler;
    if (argint(0, &interval) < 0)
        return -1;
    if (argaddr(1, &handler) < 0)
        return -1;

    struct proc *p = myproc();
    p->handler = handler;
    p->interval = interval;
    p->ticks = 0;

    return 0;
}

uint64
sys_sigreturn(void)
{
    return 0;
}

修改proc的结构,保存定时器相关的记录成员(间隔,处理函数指针,自上次调用流逝了多少ticks,是否正在处理alarm)

// Per-process state
struct proc {
  // for alarm
  int interval;                 // the alarm interval
  uint64 handler;            // the periodically called function pointer
  int ticks;                    // time since last call
  int in_alarm;                 // while the kernel is processing alarm handler
};

// kernel/proc.c
static struct proc*
allocproc(void)
{
....
found:
  p->pid = allocpid();
  p->state = USED;
  // initialize for alarm
  p->ticks = 0;
  p->handler = 0;
  p->interval = 0;
  p->in_alarm = 0;
....
}

最后在trap.c中处理时钟中断,注意,从内核空间返回后,执行的指令地址会从trapframe的epc复制到pc,进而执行相关的handler函数(alarm)

// give up the CPU if this is a timer interrupt.
  if(which_dev == 2) {
      // timer interrupt
      if (p->interval != 0) {
          p->ticks++;  // 在处理alarm的时候也需要计时
          if (p->in_alarm == 0) {
              if (p->ticks > p->interval) {
                  p->ticks = 0;
                  p->in_alarm = 1;
//              printf("Call the handler.\n");
//              p->handler();
                  p->trapframe->epc = p->handler;  // 从内核空间返回的时候,执行的地址
              }
          }
      }
      yield();
  }

运行结果

$ ./alarmtest
test0 start
......alarm!
test0 passed
test1 start
...............................................................QEMU: Terminated

test1

实现从中断处理程序中返回到用户程序,要增加保存的寄存器。

// kernel/proc.h
// Per-process state
struct proc {
  ......
  // for alarm
  int interval;                 // the alarm interval
  uint64 handler;            // the periodically called function pointer
  int ticks;                    // time since last call
  int in_alarm;                 // while the kernel is processing alarm handler
  int user_epc;              // user program's pc
  struct trapframe user_frame;  // saved 32 user's register
};

// kernel/sysproc.c
uint64
sys_sigreturn(void)
{
    struct proc *p = myproc();
    p->trapframe->epc = p->user_epc;  // 原始用户进程的epc
    memmove(p->trapframe, &p->user_frame, sizeof (p->user_frame));
    p->in_alarm = 0;
    return 0;
}

//kernel/trap.c
void
usertrap(void)
{
...
// give up the CPU if this is a timer interrupt.
  if(which_dev == 2) {
      // timer interrupt
      if (p->interval != 0) {
          p->ticks++;  // 在处理alarm的时候也需要计时
          if (p->in_alarm == 0) {
              if (p->ticks > p->interval) {
                  p->ticks = 0;
                  p->in_alarm = 1;
                  p->user_epc = p->trapframe->epc;
                  // memmove(dst, src, size)
                  memmove(&p->user_frame, p->trapframe, sizeof(p->user_frame));
                  p->trapframe->epc = p->handler;  // 从内核空间返回的时候,执行的地址
              }
          }
      }
      yield();
  }
...
}