【归档】[OS] Xv6 System Calls

Part One: System call tracing

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


Part Two: Date system call

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 the UPROGS definition in Makefile.

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() 了:

  1. syscall.h 中最后一行添加系统调用编号

    #define SYS_date   22
    
  2. 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;
    ...
    
  3. user.h 中添加用户态函数的定义

    ...
    int sleep(int);
    int uptime(void);
    // 以下为添加内容
    int date(struct rtcdate*);
    // 以上为添加内容
    
    // ulib.c
    int stat(char*, struct stat*);
    ...
    
  4. usys.S 中最后一行添加用户态函数的实现

    SYSCALL(date)
    
  5. 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

你可能感兴趣的:(归档,xv6,操作系统,MIT,系统调用)