MIT 6.S081 Fall 2020 Lab 2
System calls
学!
System call tracing
自己写一个系统调用, 能够追踪打印系统调用. 看到的时候我是蒙的, 这咋追踪. 后来看提示, 原来是修改 syscall()
函数. kernel/syscall.c
中的 syscall 函数长这样:
|
|
num 赋值为寄存器 a7 的值, 也就是系统调用号. 然后判断是否合法, 最后调用 syscallsnum. syscalls 是函数指针数组.
在这句话结束后, 打印信息就可以了.
首先加入一个系统调用:
- 在
kernel/syscall.h
中加入#define SYS_trace 22
- 在
kernel/syscall.c
中加入系统调用 sys_trace 的声明extern uint64 sys_trace(void);
; 在 syscalls 数组中加入[SYS_trace] sys_trace
的声明 - 在
user/user.h
中加入函数声明int trace(int);
- 在
user/usys.pl
中加入入口声明entry("trace");
一句话, 就是出现一堆系统调用的地方都对着抄加一个 trace 即可.
根据提示, 我们要把 mask 写到 proc 结构体中, 这样 fork 的时候就可以进行传递了. 于是在 kernel/proc.h
的 struct proc
最后加一个变量 int sys_trace_mask;
找到 kernel/proc.c
中的 fork 函数, 将子进程的 proc 结构体中的 sys_trace_mask
赋值为父进程的.
由于需要打印系统调用的名称, 所以我们还要创建一个字符串数组. 在 kernel/syscall.c
中写入如下内容:
|
|
这里用的就是系统调用号作下标.
把 syscall 函数改成这样:
|
|
就可以实现打印 mask 所指示的系统调用过程了. 由于 proc 是全局变量, 所以初始的 mask 是 0. 这个打印就不会执行.
最后差一个修改 mask, 也就是 sys_trace 的功能. 在 kernel/sysproc.c
中写入如下函数即可:
|
|
Sysinfo
写一个系统调用, 返回系统信息. kernel/sysinfo.h
如下:
|
|
先按照上一个任务的方法加入一个 sysinfo 系统调用. 这里在 kernel 文件夹下再写一个 sysinfo.c
来实现这个系统调用. 在 Makefile 的 OBJS
加入 $K/sysinfo.o \
.
sysinfo.c
如下:
|
|
需要注意头文件的顺序… 这还是第一次了解这玩意. 展开宏的时候是一个一个展开的, 所以如果有一个头文件中的 type 没有在之前的头文件中定义, 那么就会报错.
然后说一下 copyout. 由于系统调用已经陷入了内核态, 而传入的地址是某个程序的虚拟地址, 所以需要程序的页表来计算得到物理地址, 再进行写入 (这里是 copy 的方式). 千万不能认为传入的地址就是用户态的某一个变量的地址, 然后直接通过指针操作改内容.
你问我怎么知道要这样写? 答案是看 sys_fstat()
.
系统调用的问题解决了, 可以先随便赋值, 测试一下, 没问题再继续.
然后是写 freemem 和 proc_number.
在 kernel/kalloc.c
中加入如下代码:
|
|
通过翻阅 kalloc.c
之前的代码可以知道, kmem 这个结构里维护了一个空闲内存链表, 单位是页大小. 所以遍历累加即可.
在 kernel/proc.c
中加入如下代码:
|
|
通过翻阅之前的代码可以知道, proc 数组维护了所有的进程结构, 那么只需要遍历, 统计 state 不为 UNUSED 的数量就行. 当然更优秀的做法是直接用一个变量维护进程数.
Optional challenge exercises
trace syscall arguments
额外记录 syscall 的参数个数, 然后去翻寄存器就行. 不写了.
sysinfo load average
TODO. 连负载是啥我都不知道… 再说, 先学之后的, 以后回来补.