系统调用是内核中的一系列实现系统功能的子函数,本文将针对XV6中的一些系统调用进行管理。本章的配置过程如:作业1:XV6启动
第一部分:系统调用追踪
修改xv6内核,对于每一个系统调用,打印函数名和返回值以及参数,当xv6启动时,可以看到如下内容:
1 | ... |
这个是init fork,并执行了sh,sh确保只有两个文件描述符被打开,然后sh写了$
,修改syscall.c
下的syscall()
,如下功能:
- 打印系统调用名称
- 打印返回值
- 打印系统调用参数
syscall()
原型如下:
1 | void |
我们需要打印系统调用名称、返回值及参数,struct proc
结构体描述了进程的信息,具体如下:
1 | struct proc { |
从这个结构体中,我们能够得到进程的名称,但是只知道进程名称是不行的,我们需要知道函数名称。我们可以看到,在syscall中有一个比较重要的结构体tf,即trapframe,这个结构体保存了CPU寄存器中的值,内容如下
1 | struct trapframe { |
记得在Lab1里有过使用eip寄存器函数追踪的方法,但是那个过于复杂,由于系统调用比较固定,我们直接写一个函数表用查表法实现即可:
1 | static char *syacall_name[] = { |
在syscall()
中添加下面一句:
1 | cprintf("%s->%d\n",syacall_name[num],curproc->tf->eax); |
第二部分:date System Call
第二部分要求我们完成一个date 系统调用,获取当前UTC时间并返回。
实现准备
首先,我们需要一个读取硬件时间的函数,cmostime()
,这个函数定义在lapic.c
。其次我们需要一个描述时间的结构体struct rtcdate
(位于date.h
),我们需要将一个rtcdate
对象指针传递给cmostime()
。我们可以参考其他系统调用的实现,输入
1 | grep -n uptime *.[chS] |
查看和uptime系统调用有关的所有内容,并参考其完成date
系统调用。根据grep -n uptime *.[chS]
的结果,将该添加的函数声明及宏定义添加了
1 | syscall.c:106:extern int sys_date(void); |
其次,我们创建一个date.c
文件,这个文件中包含两部分,第一,这个文件包含一个main函数,即我们编译后可以产生可执行二进制文件date
,输入命令date
后,就会调用这个文件;第二,这个文件包含了date
的实现(这个是错误的)。
然而,当我在date.c
中添加了date
函数定义后,我遇到了如下错误:
1 | /home/duan/Code/OS/xv6-public/usys.S:32: multiple definition of `date' |
提示我在文件usys.S
中,date
已经定义过了,打开usys.S
,里面内容如下:
1 | #define SYSCALL(name) \ |
将date
带入宏定义并展开,得到结果如下:
1 | .globl date; |
说明我们在usys.S
中,已经通过汇编的方式,定义了date
这个函数,这个函数的内容是将SYS_date
移到eax
,然后通过中断的方式调用sys_date
,因此我们不需要再定义date
,只需要编写好sys_date
即可。但是这个时候我又疑惑了,我们的date()
是有参数的,输入的是struct rtcdate*
,但是sys_
开头的系统调用函数都是没有参数的
1 | extern int sys_link(void); // 系统调用全都没有参数 |
那么该如何处理输入参数呢?
解决输入参数的问题
后来我经过了一番寻找,终于找到了一个类似的需要输入参数的系统调用,即sys_mkdir
。而mkdir.c
中对mkdir
实现步骤
sys_date实现
在sysproc.c
中实现sys_date()
函数,练习系统调用的编写过程。
1 | int |
在Makefile中添加调用
为了使date命令能够在xv6的shell中使用,需要在Makefile
的UPROGS
定义中添加_date
。
date可执行程序实现
在date.c
中加入如下程序
1 |
|
结果
最后实现的结果如下,运行make qemu
打开操作系统后,输入date
,将显示系统时间(命令行中的777不知道是啥,好像是在qemu和shell切换时会产生)