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的命令实现居然是递归的哈哈
这里是主函数:
调用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
execcmd()返回一个struct cmd()结构体.
同样的*cmd()函数都会返回一个对应的 *结构体
值得特别强调好玩的事情是,你会发现这里 execcmd()返回的是一个 struct cmd* 指针
但是execcmd()函数确实申请的是一个struct execcmd()结构体.那么问题就来了..怎么会这样.
回过头去观察四种结构体的之间的关系你就会发现,这里巧妙之处就在于,他们的第一个成员都是相同的!
返回了一个"基类"指针.
"骚年别闹~"
Implement a Shell by yourself -- MIT xv6 shell
原文地址:http://blog.csdn.net/cinmyheart/article/details/45122619