该实验主要是实现一个简易的shell,其实在书本上有着许多地方和实验是息息相关的,最重要的就是搞懂书本。其实本次实验是标准的Unix环境编程,具体的函数大家可以参考《UNIX环境高级编程》,这本书讲的十分详细。
lab的具体内容可以在官网中下载,所有的lab代码在GitHub上。
实验要求
该实验已经完成了大部分的框架,我们只需要按照需求在tsh.c文件中实现以下的函数功能:
eval:解析和解释命令行的函数,大约70行;builtin_cmd:识别内置命令的函数,内置命令包括:quit、fg、bg和job,大约25行;do_bgfg:实现fg和bg的内置命令的杉树,大概50行;waitfg:等待前台作业完成函数,大概20行;sigchld_handler:SIGCHILD信号处理函数;sigtstp_handler:SIGTSTP (ctrl-z)信号处理函数;sigint_handler:SIGINT (ctrl-c)信号处理函数;
具体的要求可以参考writeup。
eval函数
1 | void eval(char *cmdline) |
在eval函数中,我们首先通过parseline函数解析输入的命令行,然后根据builtin_cmd函数判断是否是内置命令,如果是内置命令那直接在builtin_cmd函数中就执行了。
如果不是内置命令,则需要在shell中新建一个子进程来执行该命令,这个时候需要《深入理解计算机系统 第三版》P525的eval函数和P543用sigprocmask来同步进程。
最后,如果该命令是前台程序(fg),那么shell需要等待该程序结束;如果是后台程序,那么shell打印后继续读取新的command。
builtin_cmd函数
1 | int builtin_cmd(char **argv) |
这个直接参考书本P525的builtin_command函数。
do_bgfg函数
1 | void do_bgfg(char **argv) |
这个函数主要用来执行bg <job>和fg <job>内置命令。不管输入的是pid还是jid,我们取出job,然后设置状态,最后发送SIGCONT给整个|pid|进程组的进程去继续进程,如果是后台进程,直接打印就好了,然后继续工作;如果是前台进程,shell需要等程序执行完。
waitfg函数
这里我们采取sigsuspend的方式进行等待,当子程序结束,内核会给shell发送一个SIGCHILD信号,这时会唤醒shell。
1 | void waitfg(pid_t pid) |
sigint_handler和sigtstp_handler
1 | void sigint_handler(int sig) |
1 | void sigtstp_handler(int sig) |
这两个函数比较简单,都是直接像子进程发送信号。
sigchld_handler函数
1 | void sigchld_handler(int sig) |
本函数需参考P539的handler2函数,尽可能多的处理信号。
验证
对比了代码的执行情况,如下,可以发现二者基本一致。
1 | $ make test15 |
1 | $ make rtest15 |