Loading

6.S081 lab2 syscall

#System call tracing

  • 跟着hints做就可以了,给系统调用trace添加一个系统调用号,并且将函数原型添加到kernel/syscall.h中。

  • kernel/proc.h中为proc结构体添加一个int mask成员,mask的32位分别标识是否追踪该位所代表的系统调用。比如write的系统调用号为16,那么当mask的第16位为1时,则表示需要追踪write系统调用。

  • 关键是实现sys_trace函数,在kernel/sysproc.c中实现该函数,该函数用argint获得了trace的参数,即mask掩码,将此mask掩码拷贝至当前用户进程中。代码如下:

    uint64
    sys_trace(void)
    {
        int mask;
        if (argint(0, &mask) < 0)
            return -1;
        myproc()->mask = mask;
        return 0;
    }
    

    上述函数中,argint(int num, int *p)函数是从p->trapframe中获得编号为num寄存器的值,对于系统调用,发生中断时,p->trapframe->a0存储了系统调用第一个参数的值,p->trapframe->a1存储了第二个参数的值,依此类推。

  • kernel/syscall.c中,syscall函数为系统调用函数,在该函数中添加代码实现跟踪系统调用的效果,我的实现方法如下:

    const char *syscallNames[] = {
        0, "fork", "exit", "wait",
        "pipe", "read", "kill", "exec",
        "fstat", "chdir", "dup", "getpid",
        "sbrk", "sleep", "uptime", "open",
        "write", "mknod", "unlink", "link",
        "mkdir", "close", "trace", "sysinfo"
    };
    
    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]();
        if (p->mask >> num & 1){
            printf("%d: syscall %s -> %d\n",p->pid, syscallNames[num], p->trapframe->a0);
        }
      } else {
        printf("%d %s: unknown sys call %d\n",
                p->pid, p->name, num);
        p->trapframe->a0 = -1;
      }
    }
    

    当发生系统调用时,p->trapframe->a7存储了系统调用号,主要到syscallssyscalls为全局的函数指针,通过系统调用号,调用对应的函数。当每次发生系统调用时,查看当前进程的mask掩码,并根据mask的值判断是否追踪这次系统调用。注意到p->trapframe->a0存储了系统调用的返回值。为了打印系统调用的名称,可以添加一个字符串数组syscallNames

  • 注意在fork系统调用时,子进程拷贝父进程的空间时,也应该拷贝父进程的掩码mask

#Sysinfo

  • trace,加入sysinfo的系统调用号以及相关函数声明。

  • kernel/kalloc.c中,xv6实现了管理物理内存的方法。kmem是一个全局结构体对象,xv6把空闲物理页组织为链表,kmem->freelist为物理空闲页的头指针。在kernel/riscv.h中,第323行#define PGSIZE 4096 // bytes per page定义了物理页的大小。那么遍历链表就可以获得空闲内存的大小了,具体代码如下:

    // Get the size of free memory
    uint64
    freemem(void)
    {
        struct run *p = kmem.freelist;
        uint64 sum = 0;
        while (p){
            sum += PGSIZE;
            p = p->next;
        }
        return sum;
    }
    
  • kernel/proc.c中,xv6实现了管理内存的方法。可以在kernel/proc.h中查看struct proc的成员,其中的state是一个枚举类型的成员,指明了进程的状态(第83行),在xv6中,每个进程共有五种状态。xv6最多可以有64个进程,这些进程被组织为一个数组prockernel/proc.h11行)。

    为了获取进程数,直接遍历proc数组即可,计算状态不为UNUSED的进程数。代码如下:

    // Get the number of proc
    uint64
    getproc(void)
    {
        uint64 cnt = 0;
        struct proc* p;
        for (p = proc; p < &proc[NPROC]; p++){
            if (p->state != UNUSED) cnt++;
        }
        return cnt;
    }
    
  • 最后就是实现sys_sysinfo了,在执行系统调用时,xv6还处于内核态,需要调用copyout函数把struct sysinfo复制到进程的用户空间中,代码如下:

    uint64
    sys_sysinfo(void)
    {
        uint64 p;
        struct sysinfo s;
        s.freemem = freemem();
        s.nproc = getproc();
        if (argaddr(0, &p) < 0)
            return -1;
        if (copyout(myproc()->pagetable, p, (char*)&s, sizeof(s)) < 0)
            return -1;
        return 0;
    }
    

通关截图

posted @ 2021-11-17 10:29  Kyoz1  阅读(91)  评论(0)    收藏  举报
7