1
实验要求
编写一个简单的 UNIX xargs
程序,从标准输入中读取行并为每一行运行一个命令,将该行作为命令的参数提供。你的解决方案应该放在 user/xargs.c
中。
2
实验提示
- 使用
fork()
和 exec()
在每一行输入上调用命令。在 parent
中使用 wait()
等待 child
完成命令。 - 要读取单个输入行,请一次读取一个字符,直到出现换行符(
'\n'
)。 -
kernel/param.h
声明了 MAXARG
,如果你需要声明一个 argv
数组,这可能很有用。 - 将程序添加到
Makefile
的 UPROGS
中。 - 文件系统的变化在
qemu
的运行中持续存在。使用 make clean
然后再 make qemu
让一个干净的文件系统运行。
3
实验思路
- 根据提示,我们需要调用
fork()
创建子进程,和调用 exec()
执行命令。我们知道要从标准输入中读取行并为每行运行一个命令,且将该行作为命令的参数。即把输入的字符放到命令后面,然后调用 exec()
。我们可以依次处理每行,根据空格符和换行符分割参数,调用子进程执行命令。 - 首先,我们定义一个字符数组,作为子进程的参数列表,其大小设置为
kernel/param.h
中定义的 MAXARG
,用于存放子进程要执行的参数。而后,建立一个索引便于后面追加参数,并循环拷贝一份命令行参数,即拷贝 xargs
后面跟的参数。创建缓冲区,用于存放从管道读出的数据。 - 然后,循环读取管道中的数据,放入缓冲区,建立一个新的临时缓冲区存放追加的参数。把临时缓冲区追加到子进程参数列表后面。并循环获取缓冲区字符,当该字符不是换行符时,直接给临时缓冲区;否则创建一个子进程,把执行的命令和参数列表传入
exec()
函数中,执行命令。当然,这里一定要注意,父进程一定得等待子进程执行完毕。
4
实验代码
// Lab Xv6 and Unix utilities
// xargs.c
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/param.h"
#define MAXN 1024
int
main(int argc, char *argv[])
{
// 如果参数个数小于 2
if (argc < 2) {
// 打印参数错误提示
fprintf(2, "usage: xargs command\n");
// 异常退出
exit(1);
}
// 存放子进程 exec 的参数
char * argvs[MAXARG];
// 索引
int index = 0;
// 略去 xargs ,用来保存命令行参数
for (int i = 1; i < argc; ++i) {
argvs[index++] = argv[i];
}
// 缓冲区存放从管道读出的数据
char buf[MAXN] = {"\0"};
int n;
// 0 代表的是管道的 0,也就是从管道循环读取数据
while((n = read(0, buf, MAXN)) > 0 ) {
// 临时缓冲区存放追加的参数
char temp[MAXN] = {"\0"};
// xargs 命令的参数后面再追加参数
argvs[index] = temp;
// 内循环获取追加的参数并创建子进程执行命令
for(int i = 0; i < strlen(buf); ++i) {
// 读取单个输入行,当遇到换行符时,创建子线程
if(buf[i] == '\n') {
// 创建子线程执行命令
if (fork() == 0) {
exec(argv[1], argvs);
}
// 等待子线程执行完毕
wait(0);
} else {
// 否则,读取管道的输出作为输入
temp[i] = buf[i];
}
}
}
// 正常退出
exit(0);
}
5
实验结果
在 Makefile
文件中, UPROGS
项追加一行 $U/_xargs\
。编译并运行 xv6 进行测试。
$ make clean
...
$ make qemu
...
init: starting sh
$ echo hello too | xargs echo bye
bye hello too
退出 xv6 ,运行单元测试检查结果是否正确。
./grade-lab-util xargs
通过测试样例。
make: 'kernel/kernel' is up to date.
== Test xargs == xargs: OK (1.8s)