Lab: traps

6.S081 的 Xv6 RISC-V Lab traps,实验内容:

  • https://pdos.csail.mit.edu/6.S081/2020/labs/traps.html
$ git fetch
$ git checkout traps
$ make clean

RISC-V assembly

这题没什么具体要做的,就看一看,跟着题目学一下 RISC- V 汇编。

Backtrace

这题主要是实现打印函数栈。就是 GDB 里面 bt 的这种效果:

(gdb) bt
#0  fork () at kernel/proc.c:260
#1  0x0000000080002c3c in sys_fork () at kernel/sysproc.c:29
#2  0x0000000080002bb0 in syscall () at kernel/syscall.c:140
#3  0x000000008000289a in usertrap () at kernel/trap.c:67
#4  0x0000000000000050 in ?? ()

关键代码

kernel/riscv.h: 实现一个获取当前 frame pointer 的方法:

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

kernel/printf.c: 打印 backtrace:

void
backtrace(void)
{printf("backtrace:\n");uint64 fp = r_fp();while (PGROUNDDOWN(fp) < PGROUNDUP(fp)) {printf("%p\n", *(uint64*)(fp-8));fp = *(uint64*)(fp-16);}
}

Diff

diff --git a/kernel/defs.h b/kernel/defs.h
index 4b9bbc0..137c786 100644
--- a/kernel/defs.h
+++ b/kernel/defs.h
@@ -80,6 +80,7 @@ int             pipewrite(struct pipe*, uint64, int);void            printf(char*, ...);void            panic(char*) __attribute__((noreturn));void            printfinit(void);
+void            backtrace(void);// proc.cint             cpuid(void);
diff --git a/kernel/printf.c b/kernel/printf.c
index e1347de..fbdeb68 100644
--- a/kernel/printf.c
+++ b/kernel/printf.c
@@ -121,6 +121,9 @@ panic(char *s)printf("panic: ");printf(s);printf("\n");
+
+  backtrace();
+panicked = 1; // freeze uart output from other CPUsfor(;;);
@@ -132,3 +135,18 @@ printfinit(void)initlock(&pr.lock, "pr");pr.locking = 1;}
+
+// print a backtrace:
+// a list of the function calls on the stack above
+// the point at which the error occurred.
+void
+backtrace(void)
+{
+  printf("backtrace:\n");
+  uint64 fp = r_fp();
+  while (PGROUNDDOWN(fp) < PGROUNDUP(fp)) {
+      printf("%p\n", *(uint64*)(fp-8));
+      fp = *(uint64*)(fp-16);
+  }
+}
+
diff --git a/kernel/riscv.h b/kernel/riscv.h
index 0aec003..c95316e 100644
--- a/kernel/riscv.h
+++ b/kernel/riscv.h
@@ -319,6 +319,16 @@ sfence_vma()asm volatile("sfence.vma zero, zero");}+// compiler stores the frame pointer of the currently
+// executing function in s0.
+// this function reads current frame pointer, namely s0
+static inline uint64
+r_fp()
+{
+  uint64 x;
+  asm volatile("mv %0, s0" : "=r" (x) );
+  return x;
+}#define PGSIZE 4096 // bytes per page#define PGSHIFT 12  // bits of offset within a page
diff --git a/kernel/sysproc.c b/kernel/sysproc.c
index e8bcda9..a520959 100644
--- a/kernel/sysproc.c
+++ b/kernel/sysproc.c
@@ -58,6 +58,8 @@ sys_sleep(void)int n;uint ticks0;+  backtrace();
+if(argint(0, &n) < 0)return -1;acquire(&tickslock);

Alarm

做一套系统调用,在用户进程每运行指定次数时钟后,调用该进程提供的一个 handler。

其实也不难,(我觉得比页表简单),跟着 hint 一步步做就行了。

关键实现

  1. kernel/proc.h: 在 proc 结构体里添加需要的字段(间隔时长、已运行时长计数、处理函数地址(vm)、保存 alarm 前的寄存器):
struct proc {...int alarm_interval;     // the alarm interval (ticks)int alarm_passed;       // how many ticks have passed since the last calluint64 alarm_handler;   // pointer to the alarm handler functionstruct trapframe etpfm; // trapframe to resume
}
  1. 添加 sys_sigalarm 系统调用:接收参数,为进程设置 alarm 的间隔时长和处理函数。最终的实现在 kernel/sysproc.c (添加新系统调用的完整步骤见:6.S081 Xv6 Lab 2: system calls):
uint64
sys_sigalarm(void)
{int interval;uint64 handler;if(argint(0, &interval) < 0)return -1;if(argaddr(1, &handler) < 0)return -1;struct proc *p = myproc();p->alarm_interval = interval;p->alarm_handler = handler;return 0;
}
  1. usertrapkernel/trap.c)中处理时钟中断时,如果进程需要 alarm 就保存当前 trapframe 到 etpfm,调用 handler (把 handler 地址放到 trapframe->epc,回到用户空间之后就会运行该函数):
void
usertrap(void)
{...if(which_dev == 2) {// alarmif (p->alarm_interval) {if (++p->alarm_passed == p->alarm_interval) {memmove(&(p->etpfm), p->trapframe, sizeof(struct trapframe));// return to alarm handler: call p->alarm_handler();p->trapframe->epc = p->alarm_handler;}}yield();}...
}
  1. 实现 sys_sigalarm 系统调用,恢复 alarm 前的 trapframe(回到用户空间就会接着 alarm 之前的 PC 开始运行),把 alarm_passed 计数器置为零(允许下一次 alarm):
uint64
sys_sigreturn(void)
{struct proc *p = myproc();memmove(p->trapframe, &(p->etpfm), sizeof(struct trapframe));p->alarm_passed = 0;return 0;
}

某bug的sizeof

我做的时候不小心写了个 sizeof 的语法错误,找了半个小时呢。。。(这个问题超初学者的,,我对8起 K&R)

我一开始是这么写的:

struct trapframe {...}struct proc {...struct trapframe *trapframe;struct trapframe etpfm;
}void
usertrap(void)
{...memmove(&(p->etpfm), p->trapframe, sizeof(p->trapframe));...
}uint64
sys_sigreturn(void)
{...memmove(p->trapframe, &(p->etpfm), sizeof(p->trapframe));...
}

解决:把 sizeof(p->trapframe) 改成 sizeof(struct trapframe)

Diff

emmm,上一题做完忘记 commit 了,所以下面这个 diff 也包含了上一题(backtrace)的:

diff --git a/Makefile b/Makefile
index 1fa367e..a74296b 100644
--- a/Makefile
+++ b/Makefile
@@ -175,6 +175,7 @@ UPROGS=\$U/_grind\$U/_wc\$U/_zombie\
+  $U/_alarmtest\diff --git a/grade-lab-traps b/grade-lab-traps
index 058e77b..8619bbe 100755
--- a/grade-lab-traps
+++ b/grade-lab-traps
@@ -60,7 +60,7 @@ def test_alarmtest_test2():def test_usertests():r.run_qemu(shell_script(['usertests'
-    ]), timeout=300)
+    ]), timeout=500)r.match('^ALL TESTS PASSED$')@test(1, "time")
diff --git a/kernel/defs.h b/kernel/defs.h
index 4b9bbc0..137c786 100644
--- a/kernel/defs.h
+++ b/kernel/defs.h
@@ -80,6 +80,7 @@ int             pipewrite(struct pipe*, uint64, int);void            printf(char*, ...);void            panic(char*) __attribute__((noreturn));void            printfinit(void);
+void            backtrace(void);// proc.cint             cpuid(void);
diff --git a/kernel/printf.c b/kernel/printf.c
index e1347de..fbdeb68 100644
--- a/kernel/printf.c
+++ b/kernel/printf.c
@@ -121,6 +121,9 @@ panic(char *s)printf("panic: ");printf(s);printf("\n");
+
+  backtrace();
+panicked = 1; // freeze uart output from other CPUsfor(;;);
@@ -132,3 +135,18 @@ printfinit(void)initlock(&pr.lock, "pr");pr.locking = 1;}
+
+// print a backtrace:
+// a list of the function calls on the stack above
+// the point at which the error occurred.
+void
+backtrace(void)
+{
+  printf("backtrace:\n");
+  uint64 fp = r_fp();
+  while (PGROUNDDOWN(fp) < PGROUNDUP(fp)) {
+      printf("%p\n", *(uint64*)(fp-8));
+      fp = *(uint64*)(fp-16);
+  }
+}
+
diff --git a/kernel/proc.c b/kernel/proc.c
index dab1e1d..237fecb 100644
--- a/kernel/proc.c
+++ b/kernel/proc.c
@@ -127,6 +127,11 @@ found:p->context.ra = (uint64)forkret;p->context.sp = p->kstack + PGSIZE;+  // Initialize alarm
+  p->alarm_interval = 0;
+  p->alarm_passed = 0;
+  p->alarm_handler = 0;
+return p;}diff --git a/kernel/proc.h b/kernel/proc.h
index 9c16ea7..2dac44f 100644
--- a/kernel/proc.h
+++ b/kernel/proc.h
@@ -103,4 +103,9 @@ struct proc {struct file *ofile[NOFILE];  // Open filesstruct inode *cwd;           // Current directorychar name[16];               // Process name (debugging)
+
+  int alarm_interval;          // the alarm interval (ticks)
+  int alarm_passed;            // how many ticks have passed since the last call
+  uint64 alarm_handler;        // pointer to the alarm handler function
+  struct trapframe etpfm;      // trapframe to resume, 啊, 不会xv6的动态内存分配,所以这里直接 hardcode 一个对象了。};
diff --git a/kernel/riscv.h b/kernel/riscv.h
index 0aec003..c95316e 100644
--- a/kernel/riscv.h
+++ b/kernel/riscv.h
@@ -319,6 +319,16 @@ sfence_vma()asm volatile("sfence.vma zero, zero");}+// compiler stores the frame pointer of the currently
+// executing function in s0.
+// this function reads current frame pointer, namely s0
+static inline uint64
+r_fp()
+{
+  uint64 x;
+  asm volatile("mv %0, s0" : "=r" (x) );
+  return x;
+}#define PGSIZE 4096 // bytes per page#define PGSHIFT 12  // bits of offset within a page
diff --git a/kernel/syscall.c b/kernel/syscall.c
index c1b3670..eb079af 100644
--- a/kernel/syscall.c
+++ b/kernel/syscall.c
@@ -104,6 +104,8 @@ extern uint64 sys_unlink(void);extern uint64 sys_wait(void);extern uint64 sys_write(void);extern uint64 sys_uptime(void);
+extern uint64 sys_sigalarm(void);
+extern uint64 sys_sigreturn(void);static uint64 (*syscalls[])(void) = {[SYS_fork]    sys_fork,
@@ -127,6 +129,8 @@ static uint64 (*syscalls[])(void) = {[SYS_link]    sys_link,[SYS_mkdir]   sys_mkdir,[SYS_close]   sys_close,
+[SYS_sigalarm]  sys_sigalarm,
+[SYS_sigreturn] sys_sigreturn,};void
diff --git a/kernel/syscall.h b/kernel/syscall.h
index bc5f356..382d781 100644
--- a/kernel/syscall.h
+++ b/kernel/syscall.h
@@ -20,3 +20,5 @@#define SYS_link   19#define SYS_mkdir  20#define SYS_close  21
+#define SYS_sigalarm  22
+#define SYS_sigreturn 23
diff --git a/kernel/sysproc.c b/kernel/sysproc.c
index e8bcda9..5b64016 100644
--- a/kernel/sysproc.c
+++ b/kernel/sysproc.c
@@ -58,6 +58,8 @@ sys_sleep(void)int n;uint ticks0;+  backtrace();
+if(argint(0, &n) < 0)return -1;acquire(&tickslock);
@@ -95,3 +97,32 @@ sys_uptime(void)release(&tickslock);return xticks;}
+
+uint64
+sys_sigalarm(void)
+{
+  int interval;
+  uint64 handler;
+
+  if(argint(0, &interval) < 0)
+      return -1;
+
+  if(argaddr(1, &handler) < 0)
+      return -1;
+
+  struct proc *p = myproc();
+
+  p->alarm_interval = interval;
+  p->alarm_handler = handler;
+
+  return 0;
+}
+
+uint64
+sys_sigreturn(void)
+{
+  struct proc *p = myproc();
+  memmove(p->trapframe, &(p->etpfm), sizeof(struct trapframe));
+  p->alarm_passed = 0;
+  return 0;
+}
diff --git a/kernel/trap.c b/kernel/trap.c
index a63249e..9f2a64b 100644
--- a/kernel/trap.c
+++ b/kernel/trap.c
@@ -77,8 +77,20 @@ usertrap(void)exit(-1);// give up the CPU if this is a timer interrupt.
-  if(which_dev == 2)
+  if(which_dev == 2) {
+  // alarm
+  if (p->alarm_interval /*&& p->alarm_handler*/) {  // handler addr might be 0
+    if (++p->alarm_passed == p->alarm_interval) {
+      memmove(&(p->etpfm), p->trapframe, sizeof(struct trapframe));
+      // return to alarm handler: call p->alarm_handler();
+      p->trapframe->epc = p->alarm_handler;
+      // printf("[DEBUG] alarm: %s(%d), handler=%x\n",
+      //      p->name, p->pid, p->alarm_handler);
+      // p->alarm_passed = 0;  // sigreturn 时再恢复: prevent re-entrant calls to the handler
+    }
+  }yield();
+  }usertrapret();}
diff --git a/user/alarmtest.c b/user/alarmtest.c
index 38f09ff..427c460 100644
--- a/user/alarmtest.c
+++ b/user/alarmtest.c
@@ -101,6 +101,7 @@ test1()// restored correctly, causing i or j or the address ofj// to get an incorrect value.printf("\ntest1 failed: foo() executed fewer times than it was called\n");
+  printf("\ti=%d, j=%d\n", i, j);} else {printf("test1 passed\n");}
diff --git a/user/user.h b/user/user.h
index b71ecda..57404e0 100644
--- a/user/user.h
+++ b/user/user.h
@@ -23,6 +23,8 @@ int getpid(void);char* sbrk(int);int sleep(int);int uptime(void);
+int sigalarm(int ticks, void (*handler)());
+int sigreturn(void);// ulib.cint stat(const char*, struct stat*);
diff --git a/user/usys.pl b/user/usys.pl
index 01e426e..fa548b0 100755
--- a/user/usys.pl
+++ b/user/usys.pl
@@ -36,3 +36,5 @@ entry("getpid");entry("sbrk");entry("sleep");entry("uptime");
+entry("sigalarm");
+entry("sigreturn");

实验结果

最后,make grade (我的机器上跑 usertests 巨慢,要改一下 timeout 才能通过。。):

== Test answers-traps.txt == answers-traps.txt: OK
== Test backtrace test ==
$ make qemu-gdb
backtrace test: OK (4.7s)
== Test running alarmtest ==
$ make qemu-gdb
(3.9s)
== Test   alarmtest: test0 == alarmtest: test0: OK
== Test   alarmtest: test1 == alarmtest: test1: OK
== Test   alarmtest: test2 == alarmtest: test2: OK
== Test usertests ==
$ make qemu-gdb
usertests: OK (448.3s) (Old xv6.out.usertests failure log removed)
== Test time ==
time: OK
Score: 85/85

EOF


By CDFMLR 2020-03-28

顶部图片来自于小歪API,系随机选取的图片,仅用于检测屏幕显示的机械、光电性能,与文章的任何内容及观点无关,也并不代表本人局部或全部同意、支持或者反对其中的任何内容及观点。如有侵权,联系删除。

6.S081 Xv6 Lab 4 traps相关推荐

  1. 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 ...

  2. OS Lab 【Traps】

    一.实验内容: Part A RISC-V assembly 见问题回答 Part B Backtrace Add the prototype for your backtrace() to kern ...

  3. 2020 MIT6.s081 os Lab: page tables

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

  4. 系统调用跟踪-xv6 lab syscall

    1.概述 本文记录xv6操作系统的系统调用跟踪实验,xv6是一个类Unix的简单操作系统.该实验是要求实现一个trace系统调用,该系统调用的功能是根据用户传入的系统调用号跟踪某个或者某些进程的系统调 ...

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

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

  6. 2020 MIT6.s081 XV6操作系统调试

    文章目录 环境准备 启动调试 调试步骤 gdb layout GDB调试常用命令 参考链接 友情链接:全部实验哟 环境准备 操作系统:本人采用的操作系统版本为Ubuntu 20.04.2 LTS # ...

  7. xv6 - lab0 - 实验环境

    xv6 实验环境 为了能够在RISC-V模拟器环境中实验XV6操作系统,我需要配置的工具软件有:QEMU 5.1+, GDB 8.3+, GCC, and Binutils. 1 配置环境 1)系统环 ...

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

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

  9. 学妹:大学四年以算法为重还是技术为重?

    经常有学妹问我(其实学弟也爱问): 大学应该更偏向技术还是算法和数据结构这类. 大家都是成年人了,这还用选吗? 当然是两者都要重点啃下来呀,算法和技术相辅相成的,一定不要有二选一的想法! 算法和数据结 ...

  10. 浪潮之巅 “八叛徒”与硅谷

    #第3章 "八叛徒"与硅谷​​​​​​​​​​​​​​ #博客导航 #Result #Evaluation #博客导航 这是本人的 CSDN 博客之分类专栏链接,欢迎点击阅读! M ...

最新文章

  1. paramiko的使用
  2. 服务器响应200和304含义
  3. ABAP动态创建数据DATA或对象Object
  4. hdu1799 循环多少次?(组合递推公式的使用)
  5. JavaScript的10种跨域共享的方法
  6. Spring Boot 学习之表单验证
  7. 【知乎】神回答,我们吐的不是槽 233
  8. 基于matlab的图像仿真研究,基于MATLAB的图像锐化算法研究与仿真
  9. Java数组--数组常用的办法;
  10. LVDS屏的俩种接口:JEIDAVESA
  11. 01、Hive数据仓库——Hive SQL练习
  12. matlab画图形函数 semilogx semilogy和loglog
  13. multisimbcd码_8421BCD码加法器报告1
  14. (附源码)springboot跨境电商系统 毕业设计 211003
  15. 亚马逊多店铺统一管理还不会关联?原来是靠它
  16. 一键翻译PDF神器|网称最强翻译软件
  17. Android主流开源视频播放器对比
  18. MATLAB函数downsample的用法详解
  19. 使用代码操作Excel文件(easyExcel)
  20. 51单片机实战教程(六 网线测试治具设计)

热门文章

  1. mysql5.7 bulk insert_Bulk Insert 高效快速插入数据
  2. python工时计算_七兮网络-如何根据考勤数据自动计算出员工工作时间
  3. java学生选课系统_java实现学生选课系统
  4. SVN 安装与使用教程 2020年9月更新最新教程
  5. 基于51单片机超声波测距仪倒车雷达报警器汽车防撞系统套件
  6. office for mac的字体设置
  7. 什么是Meta分析异质性,怎么处理Meta分析异质性?看完这篇就够了
  8. The Environment class in C#
  9. 台达伺服b3参数_恩阳台达B3系列伺服安装
  10. 前端入门 前端实战项目 JS