Your first task is to modify the xv6 kernel to print out a line for each system call invocation. It is enough to print the name of the system call and the return value; you don’t need to print the system call arguments.
When you’re done, you should see output like this when booting xv6:
... fork -> 2 exec -> 0 open -> 3 close -> 0 $write -> 1 write -> 1
That’s init forking and execing sh, sh making sure only two file descriptors are open, and sh writing the $ prompt. (Note: the output of the shell and the system call trace are intermixed, because the shell uses the write syscall to print its output.)
Hint: modify the syscall() function in syscall.c.
Optional challenge: print the system call arguments.
第一步,我们要跟踪系统调用,并把他们打印在终端上。
首先,我们要看一下 syscall.h
// System call numbers
#define SYS_fork 1
#define SYS_exit 2
#define SYS_wait 3
#define SYS_pipe 4
#define SYS_read 5
#define SYS_kill 6
#define SYS_exec 7
#define SYS_fstat 8
#define SYS_chdir 9
#define SYS_dup 10
#define SYS_getpid 11
#define SYS_sbrk 12
#define SYS_sleep 13
#define SYS_uptime 14
#define SYS_open 15
#define SYS_write 16
#define SYS_mknod 17
#define SYS_unlink 18
#define SYS_link 19
#define SYS_mkdir 20
#define SYS_close 21
这里面定义了系统调用的名称和对应的序号,我们要在终端上显示的就是上面这些对应的名字和编号。
下面,我们要进行文件修改了。
打开文件:syscall.c
在文件前面添加编号和系统调用名称相互对应的数组:
...
#include "x86.h"
#include "syscall.h"
// 以下为添加的内容
static char SYS_call_names[][6] = {
[SYS_fork] "fork",
[SYS_exit] "exit",
[SYS_wait] "wait",
[SYS_pipe] "pipe",
[SYS_read] "read",
[SYS_kill] "kill",
[SYS_exec] "exec",
[SYS_fstat] "fstat",
[SYS_chdir] "chdir",
[SYS_dup] "dup",
[SYS_getpid] "getpid",
[SYS_sbrk] "sbrk",
[SYS_sleep] "sleep",
[SYS_uptime] "uptime",
[SYS_open] "open",
[SYS_write] "write",
[SYS_mknod] "mknod",
[SYS_unlink] "unlink",
[SYS_link] "link",
[SYS_mkdir] "mkdir",
[SYS_close] "close"};
// 以上为添加的内容
// User code makes a system call with INT T_SYSCALL.
// System call number in %eax.
...
然后在函数:syscall
中添加对应的输出命令:
void syscall(void)
{
int num;
struct proc *curproc = myproc();
num = curproc->tf->eax;
if (num > 0 && num < NELEM(syscalls) && syscalls[num])
{
curproc->tf->eax = syscalls[num]();
// 以下为添加的内容
// 添加的是下面这一行代码,打印系统调用名称和编号并换行,使用制表符对齐格式
cprintf("\tSYS_call: %s\tid: %d\n", SYS_call_names[num], num);
// 以上为添加的内容
}
else
{
cprintf("%d %s: unknown sys call %d\n",
curproc->pid, curproc->name, num);
curproc->tf->eax = -1;
}
}
至此,第一部分的代码修改就完成了。
最后,在ubuntu虚拟机中编译运行:
$ make qemu-nox
运行结果如下:
qemu-system-i386 -nographic -drive file=fs.img,index=1,media=disk,format=raw -drive file=xv6.img,index=0,media=disk,format=raw -smp 1 -m 512 -snapshot
xv6...
cpu0: starting 0
sb: size 20000 nblocks 19937 ninodes 200 nlog 30 logstart 2 inodestart 32 bmap start 58
SYS_call: exec id: 7
SYS_call: open id: 15
SYS_call: mknod id: 17
SYS_call: open id: 15
SYS_call: dup id: 10
SYS_call: dup id: 10
i SYS_call: write id: 16
n SYS_call: write id: 16
i SYS_call: write id: 16
t SYS_call: write id: 16
: SYS_call: write id: 16
SYS_call: write id: 16
s SYS_call: write id: 16
t SYS_call: write id: 16
a SYS_call: write id: 16
r SYS_call: write id: 16
t SYS_call: write id: 16
i SYS_call: write id: 16
n SYS_call: write id: 16
g SYS_call: write id: 16
SYS_call: write id: 16
s SYS_call: write id: 16
h SYS_call: write id: 16
SYS_call: write id: 16
SYS_call: fork id: 1
SYS_call: exec id: 7
SYS_call: open id: 15
SYS_call: close id: 21
$ SYS_call: write id: 16
SYS_call: write id: 16
Your second task is to add a new system call to xv6. The main point of the exercise is for you to see some of the different pieces of the system call machinery. Your new system call will get the current UTC time and return it to the user program. You may want to use the helper function,
cmostime()
(defined in
lapic.c
), to read the real time clock.
date.h
contains the definition of the
struct rtcdate
struct, which you will provide as an argument to
cmostime()
as a pointer.
You should create a user-level program that calls your new date system call; here’s some source you should put in
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(); }
In order to make your new
date
program available to run from the xv6 shell, add_date
to theUPROGS
definition inMakefile
.Your strategy for making a date system call should be to clone all of the pieces of code that are specific to some existing system call, for example the “uptime” system call. You should grep for uptime in all the source files, using
grep -n uptime *.[chS]
.When you’re done, typing
date
to an xv6 shell prompt should print the current UTC time.Write down a few words of explanation for each of the files you had to modify in the process of creating your date system call.
Optional challenge: add a dup2() system call and modify the shell to use it.
第二步,我们要添加一个系统调用函数,使其可以返回当前系统时间。
我们要提前了解一下:
lapic.c
中定义了函数 cmostime()
来读取时钟时间。
void cmostime(struct rtcdate *r) {}
date.h
中定义了 rtcdate
结构体,作为参数提供给函数 cmostime()
。
struct rtcdate {
uint second;
uint minute;
uint hour;
uint day;
uint month;
uint year;
};
了解完这些,我们就可以开始添加系统调用函数 date()
了:
在 syscall.h
中最后一行添加系统调用编号
#define SYS_date 22
在 syscall.c
中添加系统调用函数的外部声明
...
[SYS_mkdir] "mkdir",
[SYS_close] "close",
// 以下为添加内容
[SYS_date] "date"};
// 以上为添加内容
// User code makes a system call with INT T_SYSCALL.
// System call number in %eax.
...
...
extern int sys_write(void);
extern int sys_uptime(void);
// 以下为添加内容
extern int sys_date(void);
// 以上为添加内容
static int (*syscalls[])(void) = {
[SYS_fork] sys_fork,
...
...
[SYS_mkdir] sys_mkdir,
[SYS_close] sys_close,
// 以下为添加内容
[SYS_date] sys_date,
// 以上为添加内容
};
void syscall(void)
{
int num;
...
在 user.h
中添加用户态函数的定义
...
int sleep(int);
int uptime(void);
// 以下为添加内容
int date(struct rtcdate*);
// 以上为添加内容
// ulib.c
int stat(char*, struct stat*);
...
在 usys.S
中最后一行添加用户态函数的实现
SYSCALL(date)
在 sysproc.c
中最后一行添加系统调用函数的实现
// return current date
int
sys_date(struct rtcdate *r)
{
if (argptr(0, (void *)&r, sizeof(*r)) < 0)
return -1;
cmostime(r); //从cmos中获取时间
return 0;
}
至此,就完成了添加系统调用函数 date()
最后,我们需要添加使用这个系统调用函数的方法
新建文件 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...
printf(1, "%d-%d %d %d:%d:%d\n", r.month, r.day, r.year, r.hour, r.minute, r.second);
exit();
}
在 Makefile
中添加 UPROGS
对应命令的定义:
...
_zombie\
_big\
# 以下为添加内容
_date\
# 以上为添加内容
fs.img: mkfs README $(UPROGS)
./mkfs fs.img README $(UPROGS)
...
至此,所有文件就修改完成了。
为了防止第一部分的输出影响我们时间的显示格式,可以先将 syscall.c
中的以下几行注释掉:
// static char SYS_call_names[][6] = {
// [SYS_fork] "fork",
...
// [SYS_close] "close",
// [SYS_date] "date"};
// cprintf("\tSYS_call: %s\tid: %d\n", SYS_call_names[num], num);
在Ubuntu虚拟机中编译运行:
$ make qemu-nox
然后输入命令 date
:
$ date
10-20 2017 9:41:36
可以看到终端根据我们在 date.c
中写好的时间格式输出了UTC时间。
bingo~
参考资料:
Homework: xv6 system calls
MIT6.828 HW3: xv6 system calls