zoukankan      html  css  js  c++  java
  • MIT 6.828-operating system engineering homework: shell

    这是6.828第一节的一个小作业,就是实现一个shell,大部分的源码都给了,但是编译一下发现很多bug和warning,所以需要读懂源代码然后自己改。

    这次作业的重点在于理解和体会shell是怎么实现和构建的,尤其要体会的是unix系统中管道和重定向

    关于shell实现的一些基本原理请看xv6-book chapter0A

    shell需要保证在任何时候都有三个打开的文件描述符,他们是控制台的默认文件描述符

    惯例是从文件描述符0读入,从文件描述符1输出(标准输出),从文件描述符2输出错误

    image

    首先是gecmd读取命令

    命令有5类:

    1. 执行命令 符号: “ ”
    2. 重定向命令 > 或者 <
    3. 列表命令,也就是多个命令 符号是分号 ;
    4. 管道命令,需要先建立管道 符号是 |
    5. 返回命令(这个不知道是干嘛的),符号是 &

    整个函数的核心函数有两个,一个是parsecmd,一个是runcmd

    当读入命令的时候,首先进行解析

    parsecmd的核心函数是parseline

    这个函数是递归的,执行流程:

    1. 首先执行parsepipe,检测是否有管道命令,有的话建立管道连接
    2. 检测命令中是否有&,也就是返回命令,有的话用parsepipe的返回值生成一个新的backcmd
    3. 检测是否有;,也就是是否有多条命令分别要执行,有的话,递归调用parseline,将所有的命令分别解析之后连接起来

    下面是代码

    clip_image002

    上面的Listcmd函数其实非常简单,就是将多个命令连接成一个链表

    clip_image003

    backcmd函数更简单,就是生成一个指向新命令的指针

    clip_image004

    而真正的核心函数是parsepipe

    parsepipe的函数写的很简短,其中第一个parseexec函数是根据输入命令中第一条可执行命令生成对应结构体

    然后针对剩下的部分递归调用parsepipe函数,递归的生成命令

    clip_image005

    其实pipecmd和listcmd结构体是一样的

    clip_image006

    区别在于实际执行的时候

    如果是listcmd,那就是一条一条的执行,执行完上一条执行下一条

    下面的递归写法就是这样的

    clip_image007

    管道命令

    而pipecmd就不能这样写,因为是要生成管道而且要将管道绑定到某些端口的输入输出的

    首先生成一个管道

    然后fork一个子进程出来,将原本的输出文件描述符关闭之后,将输出文件描述符绑定到管道的输出端上,然后执行左边的命令

    对于右边的命令是一样的,这样就可以将输入输出组合起来了

    clip_image008

    这里需要注意的是为什么在两个子进程内部都需要把管道描述符关掉

    clip_image009

    重定位命令是比较有意思的

    看一下重定位命令的执行过程

    1. 调用execcmd,生成一个execcmd的结构体
    2. 然后调用parseredirs函数,将之前的execcmd改为redircmd
    3. 执行的时候,首先检测到是一个redircmd,执行重定向命令

    clip_image010

    这里执行过程是,首先关掉某文件描述符,如果重定向是>,关掉的是输出,否则是输入,释放文件描述符

    然后打开指定的文件(代码一开始是错的,没有制定flag,没有权限新建或者打开),赋予刚刚释放的文件描述符

    1. 然后递归调用runcmd执行可执行函数,如果是>,那么可执行函数的输出重定向到了文件里,如果是<,那么可执行函数的输入重定向为文件内容

    clip_image011

    1. 改进

    在上面的代码中,myexecv是我自己写的,因为比如要执行ls函数,必须要输入/bin/ls才行,为了添加默认的路径,我将输入命令进行了处理

    clip_image012

    如果说有自带的路径,直接执行就好了,否则添加默认路径/bin/

    list命令

    实际上就是一次输入多个不相关的命令,依次执行

    处理的方式也比较简单

    1. 首先调用pasrepipe来处理管道命令,这个调用会将第一个分号出现之前的所有管道命令全部解析
    2. 如果发现输入中有分号,那么调用listcmd函数,这个函数把前面生成的cmd结构体指针放入一个新的结构体中,作为left,而后面解析的部分作为right,这样像链表一样依次的解析

    clip_image013

    listcmd如下

    clip_image014

    1. 执行的时候,依次的从左至右就好了

    clip_image015

    总结

    在第领章的时候,有这么几句话

    clip_image016

    一开始看的时候我并不太懂,现在体会到了,从XV6的shell设计里,可以看出,内部只使用少量的通用接口,但是通过组合,使用管道,重定向等,可以实现很复杂强大的功能

    另外,这里面的数据结构设计的也很棒,包括命令的链表式连接,pipe的树状执行等

  • 相关阅读:
    JSONP
    懒加载
    HTTP 状态代码
    java4中创建内对象的方法
    注册jdbc驱动程序的三种方式
    java Clone()克隆
    Class.forName()的理解
    Bitmap介绍
    前端-PC端瀑布流【10张图】
    百度小程序-图片画廊-使用previewImage方法实现
  • 原文地址:https://www.cnblogs.com/bdhmwz/p/4922426.html
Copyright © 2011-2022 走看看