之前我们已经配置好了实验的环境,并将实验的代码库克隆到了本地,在第一个实验的第一个小题中,我们需要编译代码库中的源码并在qemu中启动Xv6系统
先进入克隆仓库文件夹
$ cd xv6-labs-2021
然后切换分支为util
$ git checkout util
然后编译并在qemu中运行Xv6
$ make qemu
出现以上内容即成功启动并且进入Xv6系统,可以使用 ls 指令列出根目录下文件,然后试着执行一些内置的程序,例如执行 cat README ,即可查看 README 文件的内容。若要结束运行 xv6 并终止 qemu,需在键盘上同时下 Ctrl+A 键,然后按下 X 键,即可终止 qemu 的运行。
在该实验中,我们需要实现 Unix 中经典的实用工具 sleep 。sleep 工具的作用是等待给定的 tick 数并退出(tick 是指两次次时钟中断的间隔时间)。根据要求,我们需要将实现的源码放置在 ./user/sleep.c 。
在开始写代码之前,我们可以先查看其它实用工具的源码,用以习惯其书写的方式。例如,打开源码 ./user/in.c :
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int
main(int argc, char *argv[])
{
if(argc != 3){
fprintf(2, "Usage: ln old new\\n");
exit(1);
}
if(link(argv[1], argv[2]) < 0)
fprintf(2, "link %s %s: failed\\n", argv[1], argv[2]);
exit(0);
}
注意到该源码与 C 语言源码大致相同,但也存在几个明显的区别:
include 部分引入的头文件不是我们常见的标准的 C 语言库的头文件,而是 Xv6 内核的各类头文件(Xv6还未实现标准C语言库)
<aside> 💡 types.h、stat.h和user.h的作用?
程序退出时使用的是 exit(0); 而非一般的 return 0;
<aside> 💡 Xv6 中为什么使用 exit 系统调用
实际上,在用 gcc 为 linux 构建程序时,工具链会添加所需的 exit 调用,真正的程序执行并非从 main 函数开始,而是从工具链添加的 start 函数开始的,如下:
void start(void) {
/* get argc, argv, env */
int r = main(argc, argv, envp); /* << start calls the actual main */
exit(r);
}
在 xv6 中,由于我们没有这样配置工具链,故而需要手动添加 exit(0); 语句。该系统调用的作用是按照既定的步骤结束一个进程(关闭文件,释放资源,唤醒等待的父进程,修改自身状态等)
</aside>
熟悉了以上写法以后,我们可以编写自己的用户程序,其中 ./user/user.h下有我们可以使用的一些系统调用和函数的声明,在其中发现有以下系统调用:
int sleep(int);