作业要求

这个作业是向 xv6 中添加一个系统调用,这个系统调用获取当前的 UTC 时间,并返回这个时间。在实现过程中,将会使用到帮助函数 cmostime(),这个函数需要一个 struct rtcdate 结构体指针作为参数。

为了检验这个系统调用的正确性,我们选择写一个简单的 c 语言程序 date.c:

#include "types.h"
#include "user.h"
#include "date.h"

int
main(int argc, char *argv[])
{
  struct rtcdate r;

  if (date(&r)) {
    printf(2, "date failed\n");
    exit();
  }

  // your code to print the time in any format you like...

  exit();
}

然后将这个程序加入到 shell 中

作业实现

首先从上面的示例中可以看出,提供给用户程序接口形式为 int date(struct rtcdate *),所以需要在 usys.S 中添加 SYSCALL(date) 以形成这个系统调用的汇编,用户程序调用 date() 的地方会直接链接到这块汇编代码。

usys.S 中的宏定义如下:

#define SYSCALL(name) \
  .globl name; \
  name: \
    movl $SYS_ ## name, %eax; \
    int $T_SYSCALL; \
    ret

在执行 int 64 指令前,需要将系统调用号放进 %eax 寄存器,接着又需要在 syscall.h 中添加宏:

#define SYS_date  22

将系统调用号设置好。执行 int 指令,切换到内核栈,并将一部分寄存器(%err,%eip,%cs,%eflags,%esp,%ss)值保存进内核栈。

通过查看 vectors.S,并将 0 和 64(trapframe中的trapno) push 进内核栈,随即跳转到 alltraps 符号处,紧接着通过一系列的 push 指令,在内核栈上构造出完整的 trapframe。

接着跳转执行 trap(),在里面调用 syscall(),在 syscall() 里面通过查看函数指针数组跳转到 sys_date(),为了能成功跳转,在数组中添加相应的函数指针即可。

现在就可以添加系统调用的具体代码了,在 sysproc.c 中添加如下代码:

int sys_date(void)
{
  int temp;
  if (argint(0, &temp) < 0)
    return -1;
  cmostime((struct rtcdate *)temp);
  return 0;
}

date.c 示范代码中可以看到,data() 系统调用接受了一个 struct rtcdate 结构体指针作为参数,这里为了方便,使用 argint() 来获取这个指针值,获取到后通过类型转换将这个值转换成 struct rtcdate 类型的指针,并把这个指针传入 cmostime() 函数。

至此关于系统调用的相关代码就完成了,也基本上过了一边 xv6 下系统调用的大致流程。

大部分工作已经完成,最后在目录中添加 date.c:

#include "types.h"
#include "user.h"
#include "date.h"
int main(int argc, char *argv[])
{
    struct rtcdate r;
    if (date(&r))
    {   /*第一个参数为标准错误*/
        printf(2, "date failed!!!\n");
        exit();
    }
    /*第一个参数为标准输出*/
    printf(1, "%d %d %d %d %d %d\n", r.year, r.month, r.day, r.hour, r.minute, r.second);
    exit();
}

并在 Mikefile 的 UPROGS 中添加 _date\ 使之成为命令行:

$ date
2019 5 22 15 9 18

成功!

最后注意一点的是,查看运行结果时,最好先 make clean,以免会出一些链接上的错误,导致即使正确的代码也无法正常运行。