说明

本文仅作为个人学习的记录,如果你还没有完成相应的实验,不建议阅读本文。

Traps

本实验分为三个部分,第一个部分是回答关于riscv的一些问题,按下不表;第二个部分是实现一个简单的backtrace,最后一个部分是实验的主体——实现一个定时器事件系统调用。

backtrace实现

了解riscv的ABI中关于stack frame的部分即可:

image-20210724172106610

fp中保存的是当前的stack frame的基址,fp-8位置保存的是返回地址,fp-16部分保存的是上一个fp。

判断当前fp是否是栈底部(xv6中给栈一共分配了一页的空间),如果不是,那么递归输出return address即可。

Alarm实现

Alarm分为两个部分——注册回调、返回。

注册回调

注册回调部分通过sigalarm实现,其传入tick period以及handler。其中tick period指明了过多少个tick调用一次回调函数,handler是回调函数的地址。

显然,我们需要在内核态保存关于当前tick的信息(实际上内核维护了一个cpu tick的信息,应该也可以使用这个值进行做差),同时记录handler的地址。每发生一次timer interrupt,tick++,当当前进程的tick period达到时,修改epc使得进程通过usertrapret()返回到handler中。

返回

当handler执行完成之后, 如果不做特殊处理,handler可能返回到一个莫名其妙的地方。我们希望handler执行完成之后,跳转回timer interrupt发生的现场继续执行——换而言之,我们需要在切换到handler的那一次usertrapret中记录当前的trapframe,通过调用sigreturn,恢复trapframe,从而跳转到timer interrupt的现场。

注意点

  1. handler在执行过程中不可以再次被打断(试想再次被打断,trapframe会被冲掉!也就回不到之前的现场了。当然可以用一个栈保存现场……);

Lazy Page Allocation

其实我觉得xv6这个实验,你只要懂英文,照着manual做都能做满分

简单说来,需要在xv6 kernel上修改的核心内容:

  • sbrk系统调用:不再分配page,如果参数为负,需要减少page
  • usertrap:添加对page fault的处理
  • page fault: 判断合法性(是否是invalid page)后分配page
  • 内核中涉及address的地方:判断地址合法性