Lab2 syscall


System call tracing (moderate)

题目

系统调用跟踪(中等难度)

在这个任务中,你需要添加一个系统调用跟踪功能,这可能有助于你在后续实验中进行调试。你将创建一个新的跟踪系统调用,用于控制跟踪功能。

该系统调用应接受一个参数,即一个整数“mask”,其位用于指定要跟踪的系统调用。

例如,要跟踪 fork 系统调用,程序可以调用 trace(1 << SYS_fork),其中 SYS_fork 是内核头文件 kernel/syscall.h 中的一个系统调用编号。

你需要修改 xv6 内核,以便在每个系统调用即将返回时打印一行信息,前提是该系统调用的编号在 mask 中被设置。

该行应包含进程 ID、系统调用的名称以及返回值;

你无需打印系统调用的参数。跟踪系统调用应为调用它的进程及其后续 fork 的子进程启用跟踪功能,但不应影响其他进程。

我们提供了一个用户级程序 trace,它可以以启用跟踪的方式运行另一个程序(请参见 user/trace.c)。完成后,你应该看到类似以下的输出:

复制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
$ trace 32 grep hello README  
3: syscall read -> 1023
3: syscall read -> 966
3: syscall read -> 70
3: syscall read -> 0
$
$ trace 2147483647 grep hello README
4: syscall trace -> 0
4: syscall exec -> 3
4: syscall open -> 3
4: syscall read -> 1023
4: syscall read -> 966
4: syscall read -> 70
4: syscall read -> 0
4: syscall close -> 0
$
$ grep hello README
$
$ trace 2 usertests forkforkfork
usertests starting
test forkforkfork: 407: syscall fork -> 408
408: syscall fork -> 409
409: syscall fork -> 410
410: syscall fork -> 411
409: syscall fork -> 412
410: syscall fork -> 413
409: syscall fork -> 414
411: syscall fork -> 415
...
$

在上面的第一个例子中,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() 函数以打印跟踪输出。你需要添加一个系统调用名称的数组以便索引。

解题思考

  1. sys_trace() 函数,用于通过在 proc 结构(请参见 kernel/proc.h)中的一个新变量中记住其参数来实现新的系统调用
  2. 修改 fork()(请参见 kernel/proc.c),以便将跟踪 mask 从父进程复制到子进程。
  3. 修改 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”,则表示你通过了这个任务。

一些提示:

  1. Makefile 中将 $U/_sysinfotest 添加到 UPROGS 中。

  2. 运行 make qemuuser/sysinfotest.c 将无法编译。按照上一个任务中的相同步骤添加系统调用 sysinfo。为了在 user/user.h 中声明 sysinfo() 的原型,你需要预先声明 struct sysinfo 的存在:

    c复制

    1
    2
    struct sysinfo;
    int sysinfo(struct sysinfo *);
  3. 修复编译问题后,运行 sysinfotest;它会失败,因为你尚未在内核中实现该系统调用。

  4. sysinfo 需要将一个 struct sysinfo 结构体拷贝回用户空间;可以参考 sys_fstat()(位于 kernel/sysfile.c)和 filestat()(位于 kernel/file.c),了解如何使用 copyout() 来实现这一点。

  5. 为了收集可用内存的数量,需要在 kernel/kalloc.c 中添加一个函数。

  6. 为了收集进程的数量,需要在 kernel/proc.c 中添加一个函数。

sysinfo分析


文章作者: MIKA
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 MIKA !
  目录