FDU Operating System Lab2
薪火相传
因为中途才开始写博客,一些配置上的问题可能没讲清楚。目前只是自己做一个记录吧。
如果能帮到你理解这个lab就好啦
环境:WSL + xv6
LAB2:https://docs.qq.com/slide/DR2VtU3Fvb2hGWEN0
实验准备
切换到本次实验的环境分支下
1 | git commit -am lab0 |
随后按要求修改相应 kernel
与 user
文件夹下的文件,以任务1的 procnum
为例,其余同理。
在 kernel/syscall.h
中,添加一个宏定义
1 |
然后在 kernel/syscall.c
指定系统调用的主体函数,即第22号System call会调用 sys_procnum
这个指针指向的函数
1 | extern uint64 sys_procnum(void); |
在 kernel/sysproc.c
中添加具体实现的主体函数(在后面会介绍)
1 | uint64 |
在 user/usys.pl
中添加系统调用的存根
1 | entry("procnum"); |
具体而言,就是 Makefile
会调用这个 Perl
脚本,生成一段汇编码在 usys.S
中,这是 user.h
中定义的函数实际的实现,即调用的地方。
点进 usys.S
可以看到诸如 li a7, SYS_fork
这种,还记得之前把 SYS_fork
宏定义为了数字吧,就是在这里派用场。
为了能够让用户程序访问到 procnum
系统调用,我们需要在 user/user.h
中声明该调用:
1 | // system calls |
你可能会注意到这里有一个参数列表上的不匹配,会发现所有 kernel/sysproc.c
中的系统调用实现形参列表都是void。
这是因为 procnum()
这样的函数是用户级别的函数,而 sys_procnum()
是内核级别的函数,用户空间和内核空间有不同的数据访问规则和隔离。
前者需要将参数在用户空间打包成适当的数据结构,后者会通过辅助函数 argaddr()
或 argint()
等获取用户空间的参数到内核空间,并在其中进行合法与安全性检查,确保不会引发内核的错误。
可以看看其它system call的实现方法,比如wait(),是怎么获取参数列表的
1 | int argaddr(int n, uint64 *addr); |
任务1:process counting
- 系统调用功能 procnum:统计系统总进程数
在user文件夹下添加检验程序 procnum.c
1 |
|
在makefile中添加编译项
1 | UPROGS=\ |
完成 kernel/sysproc.c
中函数的具体实现。我整体是对着 sys_wait()
函数学的。
先用 argaddr()
获取传过来的参数,是变量num的地址
1 | uint64 |
和 sys_wait()
函数一样,我们把实现放在了 kernel/proc.c
中,记得在 defs.h
中先定义函数
因为内核空间和用户空间存在隔离,所以只能使用 copyout()
这种辅助函数实现修改用户空间的变量,而不能直接拿指针去操作。
1 | // Count the total number of processes in the system. |
其余几项任务也差不多,照猫画虎。
下面的部分是我很久之后参照着实验报告补的,可能并不全面,仅供参考
任务2:Free Memory Counting
要统计空闲内存块的总字节数数,可以直接看在 kalloc.c 中的 kmem.freelist,遍历可以获得空闲“页数”,最后记得答案乘以 PGSIZE
为了操作定义在 kalloc.c 中的 kmem,可以在 kalloc.c 中添加一个函数
1 | int |
最后在系统调用时调用 get_freemem()
这个函数,和实验 1 一样我还是把实现放在了proc.c 中。记得把这些函数都在头文件 defs.h 中定义一下。
sysproc.c :
1 | uint64 |
proc.c :
把答案写回 num
1 | int |
任务3:System call tracing
这次 trace.c 已经给好了在 user 文件夹里
Trace 的用法不太一样,它传入一个参数 mask,每一个二进制位表示跟踪某一个system call。若某一 system call 处于被跟踪状态,则执行它时会输出相关信息
跟着 https://pdos.csail.mit.edu/6.S081/2022/labs/syscall.html 中的提示,我们还是先同样在 sysproc.c 中添
加系统调用 sys_trace(),它负责接收参数 mask 到当前进程。这要求我们在 proc.h 中,对
每一个进程添加一个参数 mask。
1 | // Per-process state |
随后,我们需要修改 fork,让每一个子进程也继承到 mask。
1 | int |
最后,在 syscall.c 中修改 void syscall(void),每次调用时检查所有的 system call,若对应 mask 位=1,则打印相关信息,格式和要求中相同。
1 | void |