在上一个实验中,你使用系统调用编写了几个工具。在本实验中,你将向xv6添加一些新的系统调用,这将帮助你理解它们是如何工作的,并让你了解xv6内核的一些内部机制。你将在后续实验中添加更多的系统调用。
在开始编码之前,阅读xv6书的第2章,以及第4章的4.3和4.4节,以及相关源文件:
要开始实验,切换到syscall分支:
$ git fetch $ git checkout syscall $ make clean
如果你运行make grade,你会看到评分程序无法执行trace和sysinfotest。你的任务是添加必要的系统调用和stub使它们能够工作。
我们提供了一个trace用户级程序,它在启用跟踪的情况下运行另一个程序(参见user/trace.c)。完成后,你应该看到如下输出:
$ 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并跟踪所有系统调用;2147583647的低31位全部被设置。在第三个例子中,程序没有被跟踪,所以没有打印跟踪输出。在第四个例子中,usertests中forkforkfork测试的所有后代的fork系统调用正在被跟踪。如果你的程序行为如上所示(尽管进程ID可能不同),则你的解决方案是正确的。
一些提示:
将$U/_trace添加到Makefile中的UPROGS
运行make qemu,你会看到编译器无法编译user/trace.c,因为系统调用的用户空间stub还不存在:在user/user.h中添加系统调用的原型,在user/usys.pl中添加stub,在kernel/syscall.h中添加系统调用号。Makefile调用perl脚本user/usys.pl,该脚本生成user/usys.S,即实际的系统调用stub,它使用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()函数以打印跟踪输出。你需要添加一个系统调用名称数组来进行索引。
一些提示:
将$U/_sysinfotest添加到Makefile中的UPROGS
运行make qemu;user/sysinfotest.c将编译失败。添加sysinfo系统调用,按照与前一个作业相同的步骤。要在user/user.h中声明sysinfo()的原型,你需要预先声明struct sysinfo的存在:
struct 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中添加一个函数
>
实验到此完成。确保你通过了所有make grade测试。如果本实验有问题,别忘了在answers-lab-name.txt中写下你的答案。提交你的更改(包括添加answers-lab-name.txt),然后在实验目录中输入make handin来提交你的实验。
创建一个新文件time.txt,在其中放入一个整数,表示你花在本实验上的小时数。别忘了git add和git commit该文件。
提交实验
花费时间
提交
你将使用提交网站提交你的作业。在提交任何作业或实验之前,你需要从提交网站申请一次API密钥。
在提交实验的最终更改之后,输入make handin来提交你的实验。
$ git commit -am "ready to submit my lab"
[util c2e3c8b] ready to submit my lab
2 files changed, 18 insertions(+), 2 deletions(-)
$ make handin
tar: Removing leading `/' from member names
Get an API key for yourself by visiting https://6828.scripts.mit.edu/2020/handin.py/
Please enter your API key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 79258 100 239 100 79019 853 275k --:--:-- --:--:-- --:--:-- 276k
$
make handin会将你的API密钥存储在myapi.key中。如果你需要更改API密钥,只需删除此文件,然后让make handin重新生成(myapi.key不能包含换行符)。
如果你运行make handin时有未提交的更改或未跟踪的文件,你将看到类似以下的输出:
M hello.c ?? bar.c ?? foo.pyc Untracked files will not be handed in. Continue? [y/N]检查上述行,确保你的实验解决方案所需的所有文件都被跟踪,即不在以??开头的行中列出。你可以使用git add filename让git跟踪你创建的新文件。
如果make handin不能正常工作,请尝试修复curl或Git命令的问题。或者你可以运行make tarball。这将为你制作一个tar文件,然后你可以通过我们的web界面上传。