【XV6】 traps

代码:https://github.com/JasenChao/xv6-labs.git

backtrace

题目要求实现backtrace来对堆栈上调用发生错误的地方进行跟踪。寄存器s0包含指向当前堆栈帧的指针,那么返回地址就位于帧指针的固定偏移量-8,前一个fp地址的偏移量为-16。

riscv.h文件中增加提示中的代码:

static inline uint64
r_fp()
{
    uint64 x;
    asm volatile("mv %0, s0" : "=r" (x) );
    return x;
}

printf.c文件中实现backtrace()函数:

void
backtrace(void)
{
    uint64 fp = r_fp();
    uint64 high = PGROUNDUP(fp), low = PGROUNDDOWN(fp);
    printf("backtrace:\n");
    while(fp >= low && fp < high)
    {
      printf("%p\n", *((uint64*)(fp - 8)));
      fp = *((uint64*)(fp - 16));
    }
}

defs.h文件中增加函数声明void backtrace(void)

测试程序是运行sleep函数调用backtrace,因此在sysproc.c文件中的sys_sleep函数末尾调用backtrace()

使用make GRADEFLAGS=backtrace grade测试结果是否正确。

alarm

题目要求基于给出的alarmtest函数,实现两个系统调用sigalarmsigreturn,在程序消耗n个ticks之后调用处理程序。

首先在Makefile中添加测试程序$U/_alarmtest\

user.h中添加声明,同样在usys.plsyscall.hsyscall.c中添加相应的内容:

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

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

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

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

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

proc.h中的struct proc中增加成员:

struct trapframe *pre_trapframe;
int interval;                // 触发alarm的中断数量
int ticks;                   // 时钟中断数量
uint64 handler;              // 处理alarm的函数地址

为了让pre_trapframe保存上一个trapframe,需要对proc.c中的allocprocfreeproc进行修改:

// allocproc
if((p->pre_trapframe = (struct trapframe *)kalloc()) == 0){
  freeproc(p);
  release(&p->lock);
  return 0;
}

// freeproc
if(p->pre_trapframe)
  kfree((void*)p->pre_trapframe);
p->pre_trapframe = 0;

sysproc.c文件中实现sigalarmsigreturn函数:

uint64
sys_sigalarm(void){
  int interval;
  uint64 f_addr;
  struct proc *p = myproc();

  argint(0,&interval);
  argaddr(1,&f_addr);
  // 保存触发次数和函数地址
  p->interval = interval;
  p->handler = f_addr;
  p->ticks = 0;  

  return 0;
}

uint64
sys_sigreturn(void){
  struct proc* p = myproc();
  *p->trapframe = *p->pre_trapframe;
  p->ticks = 0;
  return p->pre_trapframe->a0;
}

修改trap.c中的usertrap函数,当触发时间中断时进行判断,如果满足触发次数则执行触发函数:

// give up the CPU if this is a timer interrupt.
if(which_dev == 2) {
  p->ticks++;
  if(p->ticks == p->interval && 0 < p->interval) {
    *p->pre_trapframe = *p->trapframe;
    p->trapframe->epc = p->handler;
  } else {
    yield();
  }
}

使用make GRADEFLAGS=alarmtest grade命令测试是否通过。

测试结果

使用make grade测试,结果如下:

== Test backtrace test ==
$ make qemu-gdb
backtrace test: OK (1.6s)
== Test running alarmtest ==
$ make qemu-gdb
(3.6s)
== Test   alarmtest: test0 ==
  alarmtest: test0: OK
== Test   alarmtest: test1 ==
  alarmtest: test1: OK
== Test   alarmtest: test2 ==
  alarmtest: test2: OK
== Test   alarmtest: test3 ==
  alarmtest: test3: OK
== Test usertests ==
$ make qemu-gdb
usertests: OK (39.9s)

这部分题目太难了,参考了 https://zhuanlan.zhihu.com/p/624606664 的大神详解。

posted on 2024-02-14 19:04  未连接到互联网  阅读(13)  评论(0编辑  收藏  举报