mit6.s081 lab2 System calls

这篇博客介绍了如何在操作系统内核中实现系统调用`trace`和`sysinfo`。`trace`允许追踪指定的系统调用,而`sysinfo`用于获取系统的内存使用和进程数量信息。文章详细阐述了修改`Makefile`、`user/trace.c`、`kernel/proc.h`、`kernel/sysproc.c`、`kernel/syscall.c`等文件的过程,包括添加新的系统调用声明、实现`sys_trace()`和`sys_sysinfo()`函数,以及在用户态和内核态间进行数据传递的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

添加mit仓库

  • 添加remote
git remote add mit git://g.csail.mit.edu/xv6-labs-2020
  • 从mit拉取分支
git fetch mit
git checkout syscall
chapter 2
System call tracing

写一个程序追踪另一个程序所调用的system call,打印追踪的syscall的syscall num, syscall num, 以及调用syscall的返回值,需要注意的是如果mask中标识需要追踪trace,那么第一次调用trace时也需要打印trace syscall的追踪信息

$ trace 32 grep hello README
3: syscall read -> 1023
3: syscall read -> 966
3: syscall read -> 70
3: syscall read -> 0

$ trace 2147483647 grep hello README
4: syscall trace -> 0
4: syscall exec -> 3
4: syscall open -> 3
4: syscall read -> 1023
4: syscall read -> 966
4: syscall read -> 70
4: syscall read -> 0
4: syscall close -> 0

主要进行以下修改:
1.在Makefile中添加trace程序
2.user/trace.c中已经完成了trace程序,但是syscall trace还没有完成,所以这个exercise的主要工作为完成syscall trace
3.在kernel/proc.h中process的结构体中增加mask成员,用于记录需要追踪哪些syscall
3.在kernel/sysproc.c中完成sys_trace(),sys_trace就是trace syscall实际执行的函数,sys_trace函数的主要工作是修改process中的mask,从user space中获取trace syscall的参数的方式为使用argint函数(如argint(0, &mask)),argint实际上是从process的trapframe中获取对应位置的寄存器内容
4.修改fork()(kernel/proc.c),创建新的process继承父process的mask值
5.修改syscall()(kernel/syscall.c),这部分要做的工作是声明一个syscall name string数组,用于根据syscall num获取对应的syscall名。另外一个工作是获取当前process的mask值,判断当前调用的syscall是否是需要追踪的,如果需要追踪则将对应的信息打印出来

  • kernel/sysproc.c
uint64 sys_trace(void) {
    int mask = 0;
    if (argint(0, &mask) < 0)
      return -1;
    myproc()->mask = mask;
    return 0;
}
  • kernel/syscall.c
    添加sys_systrace的函数声明
extern uint64 sys_trace(void);

在函数指针数组syscalls中添加sys_trace函数指针

[SYS_trace]   sys_trace,

修改函数syscall

char* system_call_name[] = {
    "",
    "fork",
    "exit",
    "wait",
    "pipe",
    "read",
    "kill",
    "exec",
    "fstat",
    "chdir",
    "dup",
    "getpid",
    "sbrk",
    "sleep",
    "uptime",
    "open",
    "write",
    "mknod",
    "unlink",
    "link",
    "mkdir",
    "close",
    "trace",
};

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]();
    int mask = p->mask;
    if ((mask >> num) & 0x1) {
      printf("%d: syscall %s -> %d\n", p->pid, system_call_name[num], p->trapframe->a0);
    }
  } else {
    printf("%d %s: unknown sys call %d\n",
            p->pid, p->name, num);
    p->trapframe->a0 = -1;
  }
}
  • 在kernel/syscall.h中增加
#define SYS_trace  22
  • 在user/user.h中增加
int trace(int);
  • 在usys.pl中增加
entry("trace");

usys.pl中生成user mode下的system call的内容,具体为设置对应syscall num到寄存器a7中,然后调用ecall

Sysinfo

这部分的内容是实现sysinfo syscall,sysinfo用于收集系统中的信息,包括系统中空闲内存的大小(byte),以及系统中的进程数量,这两项信息在struct sysinfo(kernel/sysinfo.h)中记录

  • sysinfo的用户态调用函数的原型为int sysinfo(struct sysinfo *),在此syscall中的工作为传入sysinfo结构,并将结果填充到该sysinfo结构中
  • 首先要添加syscall的声明,步骤与trace相同
  • 然后是在kernel/kalloc.c中添加获取空闲内存容量的函数,根据kmalloc.c中的其他函数可知在系统中维护了一条空闲链表,每个链表节点为一个PGSIZE的页,所以获取空闲内存容量就是遍历空闲链表,获取空闲链表节点数量,空闲内容容量为空闲链表节点数量 * PGSIZE,在遍历空闲链表的过程中需要对空闲链表进行上锁
// 获取空闲内存容量
uint64 get_free_memory_info() {
  struct run *r;
  int freelist_size = 0;
  
  acquire(&kmem.lock);
  r = kmem.freelist;
  while (r) {
    freelist_size++;
    r = r->next;
  }
  uint64 res = freelist_size * PGSIZE;
  release(&kmem.lock);
  return res;
}
  • 在kernel/proc.c中添加获取系统中的process数量的函数,在proc.c中维护了一个数组struct proc proc[NPROC],遍历此数组,如果proc的state不为UNUSED那么说明这是一个有效的proc,统计有效的proc数量并返回
uint64 get_proc_num() {
  struct proc *p;
  uint64 proc_num = 0;
  for (p = proc; p < &proc[NPROC]; p++) {
    if (p->state != UNUSED)
      proc_num++;
  }
  return proc_num;
}
  • 在kernel/defs.h中对以上两个函数进行声明
  • 在kernel/sysproc.c中实现sys_sysinfo函数,在该函数中获取用户传入的sysinfo结构体的指针(user_info)可以使用argaddr实现,首先在函数声明一个sysinfo kernel_info,通过上述的两个函数获取系统中空闲内存容量以及进程数量,填充kernel_info,然后需要将kernel_info的内容拷贝到user_info中,这里涉及到kernel space到user space的内存拷贝操作,可以参考sys_fstat() (kernel/sysfile.c)和filestat() (kernel/file.c) 中的做法,使用copyout进行拷贝,copyout的参数依次为当前process的pagetable,user space的目标内存起始地址,kernel space的拷贝内存气势地址,需要拷贝的长度
uint64 sys_sysinfo(void) {
  uint64 user_info;
  if (argaddr(0, &user_info) < 0)
    return -1;
  struct sysinfo kernel_info;
  kernel_info.freemem = get_free_memory_info();
  kernel_info.nproc = get_proc_num();
  
  struct proc *p = myproc();
  if (copyout(p->pagetable, user_info, (char*)&kernel_info, sizeof(kernel_info)) < 0)
    return -1;
  return 0;
}

请添加图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值