zoukankan      html  css  js  c++  java
  • mit6.828 Homewoek 2:shell

    Homewoek 2:shell

    下载 6.828 shell 并阅读代码。shell 包含两个主要部分:解析shell命令和实现。

    将下列指令复制到 t.sh

    ls > y
    cat < y | sort | uniq | wc > y1
    cat y1
    rm y1
    ls |  sort | uniq | wc
    rm y
    

    编译 sh.c ,并执行指令:

    此执行将打印错误消息,因为尚未实现上述功能。在本任务的其余部分中,将实现这些功能。

    实现命令

    补充 runcmd 中空缺的代码。使用 man 3 exec 查看 execv 手册。

    1. int execv (const char * path, char * const argv[]); :用来执行参数 path 字符串所代表的文件路径, 与 execl() 不同的地方在于 execv() 只需两个参数, 第二个参数利用数组指针来传递给执行文件。

    该函数如果执行成功,则不会返回;若执行出错,会返回 -1 ,具体的错误代码可以通过全局变量 errno 查看,还可以通过 stderr 得到具体的错误描述字符串。

    1. int chdir(const char *path); :函数应使path参数指向的路径名所命名的目录成为当前工作目录;也就是说,路径的起点搜索不是以 “ /” 开头的路径名。转换成功返回 0 ;否则返回 -1 且当前目录不变。

    来自https://blog.csdn.net/a747979985/article/details/95094094

    本次 shell 作业执行可执行文件主要是通过该函数来实现。

    这次作业真的让我一头雾水,参考了如下博客,对 shell 大概的执行逻辑与代码实现有了头绪。

    1. Implement a Shell by yourself -- MIT xv6 shell :这篇博客分析了实现的过程,对从整体去把握 shell 的运行流程很有帮助。其次,他的实现代码运用了 access() 函数来检查权限,代码很简洁,但是不易理解。
    2. 【xv6学习之HW1】shell :这篇博文对上述博文以及代码进行了进一步解析,讲解了 access() 函数以及权限判断各个定义,有助于去理解上一篇博客的代码。

    这是我的代码 https://github.com/a74731248/journal-of-mit-6.828/blob/master/homework/HW1/sh.c 。我在代码中把自己的理解作为注释加了上去,挺详细的,希望对大家有帮助。

    实现部分

    execcmd

        // 寻找可执行文件
        if(execv(ecmd->argv[0], ecmd->argv) == -1) 
        {
          chdir("/bin/");
          if(execv(ecmd->argv[0], ecmd->argv) == -1) 
          {
            chdir("/usr/bin/");
            execv(ecmd->argv[0], ecmd->argv);
          }
        }
        fprintf(stderr, "exec %s failed.
    ", ecmd->argv[0]);
        _exit(0);
    

    redircmd

        close(rcmd->fd);
        if(open(rcmd->file, rcmd->flags, S_IRUSR | S_IWUSR) == -1)
        {
          fprintf(stderr, "file %s can't find
    ", rcmd->file);
          _exit(0);
        }
    

    pipecmd

        if(pipe(p) < 0)
        {
          fprintf(stderr, "call syscall pipe() failed in line %d
    ", __LINE__);
          _exit(0);
        }
        if(fork1() == 0)
        {
          // 子进程输出绑定到管道的 fd1
          close(1);   
          dup(p[1]);    
    
          close(p[0]);
          close(p[1]);
    
          // 执行管道左侧的命令
          runcmd(pcmd->left);
        }
    
        if(fork1() == 0)
        {
          // 绑定管道的 fd0 到子进程的输入
          close(0);
          dup(p[0]);
    
          close(p[0]);
          close(p[1]);
    
          // 运行管道右侧命令
          runcmd(pcmd->right);
        }
    
        close(p[0]);
        close(p[1]);
    
        // 这里我不太理解
        wait(&r);
        wait(&r);
    

    问题记录

    记录一下自己实现过程中的理解以及疑问(管道通信部分):

    理解:

    管道左侧的程序需用使用子进程1去执行,它需要从标准输入中读取参数,然后将程序运行结果输出到管道中去,故需要关闭子进程1的 fd1 ,然后将管道的写端口(p[1]) 拷贝到 fd1 中,从而实现子进程1输出到管道中。

    管道右侧的程序需要用子进程2去执行,它需要从管道中读取参数,故关闭子进程2的 fd0 ,然后将管道的读端口(p[0])拷贝到 fd0 中,从而实现子进程2从管道中读取参数。

    疑问:

    为什么每次都需要关闭管道的描述符(即 close(p[0]); close(p[1]);)?

    是因为管道对应的描述符已经重定向了,避免管道的内容受到其他进程的影响吗?

    总结

    1. 了解了 fork、dup、close、pipe 等系统调用的使用。
    2. 了解了 shell 是如何实现输入命令的分解。
    3. 利用 execv 实现了 exec 指令,使得 shell 能够加载并执行文件(命令)。
    4. 大致理解了进程之间的管道通信机制,通过文件描述符拷贝实现进程输入输出重定向。
    5. 实现了进程之间的管道通信。

    参考

    Implement a Shell by yourself -- MIT xv6 shell

    MIT6.828学习之homework2:shell

    【xv6学习之HW1】shell

  • 相关阅读:
    2019中山纪念中学夏令营-Day19 数论初步【GCD(最大公约数),素数相关】
    2019中山纪念中学夏令营-Day14 图论初步【dijkstra算法求最短路】
    2019中山纪念中学夏令营-Day12[JZOJ]
    2019中山纪念中学夏令营-Day9[JZOJ](第六次模拟赛)
    2019中山纪念中学夏令营-Day4[JZOJ]
    2019中山纪念中学夏令营-Day2[JZOJ]
    2019中山纪念中学夏令营-Day1[JZOJ]
    CCPC2019江西省赛-Problem G.Traffic
    T137223 节能主义
    T137226 彩虹海
  • 原文地址:https://www.cnblogs.com/joe-w/p/12613870.html
Copyright © 2011-2022 走看看