Shlab1&2
Shlab1&2
1.学会编译tsh.c,调用tsh文件traceXX.txt的功能验证方法:
通过make clean和make指令来对tsh.c文件进行编译,如果没有语法错误将会如上图所示,编译成功。在tracexx.txt文件中都是一条一条已经写好的命令行语句,将会通过文件流输入的方式给予我们写好的shell代码,然后与给的tshref运行结果进行比较,如果相等则说明我们的代码是正确的。
通过“make 跟踪文件”来对我们编写的tsh.c文件进行测试(如上左图),正确的标准结果可以通过“make r跟踪文件”来对老师给的tshref可执行文件进行测试(如上右图),如果相同则证明代码正确,实验成功。
2.用trace01和trace02比较tsh和tshref执行结果并分析:
trace01.txt
trace01跟踪文件,执行close和wait。读到close的时候关闭文件并等待。
trace02.txt
trace02.txt执行quit和wait命令。tsh.c文件中 quit并未被完善,因此在这里make test02没有执行退出,一直停留在这里。make rtest02的时候,遇到quit直接退出。
3.编程实现quit内置命令,补齐文件tsh.c中的函数eval()和函数builtin_cmd()与quit相关的部分:
- 在eval函数内调用parseline()函数解析命令行参数,然后调用builtin_cmd判断是否为内置命令。如果不是内置命令,调用fork()生成子进程,然后调用execve()加载程序并执行。
- 在builin_cmd函数内,判断命令行第一个参数是否为”quit”如果是则exit(0),中止程序。
4.使用trace03验证quit命令
首先打印了trace03.txt中的语句:trace03.txt - Run a foreground job.然后执行quit命令。
执行结果和reftsh中的一样,说明正确。
5.了解eval()与execve执行流程和fork()多进程运行方式
eval:
①eval函数首先解析命令行,然后检查命令行第一个参数是否是内置命令,如果是内置命令,则立即解释。否则外壳创建一个子进程,并在子进程中执行所请求的程序。
②如果用户要求在后台运行该程序,那么外壳返回到循环的顶部,等待下一个命令行。否则使用waitpid()函数等待作业终止。再开始下一轮迭代。
execve:
在子进程中通过系统调用execve()可以将新程序加载到子进程的内存空间。这个操作会丢弃原来的子进程execve()之后的部分,而子进程的栈、数据会被新进程的相应部分所替换。即除了进程ID之外,这个进程已经与原来的进程没有关系了。
由于是将调用进程取而代之,因此对execve的调用将永远不能返回。也无需检查它的返回值,因为该值始终为-1,实际上,一旦返回就表明了错误,通常会有error值来判断。
fork:
fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事.
fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:
1)在父进程中,fork返回新创建子进程的进程ID;
2)在子进程中,fork返回0;
3)如果出现错误,fork返回一个负值;
在fork函数执行完毕后,如果创建新进程成功,则出现两个进程,一个是子进程,一个是父进程。子进程得到的只是父进程的拷贝,而不是父进程资源的本身。因此fork产生的子进程间是相互独立的。