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()`函数,以及在用户态和内核态间进行数据传递的方法。

添加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;
}

请添加图片描述

MIT 6.S081 是麻省理工学院开设的一门关于操作系统的课程,课程全称为 **MIT 6.S081: Operating System Engineering**。该课程以实践为导向,通过让学生从零开始逐步构建一个简单的类 Unix 操作系统内核(基于 RISC-V 架构),深入理解操作系统的核心原理和实现机制。 课程内容涵盖线程、调度、虚拟内存、文件系统、系统调用、中断、设备驱动、同步机制等操作系统关键主题。课程材料公开,包括讲义、实验(labs)、视频讲座和源代码,非常适合自学。 ### 课程资源获取方式 1. **官方网站** MIT OpenCourseWare 提供了完整的课程材料,包括实验指导、源代码、讲义和部分视频讲座。 - 官网地址:[https://pdos.csail.mit.edu/6.828/2020/index.html](https://pdos.csail.mit.edu/6.828/2020/index.html) (注意:6.S0816.828 的新编号,内容基本一致) 2. **XV6 操作系统源码** 课程使用 xv6 作为教学操作系统,它是 Unix V6 的简化版,使用 C 语言编写,适合教学和学习。 - 源码地址:[https://github.com/mit-pdos/xv6-riscv](https://github.com/mit-pdos/xv6-riscv) 3. **实验(Labs)** 课程实验包括: - **Lab 1: Xv6 and Unix utilities** - **Lab 2: System calls** - **Lab 3: Page tables** - **Lab 4: Traps** - **Lab 5: Threads** - **Lab 6: Locking** - **Lab 7: File system** - **Project: Networking (optional)** 每个实验都要求修改 xv6 源码以实现特定功能,例如添加系统调用、实现线程调度、实现文件系统操作等。 4. **社区与讨论区** - **GitHub 项目**:许多学生将他们的实验成果上传到 GitHub,可以参考他们的实现思路和代码。 - **Reddit 和 Stack Overflow**:在 r/mit、r/operatingsystems 和 Stack Overflow 上可以找到相关讨论和问题解答。 - **知乎、优快云 等中文社区**:一些中文用户也分享了他们对 MIT 6.S081 的学习笔记和实验解析。 5. **学习建议** - 熟悉 C 语言和汇编基础。 - 掌握基本的数据结构(如链表、队列、树等)。 - 理解计算机体系结构(特别是 RISC-V)。 - 有操作系统基础理论知识(如进程、内存管理、文件系统等)。 ### 示例代码:添加系统调用 以下是一个简单的系统调用示例,用于在 xv6 中添加一个新的系统调用 `sys_helloworld`: ```c // user/user.h int helloworld(void); // user/usys.pl entry("helloworld"); // kernel/syscall.h #define SYS_helloworld 22 // kernel/syscall.c extern uint64 sys_helloworld(void); // kernel/sysproc.c uint64 sys_helloworld(void) { printf("Hello, world from kernel!\n"); return 0; } ``` ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值