6.s081学习记录(二)systemcall

315 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情

这是我对MIT操作系统课程6.s081中的Lab的完成记录。


参考资料

实验准备

一定要认真看官方实验要求,特别是给出的hint!!!能解决90%的问题!!

切换到当前任务实验环境

  $ git fetch
  $ git checkout syscall

我记得我当时用的是git switch syscall。

上一个实验也提到过的make clean 操作: make clean

image.png

(确实是这样,过程中某次make qemu之前忘记了make clean,结果失败,clean一下再试就可以了)

sytemcall tracing

已有条件

系统已配备用户级别的trace程序(代码见user/trace.c)

image.png 我们要完成的任务就是将这个用户级别的程序与底层联系起来,使得trace程序能被调用,借此理解系统调用的过程。

我们要完成的任务

在如下文件中添加东西:(基本上就是打开文件,上面几行代码怎么写,就依葫芦画瓢)

  • 在Makefile文件的UPROGS中加入$U/_trace image.png image.png

  • 在user/user.h的syscall中增加原型

image.png

  • 在user/usys.pl中添加存根

image.png

该文件编译的时候会产生user/usys.S文件,这是一个汇编文件,和trace有关的文件长这样:

image.png 其中的ecall指令会将系统调用的存根(stub)传递到内核。

  • kernel/syscall.h中指明trace功能对应数字22

image.png

接下来是内核层面的操作:

  • 在kernel/sysproc.c中添加一个sys_trace函数。 理论上这个sys_trace函数可以添加到kernel的任何地方,只要能找到就行(所以我看樊潇大佬就是新建了一个sys_trace.c文件然后把函数放进去),但是由于这个函数是和进程相关,为了方便理解,就放进sysproc.c里面去。

在vim中按shift+g直达文件末尾,添加如下代码:

image.png

sys_trace(void)函数解析:

  • 先看实验指导说明:

image.png 这里的variable需要自己在kernel/proc.h中指定,如下图所示: 也对应于上面那段函数的syscallnum。

image.png

  • image.png argint()用于获取这个mask,并且将其暂存入变量n后传给syscallnum。
  • 关于函数中的myproc(),我看过kernel/proc.c相应代码,和syscallnum没有直接的关系。

image.png 应该是在后续修改fork()相关代码,创建子进程的时候 有关联。


继续刚才的实验步骤哈~ - 在kernel/proc.c中的fork()函数中,添加如下一行:

image.png 使得trace mask能从父进程传到子进程。

  • 打印trace结果:修改kernel/syscall.c内容(有几处需要修改)

    • 添加和trace相关的extern:

image.png 我的理解是,要告知内核,有这么个外部函数会被调用(备案的意思)

  • 相应的,下面这个也得增加一行:

image.png

  • 修改syscall()函数的内容,使其能打印trace的结果:

image.png (就是在原来的基础上多加了个if)

  • 函数的syscall_name是自己设的一个映射表,可以是数组,也可以采用上面用到的形式(听说c++语法不支持后者),这里采用的是后者。 在sys_call函数之前添加如下代码: image.png

至此,所有的处理工作就完成了。

编译运行

make qemu

附上一张效果对比图:

image.png

一个编译通过但无法通过评测的解决方案:

在评测的时候遇到了一点问题:

image.png

但我确信不是我的问题,因为我在自己编译运行并输入

trace 2 usertests forkforkfork

的时候最后是出现了 ALL TESTS PASSED的。

这说明可能是运行时间太长,导致评测被强行终止了。

所以我修改了评测文件的超时限制,如下图所示:

image.png 打开gradelib.py文件,查找timeout关键词 (vim操作回顾:在normal模式下输入/后接你想查找的关键词,回车确定,之后再按n表示查找下一个,N表示查找上一个)

将timeout从30修改到更大的数值,我直接改成100了。

之后就舒服了:

image.png


sysinfo

已有条件

和trace一样,在user下已经有了一个sysinfotest.c的文件。 我们需要让内核感知到它的存在,让系统能顺利调用它。

命令说明

程序有一个参数,是一个指向struct sysinfo的指针。

需要完成的工作:

  • Makefile中添加$U/_sysinfotest字段
  • 仿照trace的做法在相应文件添加字段。
  • 在user/user.h中声明原型前,还要先声明结构体,也就是总共添加这两行。
 struct sysinfo;
 int sysinfo(struct sysinfo *);

以上步骤完成后,程序还未能进入内核态。

  • sysinfo需要复制一份struct sysinfo回内核态。可参照kernel/sysfile.c中的sys_fstat()和kernel/file.c中的filestat(),看看它们是怎么用copyout()的。 (笔者注:是这么用的)

image.png

举一反三,可在kernel/sysproc.c中添加如下代码:

image.png

在kernel/sysproc.c头部记得include "sysinfo.h"

这个文件也是系统准备好了的,打开看到长这样,已经为我们定义好了freemem和nproc image.png

  • 在kernel/kalloc.c中添加函数,freemem表示可用内存的字节数。

image.png

  • 在kernel/proc.c中添加函数,nproc表示状态为UNUSED(未被使用)的字节数。

image.png

编译时 无法识别proc.c中的freememsize()和nproc_active()的解决方案:

在kernel/defs.h 中加入对这两个函数的声明。 如图所示:

image.png

image.png

这个defs.h定义了一些内模块接口。

运行结果:

make qemu:

image.png 评测:

image.png

总结:

  • 这个lab让我对操作系统区分内核态、用户态,以及它们的交互过程有了更深的理解。
  • 我也从实际代码层面加深了对程序在系统中的调用过程的理解。

附录:一些概念笔记

以下是我在阅读xv6相关部分使用说明的时候的一些概念摘录: 书籍链接:pdos.csail.mit.edu/6.S081/2021…

管道相比临时文件的优势:

  • echo hello world | wc 等价于 echo hello world >/tmp/xyz; wc </tmp/xyz ,但是前者可以被自动清除,后者,tmp下的文件虽然最终会被删除,但是没那么及时
  • 管道可以传输任意长的数据流,而文件重定向需要硬盘上有足够多的空余空间去存储数据。
  • 管道允许并行执行流水线,而文件的方法需要等一个进程完成后才能开始第二个进程。
  • 进程间通信方面,管道比临时文件更高效。

操作系统应有的三大特点:

  • 多路复用
  • 隔离性
  • 交互性

用户模式、超级模式、机器模式

内核组织

整核(monolithic kernel)

整个操作系统都置于内核之中 缺点:错误是致命的,任何错误都会引起整个操作系统崩溃 事实上很多Linux系统都是这种设计

微核(microkernel):

最小化以超级模式运行的数量,并在用户态中执行大部分操作系统功能(区分user space和kernel space正式这种设计)

image.png

实验相关

image.png

图:进程虚拟地址空间的层次划分