Lab4 traps

RISC-V assembly (easy)

将问题的答案添加到answers-traps.txt

Q: Which registers contain arguments to functions? For example, which register holds 13 in main’s call to printf?
A: a0-a7; a2;

Q: Where is the call to function f in the assembly code for main? Where is the call to g? (Hint: the compiler may inline functions.)
A: There is none. g(x) is inlined within f(x) and f(x) is further inlined into main()

Q: At what address is the function printf located?
A: 0x0000000000000628, main calls it with pc-relative addressing.

Q: What value is in the register ra just after the jalr to printf in main?
A: 0x0000000000000038, next line of assembly right after the jalr

Q: Run the following code.

unsigned int i = 0x00646c72;
printf(“H%x Wo%s”, 57616, &i);

What is the output?
If the RISC-V were instead big-endian what would you set i to in order to yield the same output?
Would you need to change 57616 to a different value?
A: “He110 World”; 0x726c6400; no, 57616 is 110 in hex regardless of endianness.

Q: In the following code, what is going to be printed after ‘y=’? (note: the answer is not a specific value.) Why does this happen?

printf(“x=%d y=%d”, 3);

A: A random value depending on what codes there are right before the call.Because printf tried to read more arguments than supplied.
The second argument 3 is passed in a1, and the register for the third argument, a2, is not set to any specific value before the
call, and contains whatever there is before the call.

Backtrace (moderate)

这个task要求我们在kernel/print.c添加backtrace()函数,用来打印内核调用栈信息(return address);hints中给了r_fp()用来获取当前的frame point:

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

下图是risc-v体系结构下,c语言程序的调用栈布局:

由于return address以及prev Frame都在栈帧的固定位置,分别是fp+4, fp+8(fpuint64类型),因此我们可以不断遍历整个内核调用栈打印出每个return address;另外一个要点是,要小心遍历过程中超出内核栈的页面范围导致panic,这要求我们在遍历过程中fp始终在同一个页面中:

// kernel/print.c  加到defs.hvoid backtrace() {uint64 fp = r_fp();// 内核栈的页面基址uint64 pgbase = PGROUNDDOWN((uint64)fp);while (PGROUNDDOWN((uint64)fp) == pgbase)  // 必须位于同一个页面中{printf("%p\n", *(uint64*)(fp - 8));  // 打印return addressfp = *(uint64*)(fp - 16);            // point to prev frame}
}

然后在sys_sleep中调用:

// kernel/sysproc.cuint64
sys_sleep(void)
{backtrace();int n;uint ticks0;if(argint(0, &n) < 0)return -1;acquire(&tickslock);ticks0 = ticks;while(ticks - ticks0 < n){if(myproc()->killed){release(&tickslock);return -1;}sleep(&ticks, &tickslock);}release(&tickslock);return 0;
}

上图是执行结果,在另一个终端的项目目录下执行riscv64-unknown-elf-addr2line -e kernel/kernel,并粘贴上图输出的return address就能打印出对应的调用函数:

Alarm (hard)

这个task的要实现一个时钟中断处理函数,当时钟中断达到一定次数后执行用户态的回调函数。这里的一个难点就是回调函数在用户态,不能在内核中直接调用;另外回调函数不在进程的原始执行序列中,因此要执行回调函数必须在内核态手动地改变指令流;由于执行了回调函数,回调函数会修改寄存器状态即改变了进程状态;因此必须在回调函数执行前保存进程状态,在回调函数执行完成后恢复进程状态;

userver将寄存器状态暂存在trapframe中,而userrettrapframe恢复到寄存器中;因此为了保护进程状态,我们只需要在时钟中断处理函数sys_sigalrm中暂存trapframe,并在sys_sigreturn中恢复trapframe;

另一点需要注意的是,我们必须避免在回调函数执行过程中再次执行回调函数,这样会破坏我们暂存的trapframe;为此我们需要记录当前进程是否在执行回调函数,如果是则不再执行回调函数即可。

为struct proc增加相应的字段:

struct proc{...int pending;             // 不为0表示当前进程已经处于alarm回调处理过程int intervals;           // intervals > 0 表明开启了alarm回调int tickers;             // 已经经过的tickersvoid(* handler)();       // 回调函数,位于用户态struct trapframe *alarm_trapframe;  // 执行alarm handler时用来保存进程的trapfram
};

sigalarmsigreturn 具体实现:

// kernel/sysproc.c
uint64 sys_sigalarm(void)
{int n;uint64 fn;if (argint(0, &n) < 0)return -1;if (argaddr(1, &fn) < 0)return -1;struct proc* p = myproc();p->intervals = n;p->tickers = n;p->handler = (void(*)())(fn);return 0;
}uint64 sys_sigreturn(void)
{struct proc* p = myproc();*p->trapframe = *p->alarm_trapframe;// 这里必须是值拷贝p->pending = 0;return 0;
}

allocproc()中初始化相应字段,并分配alarm_trapframe的页面;相应地在freecproc()中还原字段的值,并销毁alarm_trapframe的页面:

// kernel/proc.cstatic struct proc*
allocproc(void)
{...found:p->pid = allocpid();// Allocate a trapframe page.if((p->trapframe = (struct trapframe *)kalloc()) == 0){release(&p->lock);return 0;}// Allocate a trapframe page for alarm_trapframe.if((p->alarm_trapframe = (struct trapframe *)kalloc()) == 0){release(&p->lock);return 0;}p->intervals = 0;p->tickers = 0;p->handler = 0;p->pending = 0;// An empty user page table.p->pagetable = proc_pagetable(p);if(p->pagetable == 0){freeproc(p);release(&p->lock);return 0;}...
}static void
freeproc(struct proc *p)
{...// 释放分配给alarm_trapfram的页面if(p->alarm_trapframe)kfree((void*)p->alarm_trapframe);p->alarm_trapframe = 0;// 清空alarm中断处理相关字段p->intervals = 0;p->pending = 0;p->tickers = 0;p->handler = 0;p->state = UNUSED;
}

usertrap() 函数中,为时钟中断增加alarm的回调机制:

 // kernel/trap.cvoid
usertrap(void)
{...// give up the CPU if this is a timer interrupt.if (which_dev == 2) {// 设置了alarm中断处理// 如果上一个回调函数还没有执行完毕,就不再处理新的中断if (p->intervals > 0 && p->pending == 0){p->tickers -= 1;if (p->tickers <= 0){p->tickers = p->intervals;// 暂存进程的trapfram,用于在中断处理结束后恢复*p->alarm_trapframe = *p->trapframe;  // 这里必须是值拷贝p->trapframe->epc = (uint64)p->handler; // 返回用户态时执行中断处理函数p->pending = 1;}}yield();}usertrapret();
}

s081-2020 Lab4 traps相关推荐

  1. MIT 6.S081 Lab4 traps

    #Lab4: traps #Source #My Code #Motivation #Backtrace (moderate) #Motivation #Solution #S0 - RISC-V 栈 ...

  2. MIT6.S081学习总结-lab4:traps

    lab4 是traps相关 Backtrace 添加一个backtrace函数,sys_sleep调用这个函数后可以打印出函数调用栈 实现: kernel/riscv.h里添加函数来获取frame p ...

  3. 6.S081 Lab4 Traps

    第一部分 RISC-V assembly 阅读汇编 相关的C代码: #include "kernel/param.h" #include "kernel/types.h& ...

  4. 6.S081 lab4: traps

    1. 写在前面   感觉这个lab给我最大的收获就是:不加参数的cat,用ctrl+D退出(笑).之前用cat的时候忘了写参数然后就陷入cat的循环里去了,不知道怎么退出,就只好强行关掉shell.另 ...

  5. 2020 MIT6.s081 os Lab: page tables

    文章目录 实验链接 Print a page table A kernel page table per process Simplify 实验结果 提交实验 查看结果 参考链接 github地址 友 ...

  6. XV6实验(2020)

    XV6实验记录(2020) 环境搭建 参考连接 Lab guidance (mit.edu) 6.S081 / Fall 2020 (mit.edu) xv6 book中文版 Lab1:Xv6 and ...

  7. MIT6.S081 2021

    MIT6.S081 2021 环境配置 Xv6 and Unix utilities vscode格式化头文件排序问题 以地址空间的视角看待变量 其他 代码参考 system calls trace ...

  8. 6.S081 Xv6 Lab 5: lazy page allocation

    Lab: xv6 lazy page allocation https://pdos.csail.mit.edu/6.S081/2020/labs/lazy.html 新的 2020 版哦. $ gi ...

  9. 「实验记录」MIT 6.S081 Lab7 multithreading

    #Lab7: multithreading I. Source II. My Code III. Motivation IV. Uthread: switching between threads ( ...

  10. mit 6.s081

    简介 xv6-book chapter1 Operating system interfaces chapter2 Operating system organization Code:startin ...

最新文章

  1. 第三波精品Android源码袭来!免费下载
  2. 线程的状态:分离(detached)和joinable(可结合的)
  3. lua源代码分析02:内存管理
  4. WebService怎样在IIS上部署
  5. 如何搭建一个打印荣誉证书的网站_如何搭建一个免费的作品集网站
  6. 扎心!“就业难”背后的“十宗罪”我竟占了一半
  7. Hadoop RPC实例
  8. qq群 html,我的群组-普通群组.html
  9. 数据科学项目_完整的数据科学组合项目
  10. Java——数据结构之顺序表
  11. 算法精解-C语言描述 递归和尾递归 (图解+实例)
  12. seaweedfs-client适配高版本的seaweedfs服务
  13. python while循环的用法_Python while循环语句详解
  14. mysql拼接两列数据_Mysql合并两列数据
  15. linux下安装配置dble--新手入门
  16. 已知鸡兔的脚一共80只C语言,鸡兔同笼鸡比兔多10只但鸡脚却比兔脚少60只问鸡兔各几只...
  17. Oracle数据库CPU使用率过高处理记录
  18. linux显示mem进行排序,linux下top命令显示详解
  19. 学历、长相、家境普通的人,未来的发展方向是什么?00后的职业规划都已经整得明明白白
  20. 安利 3 个 pandas 数据探索分析神器!

热门文章

  1. 漏洞扫描器——nmap的使用
  2. Cisco Packet Tracer思科模拟器中OSPF动态路由配置
  3. 单元测试用例如何编写
  4. 彻底解决SP2下ALEXA工具条无法显示(转)
  5. 专家视点:杜绝木马的干扰-防范后门的技巧(转)
  6. 爬取豆瓣电影TOP100
  7. java 资源描述文件_j2me新手必看-Java应用描述文件(JAD)编辑器
  8. 台达变频器485通讯接线图_台达变频器怎么接线 台达变频器接线图详解
  9. 关于Ext.grid.EditorGridPanel使用中遇到的问题
  10. SketchUp Pro2019注册机|草图大师SketchUp Pro 2019注册破解补丁下载(附SketchUp Pro 2019许可证)