动手做前请先看完 xv6 book 的第 0 章
这个实验直接在支持 Unix API 的机器上做即可,比如 linux、MacOS、WSL(Windows Subsystem for Linux)
proxychains wget https://pdos.csail.mit.edu/6.828/2018/homework/sh.c
然后新建文件t.sh
,添加以下内容:
ls > y
cat < y | sort | uniq | wc > y1
cat y1
rm y1
ls | sort | uniq | wc
rm y
然后运行以下命令:
gcc ./sh.c && ./a.out < t.sh
会打印错误信息,因为有几个功能还没实现:
Executing simple commands
用man
命令可以查看函数的手册,man 2 function_name
查看系统调用(内核提供的函数),man 3 function_name
查看库调用(程序库中的函数)。或者可以去 die.net、man7.org
实现执行命令的功能:
case ' ':
ecmd = (struct execcmd*)cmd;
if (ecmd->argv[0] == 0)
_exit(0);
// execv出错时才会返回,然后继续执行接下来的程序
execv(ecmd->argv[0], ecmd->argv);
const char* binPath = "/bin/";
int pathLen = strlen(binPath) + strlen(ecmd->argv[0]);
// abs_path为绝对路径
char* abs_path = (char*)malloc((pathLen + 1) * sizeof(char));
strcpy(abs_path, binPath);
strcat(abs_path, ecmd->argv[0]);
execv(abs_path, ecmd->argv);
fprintf(stderr, "%s: Command not found\n", ecmd->argv[0]);
break;
I/O redirection
xv6 book的第十页:
By convention, a process reads from file descriptor 0 (standard input), writes output to file descriptor 1 (standard output), and writes error messages to file descriptor 2 (standard error).
The close system call releases a file descriptor, making it free for reuse by a future open, pipe, or dup system call (see below). A newly allocated file descriptor is always the lowest-numbered unused descriptor of the current process.
实现I/O重定向的功能:
case '>':
case '<':
rcmd = (struct redircmd*)cmd;
// 关闭标准输入或输出
close(rcmd->fd);
// rcmd->fd会重定向至open的文件rcmd->file
if (open(rcmd->file, rcmd->flags) < 0) {
fprintf(stderr, "Unable to open file: %s\n", rcmd->file);
exit(1);
}
runcmd(rcmd->cmd);
break;
Implement pipes
阅读xv6 book的第13页。管道的实现还可参考 一个小小的 Shell 管道符,内部实现可真不简单!
实现管道:
case '|':
pcmd = (struct pipecmd*)cmd;
pipe(p); // 创建管道
if (fork1() == 0) {
// 在子进程中
// 将标准输出重定向至 p[1]
close(1);
dup(p[1]);
// 执行管道符左边的命令
runcmd(pcmd->left);
} else {
// 父进程需要接收子进程输出的数据,所以关闭父进程的p[1]
close(p[1]);
// 在父进程中等待子进程输出完毕
wait(&r);
// 将标准输入重定向至 p[0]
close(0);
dup(p[0]);
// 执行管道符右边的命令
runcmd(pcmd->right);
}
break;
结果
╰─$ gcc ./sh.c && ./a.out < t.sh
11 11 86
11 11 86