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. 若计数器达到时钟间隔,则 - 先保存进程的用户上下文 trapframe- 回调函数运行标志位置1- 通过设置程序计数器调用回调函数
- 回调函数调用完毕后,test程序会调用 sigreturn,sigreturn 需要做的事有 - 重置计数器- 还原进程用户上下文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();}......}
实验结果
版权归原作者 Wound+=s 所有, 如有侵权,请联系我们删除。