MIT6.S081 LAB4 实验记录

Backtrace

回溯(Backtrace)通常对于调试很有用:它是一个存放于栈上用于指示错误发生位置的函数调用列表。

//printf.c
void backtrace(void)
{
  uint64 fp;
  fp = r_fp();//获取当前栈的帧指针
  printf("backtrace:\n");
  while(PGROUNDUP(fp) - PGROUNDDOWN(fp) == PGSIZE) {//当没有达到栈底
    printptr(*(uint64*)(fp - 8));//打印当前函数的返回地址。
    printf("\n");
    fp = *(uint64*)(fp - 16);//切换到上一个栈帧
  }
}

MIT6.S081 LAB4 实验记录_第1张图片
上图为栈区的结构示意图,每一个调用函数都会有一个栈帧,里面存放了与函数相关的信息,栈由内存高地址向低地址拓展。

Alarm

在这个练习中你将向XV6添加一个特性,在进程使用CPU的时间内,XV6定期向进程发出警报

//sysproc.c
//添加两个系统调用,基本流程与之前的实验一样
uint64
sys_sigalarm(void)  // 当消耗cpu达到指定时间,调用其他函数
{
  int n;
  uint64 addr;
  if(argint(0, &n) < 0)
    return -1;
  if(argaddr(1, &addr) < 0)
    return -1;
  myproc()->interval = n;
  myproc()->handle =(void *)addr;
  return 0;
}

uint64
sys_sigreturn(void)
{
  *myproc()->trapframe = *myproc()->alarmtrapframe;
  myproc()->alarmflag = 0;
  return 0;
}
//proc.h
//在proc结构体中添加如下变量
uint64 interval; //需要报警的间隔时间
void   (*handle)();//调用函数
uint64 preinter;//之前累计的间隔时间
struct trapframe *alarmtrapframe;//保存的trapframe
int alarmflag;//是否开启报警的标志位
//trap.c
//在usertrap中添加
  if(which_dev == 2) { //当CPU产生一个时钟中断时
    if(p->alarmflag == 0) {
      p->preinter++;  //累计时间加一
      if(p->preinter == p->interval) {
        *p->alarmtrapframe = *p->trapframe;//保存当前的trapframe
        p->trapframe->epc =(uint64)p->handle;//切换中断的返回地址
        p->preinter = 0; //累计时间清零
        p->alarmflag = 1;
    }
    }
    yield();
  }

当我们完成了alarm进入的函数时,我们希望恢复到没有进入alarm调用的函数之前的状态。我们用trapframe来保存当时的寄存器的值,当完成了调用的函数之后,将寄存器的值恢复为保存的trapframe中的值。恢复原来的状态。
trapframe中的epc的值为当从陷阱返回时候,我们调用的函数的地址。
当中断发生时,系统并不会切换页表,蹦床页面映射在用户空间和内核空间相同的虚拟地址,所以不切换页表也不会发生错误。我们可以将此时用户的寄存器保存在此时进程的trapframe。

在完成系统中断时,我们的主要任务就是如何将进程恢复到完成中断之前的状态,我们利用在内核和用户空间映射相同虚拟地址的蹦床页面和保存相关寄存器的trapframe来完成这个操作。(个人理解,可能有误)

你可能感兴趣的:(操作系统学习,unix,linux)