1
实验目的
为 xv6 系统实现 UNIX 的 sleep 程序。你的 sleep 程序应该使当前进程暂停相应的时钟周期数,时钟周期数由用户指定。例如执行 sleep 100 ,则当前进程暂停,等待 100 个时钟周期后才继续执行。
2
实验要求及提示
- 如果系统没有安装
vim
的请先使用命令 sudo apt install vim
安装 vim
编辑器。 - 在开始实现程序前,阅读配套教材的第一章,其内容与实验内容息息相关。
- 实现的程序应该命名为
sleep.c
并最后放入 user
目录下。 - 可以查看
user
目录下的其他程序(如echo.c、grep.c和rm.c),以它们为参考,了解如何获取和传递给程序相应的命令行参数。 - 如果用户传入参数有误,即没有传入参数或者传入多个参数,程序应该能打印出错误信息。
- C 语言中的
atoi
函数可以将字符串转换为整数类型,在 xv6 中也已经定义了相同功能的函数。可以参考 user/ulib.c
或者参考机械工业出版社的《C程序设计语言(第2版·新版)》附录B.5。 - 使用系统调用
sleep
。 - 确保
main
函数调用 exit()
以退出程序。 - 将你的
sleep
程序添加到 Makefile
的 UPROGS
中。只有这步完成后, make qemu
才能编译你写的程序。 - 完成上述步骤后,运行
make qemu
编译运行 xv6
,输入 sleep 10
进行测试,如果 shell
隔了一段时间后才出现命令提示符,则证明你的结果是正确的,可以退出 xv6
运行 ./grade-lab-util sleep
或者 make GRADEFLAGS=sleep grade
进行单元测试。
3
实验思路
- 参考
user
目录下的其他程序,先把头文件引入,即 kernel/types.h
声明类型的头文件和 user/user.h
声明系统调用函数和 ulib.c
中函数的头文件。 - 编写
main(int argc,char* argv[])
函数。其中,参数 argc
是命令行总参数的个数,参数 argv[]
是 argc
个参数,其中第 0 个参数是程序的全名,其他的参数是命令行后面跟的用户输入的参数。 - 首先,编写判断用户输入的参数是否正确的代码部分。只要判断命令行参数不等于 2 个,就可以知道用户输入的参数有误,就可以打印错误信息。但我们要如何让命令行打印出错误信息呢?我们可以参考
user/echo.c
,其中可以看到程序使用了 write()
函数。 write(int fd, char *buf, int n)
函数是一个系统调用,参数 fd
是文件描述符,0 表示标准输入,1 表示标准输出,2 表示标准错误。参数 buf
是程序中存放写的数据的字符数组。参数 n 是要传输的字节数,调用 user/ulib.c
的 strlen()
函数就可以获取字符串长度字节数。通过调用这个函数,就能解决输出错误信息的问题啦。认真看了提示给出的所有文件代码你可能会发现,像在 user/grep.c
打印信息调用的是 fprintf()
函数,当然,在这里使用也没有问题,毕竟 fprintf()
函数最后还是通过系统调用 write()
。最后系统调用 exit(1)
函数使程序退出。按照惯例,返回值 0 表示一切正常,而非 0 则表示异常。 - 接下来获取命令行给出的时钟周期,由于命令行接收的是字符串类型,所以先使用
atoi()
函数把字符串型参数转换为整型。 - 调用系统调用
sleep
函数,传入整型参数,使计算机程序(进程、任务或线程)进入休眠。 - 最后调用系统调用
exit(0)
使程序正常退出。 - 在
Makefile
文件中添加配置,照猫画虎,在 UPROGS
项中最后一行添加 $U/_sleep\
,最后这项配置如下。
UPROGS=\
$U/_cat\
$U/_echo\
$U/_forktest\
$U/_grep\
$U/_init\
$U/_kill\
$U/_ln\
$U/_ls\
$U/_mkdir\
$U/_rm\
$U/_sh\
$U/_stressfs\
$U/_usertests\
$U/_grind\
$U/_wc\
$U/_zombie\
$U/_sleep\
4
实验代码
// Lab Xv6 and Unix utilities
// sleep.c
// 引入声明类型的头文件
#include "kernel/types.h"
// 引入声明系统调用和 user/ulib.c 中其他函数的头文件
#include "user/user.h"
// int main(int argc,char* argv[])
// argc 是命令行总的参数个数
// argv[] 是 argc 个参数,其中第 0 个参数是程序的全名,以后的参数是命令行后面跟的用户输入的参数
int
main(int argc, char *argv[])
{
// 如果命令行参数不等于2个,则打印错误信息
if (argc != 2)
{
// 系统调用 write(int fd, char *buf, int n) 函数输出错误信息
// 参数 fd 是文件描述符,0 表示标准输入,1 表示标准输出,2 表示标准错误
// 参数 buf 是程序中存放写的数据的字符数组
// 参数 n 是要传输的字节数
// 所以这里调用 user/ulib.c 的 strlen() 函数获取字符串长度字节数
write(2, "Usage: sleep time\n", strlen("Usage: sleep time\n"));
// 当然这里也可以使用 user/printf.c 中的 fprintf(int fd, const char *fmt, ...) 函数进行格式化输出
// fprintf(2, "Usage: sleep time\n");
// 退出程序
exit(1);
}
// 把字符串型参数转换为整型
int time = atoi(argv[1]);
// 调用系统调用 sleep 函数,传入整型参数
sleep(time);
// 正常退出程序
exit(0);
}
5
实验结果
在 xv6 中输入命令后一切符合预期。
$ sleep 10
$ sleep 1 1
Usage: sleep time
$ sleep
Usage: sleep time
退出 xv6 运行单元测试。
./grade-lab-util sleep
提示:如果运行测试报错,先使用命令 vim grade-lab-util
修改 grade-lab-util 文件,把第一行改为 #!/usr/bin/env python3
通过测试样例。
make: 'kernel/kernel' is up to date.
== Test sleep, no arguments == sleep, no arguments: OK (0.9s)
== Test sleep, returns == sleep, returns: OK (1.4s)
== Test sleep, makes syscall == sleep, makes syscall: OK (0.9s)