0%

lab2.System calls

Lab 2:system calls

课程地址:https://pdos.csail.mit.edu/6.828/2020/schedule.html
Lab 地址:https://pdos.csail.mit.edu/6.828/2020/labs/syscall.html
代码仓库:https://github.com/ajackchan/my-xv6-riscv/tree/syscall

System-call-tracing

实现一个新的系统调用 trace,用于跟踪指定的系统调用,并在每次调用后输出当前进程ID、系统调用的名称和返回值。

1. 定义新的 trace 系统调用

  • 目标:实现一个新的 trace 系统调用,用于追踪进程中的系统调用。

  • 实现代码

    sysproc.c 中实现系统调用 sys_trace,根据用户传递的 mask 设置当前进程的 syscall_trace 字段,用来表示要跟踪哪些系统调用。

    1
    2
    3
    4
    5
    6
    uint64 sys_trace(void) {
    int mask;
    if (argint(0, &mask) < 0) return -1;
    myproc()->syscall_trace = mask;
    return 0;
    }

2. 系统调用的注册

  • syscall.h 中为 SYS_trace 分配编号:

    1
    #define SYS_trace 22
  • syscall.c 中将 sys_trace 添加到系统调用表中:

    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
    41
    42
    43
    44
    45
    46
    47
    48
    // kernel/syscall.c   
    extern uint64 sys_chdir(void);
    extern uint64 sys_close(void);
    extern uint64 sys_dup(void);
    extern uint64 sys_exec(void);
    extern uint64 sys_exit(void);
    extern uint64 sys_fork(void);
    extern uint64 sys_fstat(void);
    extern uint64 sys_getpid(void);
    extern uint64 sys_kill(void);
    extern uint64 sys_link(void);
    extern uint64 sys_mkdir(void);
    extern uint64 sys_mknod(void);
    extern uint64 sys_open(void);
    extern uint64 sys_pipe(void);
    extern uint64 sys_read(void);
    extern uint64 sys_sbrk(void);
    extern uint64 sys_sleep(void);
    extern uint64 sys_unlink(void);
    extern uint64 sys_wait(void);
    extern uint64 sys_write(void);
    extern uint64 sys_uptime(void);
    extern uint64 sys_trace(void); // HERE

    static uint64 (*syscalls[])(void) = {
    [SYS_fork] sys_fork,
    [SYS_exit] sys_exit,
    [SYS_wait] sys_wait,
    [SYS_pipe] sys_pipe,
    [SYS_read] sys_read,
    [SYS_kill] sys_kill,
    [SYS_exec] sys_exec,
    [SYS_fstat] sys_fstat,
    [SYS_chdir] sys_chdir,
    [SYS_dup] sys_dup,
    [SYS_getpid] sys_getpid,
    [SYS_sbrk] sys_sbrk,
    [SYS_sleep] sys_sleep,
    [SYS_uptime] sys_uptime,
    [SYS_open] sys_open,
    [SYS_write] sys_write,
    [SYS_mknod] sys_mknod,
    [SYS_unlink] sys_unlink,
    [SYS_link] sys_link,
    [SYS_mkdir] sys_mkdir,
    [SYS_close] sys_close,
    [SYS_trace] sys_trace, // AND HERE
    };

3. 用户态跳板函数生成

  • usys.pl 中添加 trace,生成用户态到内核态的跳板函数。

    1
    entry("trace");
  • user.h 中声明系统调用接口:

    1
    int trace(int mask);

4. 修改进程结构

  • proc.h 中为 proc 结构添加 syscall_trace 字段,用来存储系统调用跟踪的 mask

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
       // kernel/proc.h
    // Per-process state
    struct proc {
    struct spinlock lock;

    // p->lock must be held when using these:
    enum procstate state; // Process state
    struct proc *parent; // Parent process
    void *chan; // If non-zero, sleeping on chan
    int killed; // If non-zero, have been killed
    int xstate; // Exit status to be returned to parent's wait
    int pid; // Process ID

    // these are private to the process, so p->lock need not be held.
    uint64 kstack; // Virtual address of kernel stack
    uint64 sz; // Size of process memory (bytes)
    pagetable_t pagetable; // User page table
    struct trapframe *trapframe; // data page for trampoline.S
    struct context context; // swtch() here to run process
    struct file *ofile[NOFILE]; // Open files
    struct inode *cwd; // Current directory
    char name[16]; // Process name (debugging)
    uint64 syscall_trace; // Mask for syscall tracing (新添加的用于标识追踪哪些 system call 的 mask)
    };
  • proc.c 中初始化 syscall_trace,确保每个新进程的 syscall_trace 初始值为 0。

    1
    p->syscall_trace = 0;
  • 修改 fork() 函数,使子进程继承父进程的 syscall_trace

    1
    np->syscall_trace = p->syscall_trace;

5. 跟踪系统调用

  • syscall() 函数中检查进程是否需要追踪某个系统调用。如果设置了 syscall_trace,在系统调用执行后,打印出进程ID、系统调用名称和返回值。

    1
    2
    3
       if ((p->syscall_trace >> num) & 1) {
    printf("%d: syscall %s -> %d\n", p->pid, syscall_names[num], p->trapframe->a0);
    }
  • 添加 syscall_names[] 数组,用于将系统调用编号映射到系统调用名称:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
       const char *syscall_names[] = {
    [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",
    [SYS_trace] "trace",
    };

6. 系统调用的完整流程

  • 用户程序调用 trace(),触发跳板函数,通过系统调用表找到内核中的 sys_trace,最终设置进程的 syscall_trace

  • 系统调用到达内核时,进入 syscall() 函数,根据 syscall_trace 字段决定是否打印系统调用的追踪信息。

7. 小结

  • 添加新的 trace 系统调用。
  • 更新 proc 结构,使每个进程可以记录需要追踪的系统调用。
  • 修改 fork() 函数,使得子进程继承父进程的追踪信息。
  • syscall() 函数中实现追踪逻辑,打印进程的系统调用信息。
  • 使用 syscall_names[] 字符串数组,将系统调用编号与名称映射起来,便于打印。

Sysinfo

实现一个新的系统调用 sysinfo,该调用会收集系统信息,包括空闲内存的字节数和非 UNUSED 状态的进程数。

1. kernel/syscall.h 中新增宏定义

  • sysinfo 系统调用分配系统调用编号,添加以下宏定义:

    1
    #define SYS_sysinfo 23

2. 修改 user/usys.pl,新增 sysinfo 的 entry

  • usys.pl 文件中,新增一条 entry,用于生成 sysinfo 的用户态跳板函数:

    1
    entry("sysinfo");

3. user/user.h 中新增 sysinfo 结构体及函数声明

  • 定义 sysinfo 结构体,用于保存系统信息(例如空闲内存和进程数目)。

  • 声明 sysinfo 系统调用的用户接口。

    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
    41
    42
    43
    44
    45
       struct stat;
    struct rtcdate;
    struct sysinfo; // here!

    // system calls
    int fork(void);
    int exit(int) __attribute__((noreturn));
    int wait(int*);
    int pipe(int*);
    int write(int, const void*, int);
    int read(int, void*, int);
    int close(int);
    int kill(int);
    int exec(char*, char**);
    int open(const char*, int);
    int mknod(const char*, short, short);
    int unlink(const char*);
    int fstat(int fd, struct stat*);
    int link(const char*, const char*);
    int mkdir(const char*);
    int chdir(const char*);
    int dup(int);
    int getpid(void);
    char* sbrk(int);
    int sleep(int);
    int uptime(void);
    int trace(int);
    int sysinfo(struct sysinfo *); // here!

    // ulib.c
    int stat(const char*, struct stat*);
    char* strcpy(char*, const char*);
    void *memmove(void*, const void*, int);
    char* strchr(const char*, char c);
    int strcmp(const char*, const char*);
    void fprintf(int, const char*, ...);
    void printf(const char*, ...);
    char* gets(char*, int max);
    uint strlen(const char*);
    void* memset(void*, int, uint);
    void* malloc(uint);
    void free(void*);
    int atoi(const char*);
    int memcmp(const void *, const void *, uint);
    void *memcpy(void *, const void *, uint);

4. kernel/syscall.c 中新增 sys_sysinfo 函数定义

  • syscall.c 文件中,声明 sys_sysinfo 系统调用:

    1
    extern uint64 sys_sysinfo(void);

5. kernel/syscall.c 中函数指针数组新增 sysinfo 名称

  • syscalls[] 函数指针数组中,新增 sys_sysinfo 的映射:

    1
    [SYS_sysinfo] sys_trace,
  • syscall_names[] 数组中,为 sysinfo 添加名称:

    1
    2
    3
    4
    const char *syscall_names[] = {
    ...
    [SYS_sysinfo] "sysinfo",
    };

6. kernel/kalloc.c 中新增 freemem_size 函数

  • 实现 freemem_size 函数,获取当前系统的空闲内存量:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    int
    freemem_size(void) {
      struct run *r;
      int num = 0;

      for(r = kmem.freelist; r; r = r->next) {
        num++;
      }

      return num * PGSIZE;
    }

7. kernel/proc.c 中新增 proc_num 函数

  • 实现 proc_num 函数,计算当前非 UNUSED 状态的进程数目:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    int
    proc_num(void) {
      struct proc *p;
      uint64 num = 0;

      for(p = proc; p < &proc[NPROC]; p++) {
        if(p->state != UNUSED) {
          num++;
        }    
      }

      return num;
    }

8. kernel/defs.h 中新增函数声明

  • defs.h 文件中声明新增的两个函数 freemem_sizeproc_num

    1
    2
    3
    4
    5
    // kalloc.c
    void*           kalloc(void);
    void            kfree(void *);
    void            kinit(void);
    int             freemem_size(void); // here!
    1
    2
    3
    4
    5
    6
    // proc.c
    int             cpuid(void);
    void            exit(int);
    int             either_copyin(void *dst, int user_src, uint64 src, uint64 len);
    void            procdump(void);
    int             proc_num(void); // here!

9. kernel/sysproc.c 中新增 sysinfo 的实现

  • sysproc.c 文件中包含 sysinfo.h 头文件,并实现 sysinfo 系统调用:

    1
    #define "sysinfo.h"
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    uint64
    sys_sysinfo(void) {
      struct sysinfo info;
      uint64 addr;
      struct proc *p = myproc();

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

      info.freemem = freemem_size();
      info.nproc = proc_num();

      if(copyout(p->pagetable, addr, (char *)&info, sizeof(info)) < 0) {
        return -1;
      }

      return 0;
    }
-------------本文结束感谢您的阅读-------------