问题重述
本实验要求我们为 xv6 增加一个系统调用 alarm(ticks, handler),同样是通过中断处理实现其逻辑。每当程序使用了 ticks 个时间片(tick),alarm 就会调用 handler 指向的函数来提醒程序。问题的关键点在于如何在中断处理中调用这个用户态函数。
我们已经知道函数调用的机理,即把函数参数依次压栈,再将下一条指令 eip 压栈,然后跳转到函数代码处执行。由于处理函数 alarm-handler 没有参数,我们只需要把进程的 eip 压栈并指向 alarm-handler 即可。当 alarm-handler 运行完后,eip 也会恢复,继续执行后续的指令,这样就实现了手动把该函数注入到进程的上下文中。以下为本实验的具体实现流程。
系统调用的添加
系统调用的添加过程比较简单,执行下述几个固定步骤即可:1) syscall.h 定义系统调用号:
1 | |
2) user.h 声明系统调用函数对应的内核函数
1 | |
3) syscall.c 定义系统调用号和内核函数的对应关系并声明内核函数
1 | |
4) sysproc.c 利用参数解析封装系统调用函数(已经给出)
1 | |
5) usys.S 定义系统调用的过程
1 | |
6) 增加一个用来测试的用户程序 alarmtest.c(已经给出)
1 | |
7) Makefile 添加需要的用户代码信息 UPROGS
1 | |
proc结构体的完善
注意到 sys_alarm() 里的 myproc()->alarmticks = ticks; 和 myproc()->alarmhandler = handler; 语句,这提示我们系统调用alarm的功能,一是初始化进程消耗的时间片 alarmticks,二是把 handler 函数的地址存入进程的结构体中,因此需要为进程的结构体增加相应字段:
1 | |
alarm-handler的实现
xv6 已经定义好了硬件的时钟触发机制,trap(): case T_IRQ0 + IRQ_TIMER 在每一个 tick 都会触发一次时钟中断;alarm-handler 的逻辑实现比较简单,先判断 ticks 是否消耗完毕,如果消耗完则计数清零,通过 eip 压栈执行 alarm-handler:
1 | |
启动 xv6 键入 alarmtest 得到如下结果:
1 | |