第1章 实验基本信息
1.1 实验目的
理解现代计算机系统进程与并发的基本知识;
掌握linux 异常控制流和信号机制的基本原理和相关系统函数;
掌握shell的基本原理和实现方法;
深入理解Linux信号响应可能导致的并发冲突及解决方法;
培养Linux下的软件系统开发与测试能力。
1.2 实验环境与工具
1.2.1 硬件环境
x64 CPU;1.60GHz;8G RAM;256GHD Disk。
1.2.2 软件环境
Windows10 64位。
1.2.3 开发工具
VM VirtualBox 6.1;Ubuntu 20.04 LTS 64位;
Visual Studio 2019 64位;CodeBlocks 17.12 64位;vi/vim/gedit+gcc。
1.3 实验预习
见第二章
第2章 实验预习
总分20分
2.1 进程的概念、创建和回收方法(5分)
进程的经典定义是一个执行中程序的实例,系统中的每个程序都运行在某个进程的上下文中。上下文是由程序正确运行所需的状态组成的。这个状态包括存放在内存中的程序的代码和数据,它的栈、通用目的寄存器的内容、程序计数器、环境变量以及打开文件描述符的集合。
每次用户通过向shell输入一个可执行目标文件的名字,运行程序时,shell会创建一个新的进程,然后在这个新进程的上下文中运行这个可执行目标文件。应用程序也能够创建新进程,并且在这个新进程的上下文中运行它们自己的代码或其他应用程序。
父进程通过调用fork函数创建一个新的运行的子进程。
当一个进程由于某种原因终止时,内核并不是立即把它从系统中清除。相反,进程被保持在一种已终止的状态中,直到它被父进程回收。当父进程回收已终止的子进程时,内核将子进程的退出状态传递给父进程,然后抛弃已终止的进程,从此时开始,该进程就不存在了。如果一个父进程终止了,内核会安排init进程成为它的孤儿进程的养父。
2.2信号的机制、种类(5分)
一个信号就是一条小消息,它通知进程系统中发生了一个某种类型的事件。
每种信号类型都对应于某种系统事件,底层的硬件异常是由内核异常处理程序处理的,正常情况下,对用户进程而言是不可见的。信号提供了一种机制,通知用户进程发生了这些异常。
信号种类
2.3 信号的发送方法、阻塞方法、处理程序的设置方法(5分)
1.发送方法
(1)/bin/kill程序发送信号
/ bin / ki11 程序可以向另外的进程发送任意的信号。比如,命令
linux> / bin/ kill -9 15213
发送信号9(SIGKILL) 给进程15213 。一个为负的PID 会导致信号被发送到进程组PID 中的每个进程。比如,命令
1inux > /bin/ki11 -9 -5213
发送一个SIGKILL 信号给进程组15213 中的每个进程。注意,在此我们使用完整路径/bin / kill,因为有些Unix shell 有自己内置的kill 命令。
(2)从键盘发送信号
Unix shell 使用作业(job)这个抽象概念来表示为对一条命令行求值而创建的进程。在任何时刻,至多只有一个前台作业和0 个或多个后台作业。比如,键入
linux> 1s / sort
会创建一个由两个进程组成的前台作业,这两个进程是通过Unix 管道连接起来的:一个进程运行ls 程序,另一个运行sort 程序。shell 为每个作业创建一个独立的进程组。进程组ID 通常取自作业中父进程中的一个。
(3)kill函数发送信号
(4)alarm函数发送信号
2.阻塞方法
Linux提供阻塞信号的隐式和显式的机制:
隐式阻塞机制:内核默认阻塞任何当前处理程序正在处理信号类型的待处理的信号。
显式阻塞机制:应用程序可以使用sigprocmask 函数和它的辅助函数,明确地阻塞和解除阻塞选定的信号。
3.处理程序设置方法
signal 函数可以通过下列三种方法之一来改变和信号signum 相关联的行为:
如果handler是SIG_IGN ,那么忽略类型为signum的信号。
如果handler 是SIG_DFL ,那么类型为signum的信号行为恢复为默认行为。
否则,handler就是用户定义的函数的地址,这个函数被称为信号处理程序,只要进程接收到一个类型为signum的信号,就会调用这个程序。通过把处理程序的地址传递到signal 函数从而改变默认行为,这叫做设置信号处理程序(installing the handler)。调用信号处理程序被称为捕获信号。执行信号处理程序被称为处理信号。
当一个进程捕获了一个类型为k的信号时,就会调用为信号设置的处理程序, 一个整数参数被设置为k,这个参数允许同一个处理函数捕获不同类型的信号。
当处理程序执行它的return 语句时,控制(通常)传递回控制流中进程被信号接收中断位置处的指令。我们说“通常”是因为在某些系统中,被中断的系统调用会立即返回一个错误。
2.4 什么是shell,功能和处理流程(5分)
在计算机科学中,Shell俗称壳(用来区别于核),是指"为使用者提供操作界面"的软件(命令解析器)。它类似于DOS下的command.com和后来的cmd.exe。它接收用户命令,然后调用相应的应用程序。
shell负责确保用户在命令提示符后输入的命令被正确执行。其功能包括:
(1) 读取输入并解析命令行
(2) 替换特别字符,比如通配符和历史命令符
(3) 设置管道、重定向和后台处理
(4) 处理信号
(5) 程式执行的相关设置
处理流程:
1. Shell首先从命令行中找出特殊字符(元字符),在将元字符翻译成间隔符 号。元字符将命令行划分成小块tokens。Shell中的元字符如下所示:
SPACE , TAB , NEWLINE , & , ; , ( , ) ,< , > , |
2.程序块tokens被处理,检查看他们是否是shell中所引用到的关键字。
3.当程序块tokens被确定以后,shell根据aliases文件中的列表来检查命令 的第一个单词。如果这个单词出现在aliases表中,执行替换操作并且处理过程 回到第一步重新分割程序块tokens。
4.Shell对~符号进行替换。
5.Shell对所有前面带有$符号的变量进行替换。
6.Shell将命令行中的内嵌命令表达式替换成命令;他们一般都采用$(command) 标记法。
7.Shell计算采用$(expression)标记的算术表达式。
8.Shell将命令字符串重新划分为新的块tokens。这次划分的依据是栏位分割 符号,称为IFS。缺省的IFS变量包含有:SPACE , TAB 和换行符号。
9.Shell执行通配符* ? [ ]的替换。
10.shell把所有从处理的结果中用到的注释删除,並且按照下面的顺序实行命 令的检查:
A.内建的命令
B. shell函数(由用户自己定义的)
C.可执行的脚本文件(需要寻找文件和PATH路径)
11.在执行前的最后一步是初始化所有的输入输出重定向。
12.最后,执行命令。
第3章 TinyShell的设计与实现
总分45分
3.1 设计
3.1.1 void eval(char *cmdline)函数(10分)
函数功能:处理用户输入的命令行。
参 数:用户输入的命令行。
处理流程:先使用parseline()分解命令行,得到有效的输入参数,然后使用 builtin_cmd函数判断命令行是否为内置命令:若是,则使用builtin_cmd 处理;若不是,则fork子进程然后执行execve创建新的进程。
要点分析:在eval中,父进程必须在用fork创建子进程前,使用sigprocmask阻塞 SIGCHLD信号,防止父进程刚刚创建完成子进程,子进程在未被加入 到joblist时就被回收,这会在后面在列表中删除子进程造成异常;父 进程创建完成子进程并用addjob记录后,用 sigprocmask解除阻塞。子 进程从父进程处继承了信号阻塞向量,子进程必须确保在执行新程序 之前解除对SIGCHLD的阻塞。
3.1.2 int builtin_cmd(char **argv)函数(5分)
函数功能:判断命令行输入是否为内置命令,若是,则直接进行处理,若能返回 到eval则返回1,若不是,则返回0。
参 数:使用parse()分解命令行输入得到的参数
处理流程:分别判断命令行参数是否为内置命令,“quit”“bg”“fg”“jobs”, 然后分别跳转到各自的执行程序:其中“quit”直接退出程序;“bg” 和“fg”使用函数do_bgfg进行处理,处理完毕后返回1;“jobs”使 用job的辅助操作函数listjobs列出job列表,成功后返回1;若不是 这些内置函数则返回0。
要点分析:非结束进程的处理程序结束后不要忘记return 1。
不要在do_bgfg附近设计信号阻塞,这会阻止shell接受SIGCONT信 号,从而导致命令fg,bg失效。
3.1.3 void do_bgfg(char **argv) 函数(5分)
函数功能:处理内置命令bg,fg命令
参 数:使用parse()分解命令行输入得到的参数
处理流程:首先根据%后面的数字得到joblist中相应的进程,然后根据fg\bg命令 进行不同的处理:若为fg则将该进程转为前台进程,即将进程的状态 改为FG,并显式地等待其结束;若为bg则将该进程转为后台进程, 即将该进程地状态改为BG,然后输出该进程状态。
要点分析:不要在do_bgfg附近设计信号阻塞,这会阻止shell接受SIGCONT信 号,导致命令fg,bg失效。
3.1.4 void waitfg(pid_t pid) 函数(5分)
函数功能:显式地等待前台进程结束。
参 数:待等待的进程pid。
处理流程:先记下该进程的job结构体地址,进行busy loop等待该地址下的job 被clearjob清除。
要点分析:需要先记下等待进程的job结构体地址,否则若在while判断语句中使 用getjobpid会导致在job被从joblist删除后找不到地址而出现段异常。
3.1.5 void sigchld_handler(int sig) 函数(10分)
函数功能:SIGCHLD信号处理程序,在接收到SIGCHLD后回收所有僵死子进程。
参 数:接收到的信号。
处理流程:首先使用while(waitpid(-1,&status,WNOHANG|WUNTRACED)>0)特定 的顺序回收所有僵死子进程,然后在循环体内进行信号类型的判断和 提示的输出。若进程由于正常退出而终止,则直接将其从joblist中删 除;若进程由于其他信号而被杀死,则打印提示信息后将其从joblist 中删除;若进程由于信号停止,则将其状态改为ST。
要点分析:在进程回收时循环体内处理时需要阻塞相关信号,防止在更改job的相 关成员信息时,由于其他信号的中断城改造成错误。
不要使用unix_error处理errno=ECHILD的情况,我们持续对子进程进 行回收,这必会导致回收结束时waitpid返回-1,并设置errno为 ECHILD,若使用unix_error(“waitpid error”)必会主动造成waitpid error:interrupted system call异常的出现。
3.2 程序实现(tsh.c的全部内容)(10分)
重点检查代码风格:
- 用较好的代码注释说明——5分
- 检查每个系统调用的返回值——5分
见github仓库内源代码文件
第4章 TinyShell测试
总分15分
4.1 测试方法
针对tsh和参考shell程序tshref,完成测试项目4.1-4.15的对比测试,并将测试结果截图或者通过重定向保存到文本文件(例如:./sdriver.pl -t trace01.txt -s ./tsh -a "-p" > tshresult01.txt),并填写完成4.3节的相应表格。
4.2 测试结果评价
tsh与tshref的输出在以下两个方面可以不同:
(1)pid
(2)测试文件trace11.txt, trace12.txt和trace13.txt中的/bin/ps命令,每次运行的输出都会不同,但每个mysplit进程的运行状态应该相同。
除了上述两方面允许的差异,tsh与tshref的输出相同则判为正确,如不同则给出原因分析。
4.3 自测试结果
填写以下各个测试用例的测试结果,每个测试用例1分。
4.3.1测试用例trace01.txt
tsh测试结果 | tshref测试结果 |
| |
测试结论 | 相同 |
4.3.2测试用例trace02.txt
tsh测试结果 | tshref测试结果 |
| |
测试结论 | 相同 |
4.3.3测试用例trace03.txt
tsh测试结果 | tshref测试结果 |
| |
测试结论 | 相同 |
4.3.4测试用例trace04.txt
tsh测试结果 | tshref测试结果 |
| |
测试结论 | 相同 |
4.3.5测试用例trace05.txt
tsh测试结果 | tshref测试结果 |
| |
测试结论 | 相同 |
4.3.6测试用例trace06.txt
tsh测试结果 | tshref测试结果 |
| |
测试结论 | 相同 |
4.3.7测试用例trace07.txt
tsh测试结果 | tshref测试结果 |
| |
测试结论 | 相同 |
4.3.8测试用例trace08.txt
tsh测试结果 | tshref测试结果 |
| |
测试结论 | 相同 |
4.3.9测试用例trace09.txt
tsh测试结果 | tshref测试结果 |
| |
测试结论 | 相同 |
4.3.10测试用例trace10.txt
tsh测试结果 | tshref测试结果 |
| |
测试结论 | 相同 |
4.3.11测试用例trace11.txt
测试中ps指令的输出内容较多,仅记录和本实验密切相关的tsh、mysplit等进程的部分信息即可。
tsh测试结果 | tshref测试结果 |
| |
测试结论 | 相同 |
4.3.12测试用例trace12.txt
测试中ps指令的输出内容较多,仅记录和本实验密切相关的tsh、mysplit等进程的部分信息即可。
tsh测试结果 | tshref测试结果 |
| |
测试结论 | 相同 |
4.3.13测试用例trace13.txt
测试中ps指令的输出内容较多,仅记录和本实验密切相关的tsh、mysplit等进程的部分信息即可。
tsh测试结果 | tshref测试结果 |
| |
测试结论 | 相同 |
4.3.14测试用例trace14.txt
tsh测试结果 | tshref测试结果 |
| |
测试结论 | 相同 |
4.3.15测试用例trace15.txt
tsh测试结果 | tshref测试结果 |
| |
测试结论 | 相同 |
代码、附件github地址
https://github.com/ChenDolph7in/HITICS-LABS-in-21-Spring/tree/master/Lab7