首页 > 其他分享 >MIT6.S081 Lab syscall

MIT6.S081 Lab syscall

时间:2024-04-24 14:46:21浏览次数:20  
标签:trace MIT6 mask system SYS syscall call S081

这一个实验的主要内容就是给 xv6 添加两个系统调用:tracesysinfo

Using gdb (easy)

这个部分我就不做了……

M1 的 Macbook Air 上的 gdb 太难安装了,所以暂时用不了 gdb 调试……

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 tracesystem call should enable tracing for the process that calls it and any children that it subsequently forks, but should not affect other processes.

这个任务要求我们添加一个系统调用 trace。这个系统调用会给当前进程添加一个 mask,代表一系列我们需要跟踪的系统调用编号集合。当这个进程调用了在 mask 中的系统调用的时候,在即将返回前我们就要打印类似于下面的一行信息:

3: syscall read -> 1023

这一行信息中,依次有效的内容是进程号、系统调用名和返回值。

这个 trace 系统调用设置的 mask 应该对所有其子进程生效。

准备系统调用

我们跟着 Hints 中的步骤一步步来。

Add $U/_trace to UPROGS in Makefile

Makefile 中添加终端命令,这个我们很熟悉了:

UPROGS=\
	......
	$U/_trace\

Run make qemu and you will see that the compiler cannot compile user/trace.c, because the user-space stubs for the system call don't exist yet: add a prototype for the system call to user/user.h, a stub to user/usys.pl, and a syscall number to kernel/syscall.h. The Makefile invokes the perl script user/usys.pl, which produces user/usys.S, the actual system call stubs, which use the RISC-V ecall instruction to transition to the kernel. Once you fix the compilation issues, run trace 32 grep hello README; it will fail because you haven't implemented the system call in the kernel yet.

首先我们要添加在 user/user.h 中添加 trace 函数的原型:

// system calls
int fork(void);
// .......
int trace(int);

然后在 user/usys.pl 中添加系统调用的存根:

entry("trace");

接着在 kernel/syscall.h 中添加一个系统调用号:

#define SYS_trace  22

添加 struct proc 的成员

我们需要在每个进程的状态中,添加一个成员变量来记录这个 mask。

进程的结构体被定义在 kernel/proc.h 中,我们只需要在最后加上 mask 的定义即可。

struct proc {
    // ...
    // mask
  	int trace_mask;
}

编写 sys_trace

trace 系统调用的具体实现需要在 kernel/sysproc.c 中定义一个叫做 sys_trace 的函数。

我们可以观察这个文件中已经存在的函数,了解如何编写一个系统调用。

uint64
sys_exit(void)
{
  int n;
  argint(0, &n);
  exit(n);
  return 0;  // not reached
}

uint64
sys_getpid(void)
{
  return myproc()->pid;
}

从这个文件开头的前两个函数中,我们可以得知两个有用的信息:

  • 当仅传递一个 int 类型的参数的时候,我们可以使用 argint(0, &x) 函数来将之存入 int 类型的变量 x 中。
  • 如果需要获得当前进程的结构体指针,可以使用 myproc() 函数。

于是我们很容易就可以写出这个系统调用的实现:

uint64
sys_trace(void)
{
  int mask;

  argint(0, &mask);
  
  myproc()->trace_mask = mask;

  return 0;
}

修改 fork

前面我们在要求中说到,这个 trace 系统调用设置的 mask 应该对所有其子进程生效。

这也就要求我们,在 fork 出一个子进程的时候,应该将父进程的 mask 赋值给子进程的 mask。这很容易实现:

int
fork(void)
{
  // ...
  np->cwd = idup(p->cwd);

  np->trace_mask = p->trace_mask;

  safestrcpy(np->name, p->name, sizeof(p->name));

  pid = np->pid;

  release(&np->lock);
  // ...
}

修改 syscall

这里我们要修改 syscall 文件了。

大概有三步。

首先,我们需要将 sys_trace 函数添加进 syscalls 这个函数指针数组中,在此之前不要忘记声明一下外部函数。

extern uint64 sys_trace(void);

static uint64 (*syscalls[])(void) = {
// ...
[SYS_trace]   sys_trace,
};

之后,我们要定义一个字符串数组,用来将系统调用编号映射成对应的名称。

static char *syscall_name[] = {
[SYS_fork]    "fork",
[SYS_exit]    "exit",
[SYS_wait]    "wait",
[SYS_pipe]    "pipe",
[SYS_read]    "read",
[SYS_kill]    "kill",
[SYS_exec]    "exec",
[SYS_fstat]   "fstat",
[SYS_chdir]   "chdir",
[SYS_dup]     "dup",
[SYS_getpid]  "getpid",
[SYS_sbrk]    "sbrk",
[SYS_sleep]   "sleep",
[SYS_uptime]  "uptime",
[SYS_open]    "open",
[SYS_write]   "write",
[SYS_mknod]   "mknod",
[SYS_unlink]  "unlink",
[SYS_link]    "link",
[SYS_mkdir]   "mkdir",
[SYS_close]   "close",
[SYS_trace]   "trace"
};

最后,我们需要修改 syscall 函数,在系统调用即将返回的时候,判断这个调用是否在 mask 中,并进行相应的输出。

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]();

    // processing trace
    if ((p->trace_mask >> num) & 1) {
      printf("%d: syscall %s -> %d\n", p->pid, syscall_name[num], p->trapframe->a0);
    }
  } else {
    printf("%d %s: unknown sys call %d\n",
            p->pid, p->name, num);
    p->trapframe->a0 = -1;
  }
}

运行结果

./20231019-MIT6-S081-syscall/image-20231019095919654

./20231019-MIT6-S081-syscall/image-20231019100014463

评测结果

./20231019-MIT6-S081-syscall/image-20231019100223447

完美通关!

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".

这个任务主要是要添加 sysinfo 系统调用,这个系统调用只要获得两个关于系统的信息:

  • 物理内存中空闲空间的大小
  • 进程的数量

准备系统调用

这里和前一个任务基本上差不多,就不展开了。

需要注意的区别是在 user/user.h 中,要记得声明 struct sysinfo

struct sysinfo;
int sysinfo(struct sysinfo *);

计算进程总数

通过阅读 kernel/proc.c 的一些代码可知,系统的进程都存放在 proc 数组中。因此只需要枚举 proc 数组,对于每一项都判断其 state 是否为 UNUSED 即可。

uint64 count_process(void) {
  uint64 cnt = 0;

  for (int i = 0; i < NPROC; ++i) {
    if (proc[i].state != UNUSED) {
      ++cnt;
    }
  }

  return cnt;
}

计算空闲内存

这里需要一些虚拟内存的知识了……不过还好,即使没学过仿照别的函数应该也能写出来。

xv6 是将所有的空闲空间组织成链表,链表每一项的信息直接存放在空闲页中。因此,我们只需要从链表首指针开始,通过 next 成员遍历链表同时计数,即可得到结果。

uint64 count_free_memory(void) {
  struct run *r;
  uint64 cnt = 0;

  acquire(&kmem.lock);
  r = kmem.freelist;
  while (r) {
    ++cnt;
    r = r->next;
  }
  release(&kmem.lock);

  return cnt * PGSIZE;
}

编写 sys_sysinfo

首先要声明 count_free_memorycount_process 两个外部函数。

sys_sysinfo 函数中,我们首先使用 argaddr 获得需要存放结果的地址。这个地址是用户进程的虚拟地址。

然后我们定义局部变量 info 来中转结果。

最后,使用 copyout 函数,将内核中的数据,存放到用户进程的虚拟内存中。

extern uint64 count_free_memory(void);
extern uint64 count_process(void);

uint64 sys_sysinfo(void) {
  struct sysinfo *dest;
  argaddr(0, (uint64*)&dest);

  struct sysinfo info;
  info.freemem = count_free_memory();
  info.nproc = count_process();

  if(copyout(myproc()->pagetable, (uint64)dest, (char *)&info, sizeof(info)) < 0)
    return -1;
  return 0;
}

修改 syscall.c

主要是有三个地方要改,一个是添加 sys_sysinfo 外部函数的声明,还有 syscalls 函数指针列表,syscall_name 系统调用名列表。

和前一个任务类似,不展开。

结果

./20231019-MIT6-S081-syscall/image-20231019112339668

测试

./20231019-MIT6-S081-syscall/image-20231019112359580

得分

./20231019-MIT6-S081-syscall/image-20231019112426146

完美通关!

标签:trace,MIT6,mask,system,SYS,syscall,call,S081
From: https://www.cnblogs.com/hankeke303/p/18155265/MIT6-S081-syscall

相关文章

  • MIT6.S081 Lab Page Tables
    实验开始前的折腾突然发现2023版的和2020版的实验内容其实还不一样……因为我正在看的视频以及参考资料都是基于2020版的课程,因此我还是决定将之前的实验都迁移到2020版的xv6-lab-2020来。在自己的MacbookAir上折腾了好久……还是没能成功。因此还是用上了我在阿......
  • MIT6.S081 - Lab2: system calls
    Lab2:systemcalls预备知识执行一次系统调用的流程:USERMODEstep1:系统调用声明user/user.h:系统调用函数(如intfork(void))step2:ecall进入内核态user/usys.S(该文件由user/usys.pl生成,后续添加函数可以在这里添加):执行如下命令.globalforkfork:lia7,SYS_f......
  • MIT6824 MapReduce总结
    MapReduce是一个分布式大任务计算框架,旨在可以方便Google内部的将大型任务拆分到集群环境下,以得到并行化的处理速度。在分布式情况下,多台机器协作完成一个大型任务需要考虑很多问题:整个分布式系统中都有哪些角色?可以预见的就是肯定有任务的拆分者负责拆分调度任务,有任务的实际......
  • MIT6.S081 - Lecture3: OS Organization and System Calls
    为什么要使用操作系统使用操作系统的主要原因是为了实现CPU多进程分时复用以及内存隔离如果没有操作系统,应用程序会直接与硬件进行交互,这时应用程序会直接使用CPU,比如假设只有一个CPU核,一个应用程序在这个CPU核上运行,但是同时其他程序也需要运行,因为没有操作系统来帮助......
  • MIT6.S081 - Lab1: Xv6 and Unix utilities
    Part1:sleep实验要求与提示可以参考user/echo.c,user/grep.c和user/rm.c文件如果用户忘记传递参数,sleep应该打印一条错误消息命令行参数传递时为字符串,可以使用atoi函数将字符串转为数字使用系统调用sleep,有关实现sleep系统调用的内核代码参考kernel/sysproc.c(......
  • MIT6.S081 - Lecture1: Introduction and Examples
    课程简介课程目标理解操作系统的设计和实现通过XV6操作系统动手实验,可以扩展或改进操作系统操作系统的目标Abstraction:对硬件进行抽象Multiplex:在多个应用程序之间共用硬件资源Isolation:隔离性,程序出现故障时,不同程序之间不能相互干扰Sharing:实现共享,如数据交互或协......
  • 免杀-syscall
    3x3syscall我们windowsapi的调用,通过层层调用最终还是会进入ntdll的底层函数的调用,再通过syscall快速调用进入0环实现的代码,下面我将记录一些syscall的底层基础知识,最后的代码实现是通过现成项目直接快速调用敏感api,这种现成syscall的项目很多,但是感觉都比较久了免杀效果不太好......
  • MIT 6.S081入门lab10 mmap
    MIT6.S081入门lab10mmap一、参考资料阅读与总结1.JournalingtheLinuxext2fsFilesystem文件系统可靠性:磁盘崩溃前数据的稳定性;故障模式的可预测性;操作的原子性-论文核心:将日志事务系统加入Linux的文件系统中;事务系统的要求:元数据的更新;事务系统的顺序性;数据块写入磁......
  • MIT 6.S081入门lab8 锁
    #MIT6.S081入门lab8锁一、参考资料阅读与总结1.xv6book书籍阅读(Chapter7:Scheduling:7.5toend)5.sleep与wakeupxv6使用了sleep-wake的机制,实现了进程交互的抽象(序列协调/条件同步机制)这一机制的核心是防止丢失唤醒(生产者还未睡眠时,资源更新并唤醒):如果贸然在睡眠中加......
  • MIT 6.S081入门lab7 多线程
    MIT6.S081入门lab7多线程一、参考资料阅读与总结1.xv6book书籍阅读(Chapter7:SchedulingthroughSection7.4)1.概述:由于操作系统往往运行比CPU数量更多的进程,因此需要对CPU进行虚拟化,使多个进程能够分时复用CPU资源2.多路复用:xv6中多路复用有2种方式:sleep和wakeup机制......