CSAPP ShLab
shell-lab是csapp的配套实验之一,它要求我们实现一个功能和unix shell类似的tiny shell,在源文件tsh.c中已给出了基本框架,剩下的只需要完成实现指定的函数即可,该实验对应csapp的第8章内容。
Unix Shell
Shell可以认为是一个可交互的命令解释器,它替代用户运行程序,shell会从标准输入流stdin
等待输入命令行,根据命令行内容的指示执行对应操作。命令行是由空格分隔的ascii单词序列组成,其中第一个单词为内置命令或可执行文件的路径(或环境变量path目录下的可执行文件相对路径),其余的单词就是命令行的参数了。如果是执行内置命令,那么shell将在当前的进程立即执行该命令;如果执行其它可执行文件,shell就会fork一个子进程,然后在该子进程的上下文加载并运行该程序,用于解释单个命令而创建的子进程统称为job(作业)
。当命令行以&
结尾时,作业将在后台运行,即shell打印提示并等待下一个命令之前,不会等待作业终止,否则作业将在前台运行,这样shell在等待下一个命令之前要等待作业终止,所以任何时候前台只能运行一个作业,而后台可以运行多个作业。
Unix Shell也支持 作业控制 的概念,允许用户切换作业为前台或后台运行,也允许通过信号修改作业的状态(running、stopped、terminated)。通过键盘可以产生信号,例如CTRL+C产生SIGINT
(interrupt)终止信号,会终止前台作业并使其变为后台terminated状态;CTRL+Z产生SIGTSTP
(stop)暂停信号,会暂停前台作业并使其变为后台stopped状态。通过命令也可以产生信号,例如bg和fg命令产生SIGCONT
(continue)继续信号,会继续运行被暂停的作业并使其变为后台(前台)running状态,如果对应作业本身就为running状态,那么这两个命令只有切换前后台的作用。
tsh
我们需要按照题目的要求实现一个tiny shell,只有unix shell的部分功能,其要求如下:
- 提示前缀为”
tsh>
“。 - 用户键入的命令由一个name和0至多个参数构成,他们均由空格分隔。如果name是一个内置指令,则tsh要立即处理它并继续等待下一个命令;否则tsh假定name是可执行文件的路径,并创建一个子进程(作业),在该子进程的上下文中加载该文件并运行。
- tsh不需要支持管道(
|
)和I/O重定向(>和<
)。 - 输入
CTRL+C(CTRL+Z)
应当发送SIGINT(SIGTSTP)
信号给当前的前台作业和其任何子进程,如果没有前台作业则该信号无效。 - 当命令行以
&
结尾时,tsh应当运行后台作业,否则将会在前台运行这个作业。 - 每个作业都应当由进程ID(PID)或作业ID(JID)标识,JID是tsh分配的正整数,且JID在命令行需要加上前缀
%
。 - tsh需要支持如下内置指令:
quit
命令终止tsh进程jobs
命令列出所有后台进程bg <job>
命令会向作业发送 SIGCONT
信号来重启job,并作为后台作业运行,参数可以是PID或JID fg <job>
同上,唯一区别是job以前台作业运行
- tsh需要回收所有其子僵尸进程,如果有任何作业由于接收到未捕获的信号而终止,那么tsh需要识别此事件并打印一条带有该作业PID的消息以及对该问题信号的描述。
代码实现
tsh.c已经将主体的框架和非关键部分的函数实现好,我们只要完成eval、builtin_cmd、do_bgfg、waitfg这四个函数加上sigchld_handler、sigtstp_handler、sigint_handler这三个信号处理函数即可,很多代码可以参考书里第八章的部分代码,具体实现如下:
/* |
实验提供了trace01~trace16十六个文本文件,作为sdrive.pl的输入驱动我们编写的tsh输出对应结果,模拟命令行执行的结果,开发阶段可以对tsh手动输入trace文本中的命令进行调试。另外实验也提供了tshref作为参考二进制文件,我们的目标就是使tsh的表现与tshref完全一致,makefile文件中给出了测试指令,使用make test和make rtest指令即可对tsh和tshref进行快速测试,tshref.out文件为参考输出结果。
注意
- 由于我们引入了csapp源文件,所以会出现一些重复定义的编译错误,相应的需要修改源文件tsh.c,将重复定义的符号MAXLINE、unix_error、app_error、Signal注释掉;makefile文件也要修改加上对csapp的依赖,因为csapp.c用到的pthread库不是标准linux库,所以要在编译tsh文件时要加上
-lpthread
参数。makefile中添加如下内容:tsh: tsh.c csapp.c csapp.h
$(CC) $(CFLAGS) -o tsh csapp.c tsh.c -lpthread - 代码中大量使用了csapp.c提供的 错误处理包装函数(如Kill,Sigprocmask),而这些函数又使用了unix_error函数,其调用的exit函数不是 异步信号安全 的函数,所以在信号处理程序中不能使用,而要重新包装换用
sio_error
函数。也就是存在一种可能(尽管可能性很低),主程序在执行exit函数的过程中(因为其非原子过程)被信号中断,而信号处理程序中也恰好调用了exit函数,则可能会产生状态不一致的问题。但是代码中省略了重新包装的步骤,特此注明一下。 - 不论是主程序还是信号处理程序在访问全局共享变量时(如jobs),都要临时阻塞所有信号,待访问结束后再解除阻塞,将访问操作变为不可中断的原子操作,避免信号中断引起的状态不一致。
- 注意在信号处理程序中保存和恢复
errno
的状态。 - 为了方便,在信号处理程序中使用的printf函数,其实应当替换为异步信号安全的
sio_printf
函数。
本文标题:CSAPP ShLab
文章作者:古玩
发布时间:2020-08-18
最后更新:2020-08-18
原始链接:https://blog.handsomesnail.com/list/1597733110.html
版权声明:本作品采用CC BY-SA 4.0协议进行许可。