MIT_6.s081_Lab2:Xv6 and Syscall

2022-12-08 14:37:58 浏览数 (1)

MIT_6.s081_Lab2:Xv6 and Syscall

于2022年3月4日2022年3月4日由Sukuna发布

Lab2_1 Trace.

实验2_1主要是完成一个新的系统调用,这个系统调用主要的功能就是追踪,主要就是创建一个新的跟踪系统调用来控制跟踪,它应该采用一个参数,一个整数“掩码”,其位指定要跟踪的系统调用.比如说跟踪fork系统调用就会调用trace(1<<SYS_USER_FORK).我们需要修改 xv6 内核以在每个系统调用即将返回时打印出一行.该行应包含进程id、系统调用的名称和返回值,我们还必须对这个进程以及所有子进程进行跟踪.

代码语言:javascript复制
//trace.c
int main(int argc, char *argv[])
{
  int i;
  char *nargv[MAXARG];

  if(argc < 3 || (argv[1][0] < '0' || argv[1][0] > '9')){
    fprintf(2, "Usage: %s mask commandn", argv[0]);
    exit(1);
  }

  if (trace(atoi(argv[1])) < 0) {
    fprintf(2, "%s: trace failedn", argv[0]);
    exit(1);
  }
  
  for(i = 2; i < argc && i < MAXARG; i  ){
    nargv[i-2] = argv[i];
  }
  exec(nargv[0], nargv);
  exit(0);
}

我们发现在调用trace的时候,首先判断输入参数的合法性,然后再进行trace操作,接着再执行剩下的操作.这个时候trace就是一个系统调用,我们需要完成系统调用.

现在我们开始实验:

1) 在user.h中添加对于trace函数的支持.

这里面存储了所有user函数会调用的系统调用.

2) 添加一个entry在user.pl里面
代码语言:javascript复制
#!/usr/bin/perl -w

# Generate usys.S, the stubs for syscalls.

print "# generated by usys.pl - do not editn";

print "#include "kernel/syscall.h"n";

sub entry {
    my $name = shift;
    print ".global $namen";
    print "${name}:n";
    print " li a7, SYS_${name}n";
    print " ecalln";
    print " retn";
}
	
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");

这个user.pl负责输出汇编代码,这个汇编代码就是user文件夹里面的代码调用系统调用之后由U态进入到S态的过渡代码,主要是构建参数的代码和ecall组成.

执行顺序:调用系统调用->user.pl生成的代码(U态进入到S态)->S态的系统调用.

3) 在syscall.h添加系统调用号
代码语言:javascript复制
// 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

这个文件里面存储了系统调用的调用调用号,在后面这些宏就负责替换为编号.

4) 在proc结构体里面添加一个mask成员,用来记录要trace哪种系统调用.
5) 在kernel/sysproc.c里面实现具体的系统调用,在这里你会发现系统调用的实现会保存在这里.
代码语言:javascript复制
uint64
sys_trace(void)
{
  int n;
  if (argint(0, &n) < 0) // get the number of argv[1].
    return -1;
  myproc()->mask = n;    // save in the mask.
  return 0;
}

这个时候可以通过argint系统调用来获得栈帧中保存的寄存器值.然后把寄存器的值保存到mask元素中.

6) 更改fork函数,添加mask的复制.np->mask = p->mask;

这一步的目的就是为了让子进程执行系统调用也可以完成.

7) 添加syscall.c中关于trace函数的支持.
代码语言:javascript复制
//在syscall的函数数组中添加即可.
......
[SYS_close]   sys_close,
[SYS_trace]   sys_trace,
};
//添加函数声明.
extern uint64 sys_trace(void);
8) 更改syscall.c的syscall函数
代码语言:javascript复制
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 & (1 << num))
    {
      printf("%d: syscall %s -> %dn",p->pid, syscalls_name[num], p->trapframe->a0);
    }
  } else {
    printf("%d %s: unknown sys call %dn",
            p->pid, p->name, num);
    p->trapframe->a0 = -1;
  }
}

添加关于追踪的函数,追踪的方法很简单,因为要追踪的mask是处于第几位的,只需要求一个与看看是不是0即可.

全部过关

Lab2_2 sys info

我们需要完成一个系统调用,给定一个struct sysinfo的指针,然后可以输出当前系统的可用进程数和可用内存数.

1) 在makefile,user.pl和user.h添加对于sysinfo系统调用的支持.(不懂的请翻阅上面)
2) 统计当前正在使用进程数.定义新函数proc_size(),在proc.h中
代码语言:javascript复制
//return: the proc that are occupied.
int
proc_size()
{
  int i;
  int n = 0;
  for (i = 0; i < NPROC; i  )
  {
    if (proc[i].state != UNUSED) n  ;
  }
  return n;
}

方法就是对于所有进程(NPROC)进行一遍遍历,如果不是UNUSED,就代表已经使用了

3) 统计当前的内存数,定义新函数freememory(),在kalloc.h中.
代码语言:javascript复制
uint64 
freememory()
{
  struct run* p = kmem.freelist;
  uint64 num = 0;
  while (p)
  {
    num   ;
    p = p->next;
  }
  return num * PGSIZE;
}

获得freelist(物理内存中没有分配的块),然后对链表进行一遍遍历,找到有几个块没有分配,乘以一个块的数量即可.

4) 完成sysinfo的操作.注意2)和3)定义的函数要声明在def.h
代码语言:javascript复制
uint64
sys_sysinfo(void)
{
  struct sysinfo info;
  uint64 addr;
  // get the the address of the sysinfo structure.
  if (argaddr(0, &addr) < 0) 
    return -1;
  struct proc* p = myproc();
  //get freemem
  info.freemem = freememory();
  //get the proc
  info.nproc = proc_size();
  // copyto the structure.
  if (copyout(p->pagetable, addr, (char*)&info, sizeof(info)) < 0)
    return -1;
  return 0;
}

这个操作主要是,用户会传递指针来,这个指针就指向了sysinfo的结构体,所以说我们需要在内核态获得信息构造一个新的结构体传回去就可以了.

5) 像上个实验一样完成syscall.h,syscall.c的支持即可.

完成.

0 人点赞