0


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:0x0000000080002cda0x0000000080002bb60x0000000080002898

实验实现

先看提示

  • 编译器会往每一个帧栈中放入一个指向调用者地址的frame pointer(指向某个函数使用的栈地址的顶端–从高往低)。你的 backtrace 应该利用这些 frame pointer 来遍历栈,打印每个帧栈的返回地址
  • gcc编译器将当前的frame pointer 储存在 寄存器 s0,在kernel/riscv.h 加入以下函数来获取它
staticinline uint64
r_fp(){
  uint64 x;asmvolatile("mv %0, s0":"=r"(x));return x;}
  • 返回地址储存在距离帧栈顶部(-8)偏移处, 指向上一个帧栈的指针储存在帧栈顶部(-16)偏移处请添加图片描述
  • xv6 为每个栈分配一页的大小,并且栈底地址是 PGSIZE 对齐的,你可以使用 PGROUNDDOWN(fp)PGROUNDUP(fp) 来计算栈顶和栈底的地址。(可用于计算 backtrace 的终止条件)

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

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

递归地进行

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

代码实现

/*    kernel/printf.c    */voidbacktrace(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 返回。另外,在正在运行回调函数时,我们不希望再次中断运行回调函数,所以还需要加一个标志位标识是否正在运行回调函数。
structproc{......int interval;// interval of alarmint since_interval;// last call until nowvoid(*handler)();// function pointerint running_hand;// flag of runningstructtrapframe 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){structproc*p =myproc();
  p->since_interval =0;*(p->trapframe)= p->trapframe_cp;
  p->running_hand =0;return0;}

uint64
sys_sigalarm(void){int interval;
  uint64 pointer;if(argint(0,&interval)<0)return-1;if(interval ==0)return0;if(argaddr(1,&pointer)<0)return-1;myproc()->handler =(void*)pointer;myproc()->since_interval =0;myproc()->running_hand =0;myproc()->interval = interval;return0;}/*    proc.c    */staticstructproc*allocproc(void){....
found:
  p->pid =allocpid();
  p->interval =0;
  p->since_interval =0;
  p->handler =0;....}/* trap.c    */voidusertrap(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();}......}

实验结果

请添加图片描述

标签: 操作系统

本文转载自: https://blog.csdn.net/qq_25698501/article/details/121051947
版权归原作者 Wound+=s 所有, 如有侵权,请联系我们删除。

“xv6-lab4-trap”的评论:

还没有评论