6.S081-Lab 2: System Calls

更好的阅读体验

  • 官方材料:https://pdos.csail.mit.edu/6.S081/2021/labs/syscall.html
  • 参考材料:
    • https://th0ar.gitbooks.io/xv6-chinese/content/
    • https://mit-public-courses-cn-translatio.gitbook.io/mit6-s081/

在开始编码之前,请阅读xv6书籍的第2章、第4章的4.3和4.4节以及相关的源文件:
将系统调用路由到内核的用户空间“stubs”位于user/usys中。S,在运行make时由用户/usys.pl生成。声明在user/user.h中
将系统调用路由到实现它的内核函数的内核空间代码位于kernel/syscall.c和kernel/syscall.h中。
与进程相关的代码是kernel/proc.h和kernel/proc.c。

System call tracing (moderate)

In this assignment you will add a system call tracing feature that may help you when debugging later labs. You’ll create a new trace system call that will control tracing. It should take one argument, an integer “mask”, whose bits specify which system calls to trace. For example, to trace the fork system call, a program calls trace(1 << SYS_fork), where SYS_fork is a syscall number from kernel/syscall.h. You have to modify the xv6 kernel to print out a line when each system call is about to return, if the system call’s number is set in the mask. The line should contain the process id, the name of the system call and the return value; you don’t need to print the system call arguments. The trace system call should enable tracing for the process that calls it and any children that it subsequently forks, but should not affect other processes.

由于 xv6 内核是一个操作系统,因此它包含内核代码和用户级代码。用户级代码通常编写为用户级进程,使用系统调用来与内核进行通信。

在本次任务中,您需要修改 xv6 内核来实现一个新的系统调用,以及一个用户级程序来调用该系统调用。下面是一些提示,以帮助您完成此任务:

  1. 在 Makefile 中添加 $U/_traceUPROGS,以便编译 trace 程序。

    UPROGS=\
    	$U/_cat\
    	$U/_echo\
    	$U/_forktest\
    	$U/_grep\
    	$U/_init\
    	$U/_kill\
    	$U/_ln\
    	$U/_ls\
    	$U/_mkdir\
    	$U/_rm\
    	$U/_sh\
    	$U/_stressfs\
    	$U/_usertests\
    	$U/_wc\
    	$U/_zombie\
    	$U/_trace
    
  2. 修改 user/user.h,添加 trace 系统调用的函数原型。

    // 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(const 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); // <--- Here!
    
  3. user/usys.pl 中添加对 trace 系统调用的支持。

    entry("fork");
    entry("exit");
    entry("wait");
    entry("pipe");
    entry("read");
    entry("write");
    entry("close");
    entry("kill");
    entry("exec");
    entry("open");
    entry("mknod");
    entry("unlink");
    entry("fstat");
    entry("link");
    entry("mkdir");
    entry("chdir");
    entry("dup");
    entry("getpid");
    entry("sbrk");
    entry("sleep");
    entry("uptime");
    entry("trace"); // <--- Here!
    

    运行后会生成汇编文件

    .global trace
    trace:
     li a7, SYS_trace
     ecall
     ret
    
  4. kernel/syscall.h 中添加 trace 系统调用的号码。

    // 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
    #define SYS_trace  22 // <--- Here
    
  5. kernel/syscall.c 中添加一个数组,用于将系统调用号码映射到名称。例如:

    static char* syscalls_name[] = {
    [SYS_fork]    "syscall fork",
    [SYS_exit]    "syscall exit",
    [SYS_wait]    "syscall wait",
    [SYS_pipe]    "syscall pipe",
    [SYS_read]    "syscall read",
    [SYS_kill]    "syscall kill",
    [SYS_exec]    "syscall exec",
    [SYS_fstat]   "syscall fstat",
    [SYS_chdir]   "syscall chdir",
    [SYS_dup]     "syscall dup",
    [SYS_getpid]  "syscall getpid",
    [SYS_sbrk]    "syscall sbrk",
    [SYS_sleep]   "syscall sleep",
    [SYS_uptime]  "syscall uptime",
    [SYS_open]    "syscall open",
    [SYS_write]   "syscall write",
    [SYS_mknod]   "syscall mknod",
    [SYS_unlink]  "syscall unlink",
    [SYS_link]    "syscall link",
    [SYS_mkdir]   "syscall mkdir",
    [SYS_close]   "syscall close",
    [SYS_trace]   "syscall trace",
    };
    
  6. 用 extern 全局声明新的内核调用函数,并且在 syscalls 映射表中,加入从前面定义的编号到系统调用函数指针的映射

    // Prototypes for the functions that handle system calls.
    extern uint64 sys_fork(void);
    extern uint64 sys_exit(void);
    extern uint64 sys_wait(void);
    extern uint64 sys_pipe(void);
    extern uint64 sys_read(void);
    extern uint64 sys_kill(void);
    extern uint64 sys_exec(void);
    extern uint64 sys_fstat(void);
    extern uint64 sys_chdir(void);
    extern uint64 sys_dup(void);
    extern uint64 sys_getpid(void);
    extern uint64 sys_sbrk(void);
    extern uint64 sys_sleep(void);
    extern uint64 sys_uptime(void);
    extern uint64 sys_open(void);
    extern uint64 sys_write(void);
    extern uint64 sys_mknod(void);
    extern uint64 sys_unlink(void);
    extern uint64 sys_link(void);
    extern uint64 sys_mkdir(void);
    extern uint64 sys_close(void);
    extern uint64 sys_trace(void); // <--- Here
    
    // An array mapping syscall numbers from syscall.h
    // to the function that handles the system call.
    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, // <--- Here
    };
    
  7. kernel/proc.h 中給proc 结构体添加 mask 字段

    uint64 mask;
    
  8. kernel/sysproc.c 中添加 sys_trace() 函数,该函数用于实现新的 trace 系统调用。在该函数中,将传递给 trace 系统调用的参数保存到进程的 proc 结构中。

    uint64
    sys_trace(void)
    {
      int mask;
      argint(0, &mask);
      struct proc *p = myproc();
      p->mask = mask;
      return 0;
    }
    
  9. 修改 kernel/proc.c 中的 fork() 函数,以便将父进程的跟踪掩码复制到子进程中。

    // Create a new process, copying the parent.
    // Sets up child kernel stack to return as if from fork() system call.
    int
    fork(void)
    {
      int i, pid;
      struct proc *np;
      struct proc *p = myproc();
    
      // Allocate process.
      if((np = allocproc()) == 0){
        return -1;
      }
    
      // 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;
    
      np->mask = p->mask;
    
      // copy saved user registers.
      *(np->trapframe) = *(p->trapframe);
    
      // Cause fork to return 0 in the child.
      np->trapframe->a0 = 0;
    
      // increment reference counts on open file descriptors.
      for(i = 0; i < NOFILE; i++)
        if(p->ofile[i])
          np->ofile[i] = filedup(p->ofile[i]);
      np->cwd = idup(p->cwd);
    
      safestrcpy(np->name, p->name, sizeof(p->name));
    
      pid = np->pid;
    
      release(&np->lock);
    
      acquire(&wait_lock);
      np->parent = p;
      release(&wait_lock);
    
      acquire(&np->lock);
      np->state = RUNNABLE;
      release(&np->lock);
    
      return pid;
    }
    
  10. 修改 kernel/syscall.c 中的 syscall() 函数,以在需要时输出跟踪信息。

    void
    syscall(void)
    {
      int num;
      struct proc *p = myproc();
    
      num = p->trapframe->a7;
      if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {
        // Use num to lookup the system call function for num, call it,
        // and store its return value in p->trapframe->a0
        p->trapframe->a0 = syscalls[num]();
        if ((p->mask >> num) &0b1) {
        printf("%d: %s -> %d\n", 
                p->pid, syscalls_name[num], p->trapframe->a0);
        }
      } else {
        printf("%d %s: unknown sys call %d\n",
                p->pid, p->name, num);
        p->trapframe->a0 = -1;
      }
    }
    
  11. 实现 user/trace.c 程序。该程序应该在新的进程中调用 trace 系统调用,然后运行另一个程序,以便跟踪指定的系统调用。在实现 user/trace.c 时,您需要使用 fork()exec() 系统调用来运行其他程序。

Answer

Sysinfo (moderate)

In this assignment you will add a system call, sysinfo, that collects information about the running system. The system call takes one argument: a pointer to a struct sysinfo (see kernel/sysinfo.h). The kernel should fill out the fields of this struct: the freemem field should be set to the number of bytes of free memory, and the nproc field should be set to the number of processes whose state is not UNUSED. We provide a test program sysinfotest; you pass this assignment if it prints “sysinfotest: OK”.

  1. kernel/sysinfo.h 中声明 struct sysinfo 结构体,并包含两个字段: uint64_t freememuint64_t nproc.

    struct sysinfo {
      uint64 freemem;   // amount of free memory (bytes)
      uint64 nproc;     // number of process
    };
    
  2. usys.pl

    entry("fork");
    entry("exit");
    entry("wait");
    entry("pipe");
    entry("read");
    entry("write");
    entry("close");
    entry("kill");
    entry("exec");
    entry("open");
    entry("mknod");
    entry("unlink");
    entry("fstat");
    entry("link");
    entry("mkdir");
    entry("chdir");
    entry("dup");
    entry("getpid");
    entry("sbrk");
    entry("sleep");
    entry("uptime");
    entry("trace");
    entry("sysinfo");
    
  3. user.h

    // 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(const 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);
    struct sysinfo;
    int sysinfo(struct sysinfo *);
    
  4. kernel/syscall.h 中添加一个名为 sysinfo() 的原型和一个新的系统调用号。

    // 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
    #define SYS_trace  22
    #define SYS_sysinfo  23
    
  5. kernel/sysproc.c 中实现 sys_sysinfo() 函数。

    uint64
    sys_sysinfo(void)
    {
      // user pointer to struct sysinfo
      uint64 si_addr;
    
      argaddr(0, &si_addr);
    
      struct sysinfo sysinfo;
      sysinfo.freemem = free_mem_num();
      sysinfo.nproc = num_of_processes();
    
      if (copyout(myproc()->pagetable, si_addr, (char *)&sysinfo, sizeof(sysinfo)) < 0)
        return -1;
    
      return 0;
    }
    
  6. 添加一个名为 free_mem_num() 的函数到 kernel/kalloc.c, 返回系统中空闲内存的字节数。

    // 统计未使用内存
    // 一页等于 4096 bytes
    uint64
    free_mem_num(void)
    {
    
      struct run *r;
      uint64 free_num = 0;
      acquire(&kmem.lock);
      r = kmem.freelist;
      while (r) {
        free_num++;
        r = r->next;
      }
      release(&kmem.lock);
      return free_num * PGSIZE;
    }
    
  7. 添加一个名为 num_of_processes() 的函数到 kernel/proc.c, 返回状态不是 UNUSED 的进程数量。

    // used by sysinfo
    int
    num_of_processes(void)
    {
      int nproc = 0;
      for (struct proc *p = proc; p < &proc[NPROC]; p++) {
        if (p->state != UNUSED)
          nproc++;
      }
      return nproc;
    }
    
  8. sysinfo() 函数中,分别调用 free_mem_num()num_of_processes() 函数来填充结构体 sysinfo 中的 freemem 和 nproc 字段。

  9. 使用 copyout() 函数将结构体 sysinfo 复制回用户空间。

Answer

  • 代码改动见 [commit](
### 系统调用在操作系统实验室中的相关内容 #### 系统调用的基础概念 系统调用(System Calls)是用户程序与操作系统内核交互的主要方式之一。通过这些接口,应用程序可以请求操作系统完成特定的任务,比如文件访问、进程管理以及内存分配等[^1]。 在一个典型的实验环境中,例如基于Xv6的操作系统课程中,学生通常会被要求实现新的系统调用或者修改现有的系统调用来加深对操作系统的理解[^4]。这种实践有助于掌握如何编写高效且安全的代码来处理资源管理和错误检测等问题。 #### 文件操作相关的系统调用数量分析 对于具体的文件操作而言,打开、关闭和读取文件等功能都需要相应的系统调用来支持。具体来说,在某些情况下可能只需要几次简单的调用就能完成基本功能;然而复杂场景下则需多次调用来满足需求[^2]。这取决于所使用的编程模型及其抽象层次的设计哲学——无论是采用单片式内核(monolithic kernel),还是微内核架构(microkernel architecture)[^1]。 #### 实验室环境下的应用实例 以`pgaccess_test.c`为例证说明了这样一个事实:即使是在受控条件下开发出来的测试工具也必须遵循标准流程并通过正式渠道即定义好的APIs去获取所需服务而不是绕过它们直接操控硬件资源如物理存储器单元等等[^4]。此例子强调了正确使用系统调的重要性同时也展示了实际编码过程中需要注意哪些方面才能确保最终产品既符合规格又能稳定运行于目标平台上。 以下是该示例的一个简化版本用于演示目的: ```c #include "types.h" #include "stat.h" #include "user.h" int main(int argc, char *argv[]) { int page = atoi(argv[1]); if (page >= 0 && page < NUM_PAGES) { printf(1, "Accessing page %d\n", page); access_page(page); // 假设这是我们要实现的新系统调用 } else { printf(2, "Invalid page number.\n"); } exit(); } ``` 这段伪代码片段显示了一个小型的应用程序怎样利用命令行参数指定要查询的具体页面索引号并调用假设存在的名为 `access_page()` 的新创建之系统函数来进行进一步的动作之前先验证输入数据的有效范围以防越界错误发生从而提高软件健壮性和安全性水平。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值