问题重述
本实验要求我们为 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 |
|