System call tracing (moderate)
题目
系统调用跟踪(中等难度)
在这个任务中,你需要添加一个系统调用跟踪功能,这可能有助于你在后续实验中进行调试。你将创建一个新的跟踪系统调用,用于控制跟踪功能。
该系统调用应接受一个参数,即一个整数“mask”,其位用于指定要跟踪的系统调用。
例如,要跟踪 fork 系统调用,程序可以调用 trace(1 << SYS_fork),其中 SYS_fork 是内核头文件 kernel/syscall.h 中的一个系统调用编号。
你需要修改 xv6 内核,以便在每个系统调用即将返回时打印一行信息,前提是该系统调用的编号在 mask 中被设置。
该行应包含进程 ID、系统调用的名称以及返回值;
你无需打印系统调用的参数。跟踪系统调用应为调用它的进程及其后续 fork 的子进程启用跟踪功能,但不应影响其他进程。
我们提供了一个用户级程序 trace,它可以以启用跟踪的方式运行另一个程序(请参见 user/trace.c)。完成后,你应该看到类似以下的输出:
复制
1 | $ trace 32 grep hello README |
在上面的第一个例子中,trace 调用了 grep,仅跟踪 read 系统调用。数字 32 是 1<<SYS_read。在第二个例子中,trace 在跟踪所有系统调用的情况下运行 grep;数字 2147483647 的低 31 位都被设置。在第三个例子中,程序未被跟踪,因此没有跟踪输出。在第四个例子中,usertests 中的 forkforkfork 测试的所有后代进程的 fork 系统调用都被跟踪。如果你的程序表现如上所示(尽管进程 ID 可能不同),则你的解决方案是正确的。
一些提示:
- 在 Makefile 中将
$U/_trace添加到UPROGS中。 - 运行
make qemu,你会看到编译器无法编译user/trace.c,因为用户空间的系统调用存根尚不存在:需要在user/user.h中添加系统调用的原型,在user/usys.pl中添加存根,并在kernel/syscall.h中添加一个系统调用编号。Makefile 调用了 Perl 脚本user/usys.pl,它生成user/usys.S,即实际的系统调用存根,这些存根使用 RISC-V 的ecall指令切换到内核。修复编译问题后,运行trace 32 grep hello README;它会失败,因为你尚未在内核中实现该系统调用。 - 在
kernel/sysproc.c中添加一个sys_trace()函数,用于通过在proc结构(请参见kernel/proc.h)中的一个新变量中记住其参数来实现新的系统调用。从用户空间检索系统调用参数的函数在kernel/syscall.c中,你可以在kernel/sysproc.c中看到这些函数的使用示例。 - 修改
fork()(请参见kernel/proc.c),以便将跟踪 mask 从父进程复制到子进程。 - 修改
kernel/syscall.c中的syscall()函数以打印跟踪输出。你需要添加一个系统调用名称的数组以便索引。
解题思考
sys_trace()函数,用于通过在proc结构(请参见kernel/proc.h)中的一个新变量中记住其参数来实现新的系统调用。- 修改
fork()(请参见kernel/proc.c),以便将跟踪 mask 从父进程复制到子进程。 - 修改
kernel/syscall.c中的syscall()函数以打印跟踪输出。你需要添加一个系统调用名称的数组以便索引。
我们通过trace来实现对于输入参数的处理,然后需要进入系统调用。
sys_trace就是系统调用函数。
fork实现将mask从父进程复制到子进程,也就是说proc中需要有一个来保存mask,同时mask是需要作为参数来获取的。
syscall来打印输出
一些发现
-
整个user部分的函数,都在user.h中声明
-
实际的系统调用存根,这些存根使用 RISC-V 的
ecall指令切换到内核。所以要创建用户空间的系统调用存根 -
从用户空间检索系统调用参数的函数在
kernel/syscall.c中从用户空间转到内核空间,需要通过特殊的函数来传递参数
系统信息(中等难度)
题目
在这个任务中,你需要添加一个系统调用 sysinfo,用于收集运行系统的相关信息。该系统调用接受一个参数:一个指向 struct sysinfo 的指针(请参见 kernel/sysinfo.h)。内核需要填充这个结构体的字段:freemem 字段应设置为可用内存的字节数,nproc 字段应设置为状态不是 UNUSED 的进程数量。我们提供了一个测试程序 sysinfotest;如果它打印出“sysinfotest: OK”,则表示你通过了这个任务。
一些提示:
-
在
Makefile中将$U/_sysinfotest添加到UPROGS中。 -
运行
make qemu;user/sysinfotest.c将无法编译。按照上一个任务中的相同步骤添加系统调用sysinfo。为了在user/user.h中声明sysinfo()的原型,你需要预先声明struct sysinfo的存在:c复制
1
2struct sysinfo;
int sysinfo(struct sysinfo *); -
修复编译问题后,运行
sysinfotest;它会失败,因为你尚未在内核中实现该系统调用。 -
sysinfo需要将一个struct sysinfo结构体拷贝回用户空间;可以参考sys_fstat()(位于kernel/sysfile.c)和filestat()(位于kernel/file.c),了解如何使用copyout()来实现这一点。 -
为了收集可用内存的数量,需要在
kernel/kalloc.c中添加一个函数。 -
为了收集进程的数量,需要在
kernel/proc.c中添加一个函数。
