作业地址

主要打印系统调用和实现自定义系统调用

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:

1
2
3
4
5
6
7
...
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 对应的序号,任务要求是输出 system call 对应的序号

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#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.h 内对应关系建立相关数据并输出

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
char syscall_name[][10] = {
  [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",
};

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("%s -> %d\n", syscall_name[num], num);  // 新增打印 system call 语句
  } else {
    cprintf("%d %s: unknown sys call %d\n",
            curproc->pid, curproc->name, num);
    curproc->tf->eax = -1;
  }
}

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:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#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.

根据提示得到实现 uptime system call 出现的文件和其所在行数,从而进行仿造:

1
2
3
4
5
6
7
8
[email protected]:~/lab/xv6-public# grep -n uptime *.[chS]
syscall.c:107:extern int sys_uptime(void);
syscall.c:123:[SYS_uptime]  sys_uptime,
syscall.c:147:  [SYS_uptime] "uptime",
syscall.h:15:#define SYS_uptime 14
sysproc.c:83:sys_uptime(void)
user.h:25:int uptime(void);
usys.S:31:SYSCALL(uptime)

syscall.h 中添加声明 system call 序号

1
#define SYS_date   22

syscall.c 中添加调用函数的外部声明

1
2
3
4
5
extern int sys_date(void);
static int (*syscalls[])(void) = {
  // ...
  [SYS_date]    sys_date,
};

sysproc.c 中添加 sys_date() 的实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
int
sys_date(void)
{
  int addr;

  if (argint(0, &addr) < 0) {
    return -1;
  }

  // 获得系统 UTC 时间
  cmostime((struct rtcdate *)addr);

  return 0;
}

user.h 中加入用户态函数定义

1
int date(struct rtcdate *);

usys.S 中加入用户态函数实现

1
SYSCALL(date)

新建 date.c 用于提供用户空间对内核提供的 system call 进行调用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#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(2, "UTC Time: %d-%d-%d %d:%d:%d\n",
         r.year, r.month, r.day,
         r.hour, r.minute, r.second);

  exit();
}

修改 MakefileUPROGS flag

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
UPROGS=\
_cat\
_echo\
_forktest\
_grep\
_init\
_kill\
_ln\
_ls\
_mkdir\
_rm\
_sh\
_stressfs\
_usertests\
_wc\
_zombie\
_date\  # 新增的

Optional challenge: add a dup2() system call and modify the shell to use it.

关于 fd, dup()dup2() 可参考下面视频

具体介绍可参考: 进程间通信管道进阶篇:linux 下 dup/dup2 函数的用法

仿造 sysfile.csys_dup() 的实现方式实现 dup2()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int
sys_dup2(void)
{
  struct file *f1, *f2;
  int fd1, fd2;

  if ((argfd(0, &fd1, &f1) < 0) ||
      (argfd(1, &fd2, &f2) < 0))
    return -1;
  if (fd2 < 0 || fd2 >= NOFILE)
    return -1;
  // 如果新的 fd 与旧的 fd 不同
  if (fd1 != fd2) {
    f2 = myproc->ofile[fd2];
    // 判断新 fd 状态,打开则关闭
    if (f2) {
      fileclose(f2);
    }
    myproc->ofile[fd2] = f1;
    filedup(f1);
  }
  return fd;
}