Lab 2: system calls


Lab 2: system calls

环境配置

xv6 代码切换到syscall 分支并初始化

1
2
3
$ git fetch
$ git checkout syscall
$ make clean

一、trace 系统调用(难度:moderate)

添加一个trace system call调用,可以实现跟踪system call。此函数入参为一个数字,可以控制跟踪哪些system call。如:

trace(1<<SYS_fork),trace(10b),trace(2) 表示跟踪fork调用;

trace(1<<SYS_read),trace(10 0000b),trace(32) 表示跟踪read调用;

trace(10 0010b),trace(34) 表示跟踪fork、read调用;

1、系统调用流程

用户态中:

  • user/user.h 做函数声明
  • Makefile 调用usys.pl(perl脚本) 生成usys.S ,里面写了具体实现,通过ecall 进入 kernel,通过设置寄存器a7 的值,表明调用哪个system call
  • ecall 执行后存储用户态上下文信息到进程的trapframe 结构体中,并加载内核态页表、寄存器等信息

内核态中:

  • 开始执行syscall 函数,通过读取trapframea7 寄存器的值,判断系统调用类型
  • 通过函数指针执行对应内核函数,实现系统调用功能

2、具体实现

  • user/user.h 中申明sys_trace 函数
    1
    int trace(int);
  • user/usys.pl 中定义函数 entry
    1
    entry("trace");
  • Makefile 中添加$U/_trace
  • kernel/syscall.h 中添加系统调用号宏定义
    1
    #define SYS_trace 22
  • kernel/proc.hproc 结构体中增加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
    25
    struct proc
    {
    struct spinlock lock;

    // p->lock must be held when using these:
    enum procstate state; // Process state
    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

    // wait_lock must be held when using this:
    struct proc *parent; // Parent process

    // 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)
    char mask[32]; // Trace mask
    };
  • kernel/sysproc.c 新增函数sys_trace ,将系统调用号存入到proc 结构体的mask 数组中
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    uint64 sys_trace(void)
    {
    int n;
    if (argint(0, &n) < 0)
    return -1;
    struct proc *p = myproc();
    char *mask = p->mask;
    int i = 0;
    while (n > 0 && i < 32)
    {
    if (n % 2)
    {
    mask[i++] = '1';
    }
    else
    {
    mask[i++] = '0';
    }
    n >>= 1;
    }
    return 0;
    }
  • kernel/syscall.c 中引入sys_trace 函数申明,并与系统调用号进行关联
    1
    2
    3
    4
    extern uint64 sys_trace(void);

    // 在 (*syscalls[])(void) 中添加以下内容
    [SYS_trace] sys_trace,
  • kernel/syscall.c 中新建一个数组存放system_call 的名称
    1
    static char *syscall_names[32] = {"", "fork", "exit", "wait", "pipe", "read", "kill", "exec", "fstat", "chdir", "dup", "getpid", "sbrk", "sleep", "uptime", "open", "write", "mknod", "unlink", "link", "mkdir", "close", "trace"};
  • kernel/syscall.csyscall 函数中实现具体的跟踪功能,即判断当前系统调用号是否在需要跟踪的mask
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    void syscall(void)
    {
    int num;
    struct proc *p = myproc();

    num = p->trapframe->a7;
    if (num > 0 && num < NELEM(syscalls) && syscalls[num])
    {
    p->trapframe->a0 = syscalls[num]();
    // trace begin
    if (strlen(p->mask) > 0 && p->mask[num] == '1')
    {
    printf("%d: syscall %s -> %d\n", p->pid, syscall_names[num], p->trapframe->a0);
    }
    // trace end
    }
    else
    {
    printf("%d %s: unknown sys call %d\n",
    p->pid, p->name, num);
    p->trapframe->a0 = -1;
    }
    }
  • kernel/proc.cfork 函数中实现自动复制父进程的mask
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // Copy user memory from parent to child.
    if (uvmcopy(p->pagetable, np->pagetable, p->sz) < 0)
    {
    freeproc(np);
    release(&np->lock);
    return -1;
    }
    np->sz = p->sz;

    // Copy mask from parent to child
    safestrcpy(np->mask, p->mask, sizeof(p->mask));

    // copy saved user registers.
    *(np->trapframe) = *(p->trapframe);

二、sysinfo 系统调用(难度:moderate)

添加一个 sysinfo system call 调用,可以实现打印可用空间(字节)、可用进程数。

1、具体实现

  • user/user.h 中申明sysinfo 结构体并申明sys_sysinfo 函数
    1
    2
    3
    struct sysinfo;

    int sysinfo(struct sysinfo *);
  • user/usys.pl 中定义函数 entry
    1
    entry("sysinfo");
  • Makefile 中添加$U/_sysinfotest
  • kernel/syscall.h 中添加系统调用号宏定义
    1
    #define SYS_sysinfo 23
  • kernel/kalloc.c 新增函数freemem_size 用来统计可用空间大小
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    int freemem_size(void)
    {
    struct run *r;
    int num = 0;
    for (r = kmem.freelist; r; r = r->next)
    {
    num++;
    }
    return num * PGSIZE;
    }
  • kernel/proc.c 新增函数proc_num 用来统计可用进程数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    int proc_num(void)
    {
    struct proc *p;
    uint64 num = 0;
    for (p = proc; p < &proc[NPROC]; p++)
    {
    if (p->state != UNUSED)
    num++;
    }
    return num;
    }
  • kernel/defs.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
    // kalloc.c
    void *kalloc(void);
    void kfree(void *);
    void kinit(void);
    int freemem_size(void); // 新增

    // proc.c
    int cpuid(void);
    void exit(int);
    int fork(void);
    int growproc(int);
    void proc_mapstacks(pagetable_t);
    pagetable_t proc_pagetable(struct proc *);
    void proc_freepagetable(pagetable_t, uint64);
    int kill(int);
    struct cpu *mycpu(void);
    struct cpu *getmycpu(void);
    struct proc *myproc();
    void procinit(void);
    void scheduler(void) __attribute__((noreturn));
    void sched(void);
    void sleep(void *, struct spinlock *);
    void userinit(void);
    int wait(uint64);
    void wakeup(void *);
    void yield(void);
    int either_copyout(int user_dst, uint64 dst, void *src, uint64 len);
    int either_copyin(void *dst, int user_src, uint64 src, uint64 len);
    void procdump(void);
    int proc_num(void); // 新增
  • kernel/sysproc.c 引入sysinfo.h ,并新增函数sys_sysinfo ,调用上面的两个函数并将结果封装到sysinfo 结构体中,使用copyout 函数将结构体传回用户态
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #include "sysinfo.h"

    uint64 sys_sysinfo(void)
    {
    uint64 addr;
    struct proc *p = myproc();
    struct sysinfo info;
    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;
    }
  • kernel/syscall.c 中引入sys_sysinfo 函数申明,并与系统调用号进行关联
    1
    2
    3
    4
    extern uint64 sys_sysinfo(void);

    // 在 (*syscalls[])(void) 中添加以下内容
    [SYS_sysinfo] sys_sysinfo,
  • kernel/syscall.csystem_call 中加入sysinfo
    1
    static char *syscall_names[32] = {"", "fork", "exit", "wait", "pipe", "read", "kill", "exec", "fstat", "chdir", "dup", "getpid", "sbrk", "sleep", "uptime", "open", "write", "mknod", "unlink", "link", "mkdir", "close", "trace", "sysinfo"};

实验结果


文章作者: leven
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 leven !
评论
  目录