Implement a Shell by yourself -- MIT xv6 shell
来源:互联网 发布:淘宝金牌卖家靠谱吗 编辑:程序博客网 时间:2024/05/29 03:14
Implement a Shell by yourself -- MIT xv6 shell
这个其实是作为6.828的一个小课堂作业 ...
着重分析构建思想和过程,具体代码实现去github可以找到.
https://github.com/jasonleaster/MIT_6_828_assignments_2012/blob/homework1/sh.c
这里主要实现了基础的三类命令
- 可执行的程序命令
- 重定向命令
- 管道命令
实现的"基类" (原谅我用了这个词)就是struct cmd这个结构体就一个成员,用于记录命令的类型.
三类, ' ' 表示可执行程序 '|' 表示管道命令, '<' 和'>' 表示重定向类型.
每一个类型分别继承基类,派生出对应的三类结构体
struct execcmd
struct redircmd
struct pipecmd
对于可执行命令,主要记录可执行程序的程序名字还有各种选项参数.所以会有 char* argv[MAXARGS]
对于重定向命令,主要记录 cmd 即触发这个重定向的程序比方说 ./a.out > tmp.txt
那么cmd就是记录的./a.out 重定向到那个文件的文件名 char *file指针指向这个文件名.
对于管道, 则主要记录管道左右两侧的命令
void runcmd(struct cmd * cmd);
这个函数是真正驱动调用实现shell的核心.负责调用系统接口函数 execv(), open(), close(), dup(), pipe()等等一系列函数,来完成我们既定的目标.
作业也就是补全这个函数.
这是个递归的函数!很有意思.
你会发现,shell的命令实现居然是递归的哈哈
下面是我画的一个简单的流程图
你会发现,是先处理可执行程序,然后检查输入中是否有管道,如果有,那么递归的调用parsepipe去构建这些可执行程序间的输入输出的关系.直到所有管道被检查完,那么返回parsecmd(),进入到runcmd()开始执行命令.
这里是主函数:
调用getcmd()在标准输入读取sizeof(buf)大小的字符,然后,写入到buf中.
那个 if(buf[0] .... )是判断你是不是输入了 cd命令 如果是把buf尾部赋值为0,这样buf看起来就是储存的一个字符串,
然后调用chdir() 更换当buf+3开始的字符串指定的路径.
接着continue继续读取命令啦...
如果你不更换路径了
我们就fork1()出一个子进程,parent process就一直等待子进程挂掉...等啊等..等啊等..
这个时候,子进程就开始调用parsecmd()去分析你输入的命令字符串咯...
es指针指向字符串的末端,确切的说是空字符处
然后去调用 parseline(&s, es)
parseline() 看起来太弱了,就是一层简单的封装.实际核心函数还是parsepipe
这里要说一说里面关键的几个子函数
经常会看到下面这个while循环,作用是啥呢?
s 指向我们输入的命令字符串,strchr通过检查 *s是否是 whilespace里的任意一个字符,如果是其中某个字符,那么我们就要跳过这个字符,知道我们把s移动到指向一个非空格类的字符
再看另外一个一旦遇到空格字符,或者symbols中的任意一个字符,我们就不再移动指针s
下面是实现pipe的一部分.
分析:
第一次调用fork1() 产生 child process 1 该进程用于运行 pcmd->left 指向的进程
第二次调用fork1() 产生 child process 2 该进程用于运行 pcmd->right 指向的进程
child process 1 由于先 close(1)那么文件描述符1就被空余出来了, 调用dup(p[1])把 child process 1的标准输出(文件描述符默认的是1)和管道的输出关联起来
child process 2的伎俩差不多,只是把进程的标准输入关闭了,把从管道的输入作为进程的标准输入来用.
我刚差点被自己蠢哭了,睡了三个小时哈哈哈
这里有两个 wait(), 为什么呢?奇怪..
一点都不奇怪..必须两个wait(),因为这里有两个子进程,parent process必须等这两个进程都挂了之后再结束.
execcmd()返回一个struct cmd()结构体.
同样的*cmd()函数都会返回一个对应的 *结构体
值得特别强调好玩的事情是,你会发现这里 execcmd()返回的是一个 struct cmd* 指针
但是execcmd()函数确实申请的是一个struct execcmd()结构体.那么问题就来了..怎么会这样.
回过头去观察四种结构体的之间的关系你就会发现,这里巧妙之处就在于,他们的第一个成员都是相同的!
返回了一个"基类"指针.
最后的运行效果.
能在本地的文件 ./tmp.txt里找到输出.
update: 2015.04.19 原本的程序是不能调用/bin/目录下的程序的,那多无聊哇...我该了部分代码.然后还改了那天杀的两个对齐...
"骚年别闹~"
- Implement a Shell by yourself -- MIT xv6 shell
- xv6 shell
- xv6-lab(1)--shell
- xv6 Shell & OS organization
- 【xv6学习之HW1】shell
- Make a Windbg By Yourself
- Make a Windbg By Yourself
- [MIT 6.828] HW shell
- 关于mit xv6
- Make a Windbg By Yourself(一)
- Xv6,MIT教学性操作系统
- [MIT 6.828] HW boot xv6
- Shell Script Utility To Read a File Line By Line
- Follow MIT的6.828 Lab1 & Homework:shell
- UIL doesn't support scheme(protocol) by default You should implement this support yourself
- [gist]Build Yourself a Backbone.js Step by Step
- implement a queue by using two stacks
- How to implement an UEFI Shell Application
- T-SQL:取最值的几种方式的简单比较.
- Python xml属性/节点/文本的增删改[xml.etree.ElementTree]
- hdu2571 命运 简单dp
- Redis的强大高级应用
- spket插件设置ExtJS自动提示
- Implement a Shell by yourself -- MIT xv6 shell
- iOS 完全复制UIView
- ZOJ 3471 Most Powerful(状压DP)
- iOS AutoLayout
- 2015编程之美资格赛2题—区间dp
- UVA 11361 Investigating Div-Sum Property(数位DP)
- 剑指offer--数组中的逆序对
- Android开发环境搭建
- G - Good subsequence