xv6-lab4-trap

Lab:traps

前置知识

  • xv6 book 第四章
  • kernel/trampolines.S 涉及从用户态到内核态以及返回的代码
  • kernel/trap.c 处理中断的代码

RISC-V assembly

实验目标

该实验旨在让你熟悉一些汇编代码

Backtrace

实验目标

kernel/printf.c 中实现一个函数 backtrace(), 并在 sys_sleep 中调用它。

backtrace() 会打印当前栈上所有函数调用返回地址

backtrace:
0x0000000080002cda
0x0000000080002bb6
0x0000000080002898

实验实现

先看提示

  • 编译器会往每一个帧栈中放入一个指向调用者地址的frame pointer(指向某个函数使用的栈地址的顶端–从高往低)。你的 backtrace 应该利用这些 frame pointer 来遍历栈,打印每个帧栈的返回地址
  • gcc编译器将当前的frame pointer 储存在 寄存器 s0,在kernel/riscv.h 加入以下函数来获取它
static inline uint64
r_fp()
{
  uint64 x;
  asm volatile("mv %0, s0" : "=r" (x) );
  return x;
}
  • 返回地址储存在距离帧栈顶部(-8)偏移处, 指向上一个帧栈的指针储存在帧栈顶部(-16)偏移处
    请添加图片描述

  • xv6 为每个栈分配一页的大小,并且栈底地址是 PGSIZE 对齐的,你可以使用 PGROUNDDOWN(fp)PGROUNDUP(fp) 来计算栈顶和栈底的地址。(可用于计算 backtrace 的终止条件)

综上所述,我们得出一个 backtrace 的大概设计方案

先使用 r_fp() 获得当前的帧栈顶部地址

递归地进行

  • 打印返回地址(-8)
  • 获取指向上一个帧栈顶部的指针(-16)
  • 解析指针,判断是否到达栈底

代码实现

/*	kernel/printf.c	*/
void
backtrace(void)
{
  printf("backtrace:\n");

  uint64 addr = r_fp();
  while (PGROUNDUP(addr) - PGROUNDDOWN(addr) == PGSIZE)
  {
    uint64 ret_addr = *(uint64 *)(addr - 8);
    printf("%p\n", ret_addr);
    addr = *((uint64 *)(addr - 16));
  }
  
}

/*	sysproc.c	*/
uint64
sys_sleep(void)
{
  backtrace();
  ......
}

Alarm

实验目标

实现系统调用*sigalarm(int n, (void )handler)sigreturn()

当它被调用后, 系统将在 n 个时钟中断后执行 handler,执行完毕后调用 sigreturn 返回

实验实现

先看提示

  • 你需要保持对时钟间隔 n 和回调函数指针 handler 的记录。(在 proc structure 中记录)。除此之外,我们还需要判断当前是否以达到时钟间隔,并且在调用回调函数前,需要保存当前进程的用户上下文(trapframe),以用于 sigreturn 返回。另外,在正在运行回调函数时,我们不希望再次中断运行回调函数,所以还需要加一个标志位标识是否正在运行回调函数。
struct proc {
  ......
  int interval;                // interval of alarm
  int since_interval;          // last call until now
  void (*handler)();           // function pointer
  int running_hand;            // flag of running
  struct trapframe trapframe_cp;  //copy of trapframe_cp
};
  • allproc 中初始化你新增的字段

  • 每个 tick,硬件都会发出一个时钟中断到内核, 在 usertrap() 中被处理

if(which_dev == 2) ...

综上所述,我们得出大概得设计方案

  1. 在每次时钟中断处,将时钟计数器+1. 若计数器达到时钟间隔,则
    • 先保存进程的用户上下文 trapframe
    • 回调函数运行标志位置1
    • 通过设置程序计数器调用回调函数
  2. 回调函数调用完毕后,test程序会调用 sigreturnsigreturn 需要做的事有
    • 重置计数器
    • 还原进程用户上下文trapframe
    • 回调函数运行标志位置0

代码实现

/*
	系统调用声明部分略过
*/

/*	sysproc.c	*/
uint64
sys_sigreturn(void)
{
  struct proc *p = myproc();
  p->since_interval = 0;
  *(p->trapframe) = p->trapframe_cp;
  p->running_hand = 0;
  return 0;
}

uint64
sys_sigalarm(void)
{
  int interval;
  uint64 pointer;
  if(argint(0, &interval) < 0)
    return -1;
  if(interval == 0)
    return 0;
  if(argaddr(1, &pointer) < 0)
    return -1;
  myproc()->handler = (void *)pointer;
  myproc()->since_interval = 0;
  myproc()->running_hand = 0;
  myproc()->interval = interval;
  return 0;
}

/*	proc.c	*/
static struct proc*
allocproc(void)
{
  ....
found:
  p->pid = allocpid();
  p->interval = 0;
  p->since_interval = 0;
  p->handler = 0;
  ....
}


/* trap.c	*/
void
usertrap(void)
{
  .......
  // give up the CPU if this is a timer interrupt.
  if(which_dev == 2)
  {
    if(p->interval != 0)
    {
      if(!p->running_hand)
        p->since_interval = p->since_interval + 1;
      if(!p->running_hand && p->since_interval == p->interval)
      {
        printf("alarm!\n");
        p->running_hand = 1;
        p->trapframe_cp = *(p->trapframe);
        p->trapframe->epc = (uint64)p->handler;
      }
    }
    yield();
  }
  ......
}

实验结果

请添加图片描述

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值