MIT6.828 Fall2018 笔记 - Homework 2: shell

Homework: shell

动手做前请先看完 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.netman7.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

猜你喜欢

转载自www.cnblogs.com/zsmumu/p/12470273.html