| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 系统运维 -> 实验四-Shelllab实验(csapp、计算机系统外壳实验) -> 正文阅读 |
|
[系统运维]实验四-Shelllab实验(csapp、计算机系统外壳实验) |
??一、准备工作1、首先明确实验目的: ·总的来说就是让我们补充位于tsh.c中的七个函数,从而实现一个支持任务功能的shell。 因此在这儿将这七个函数分为两部分: (1)实现完成内建命令(jobs、fg、bg、kill)的四个函数: ?? 接着再来了解一下tsh支持的四个内置命令: ·Quit:命令终止tsh进程 ·jobs:命令列出所有后台进程 ·bg:命令会向作业发送SIGCNOT信号来重启job,并作为后台作业运行,参数可以是PID或JID ·fg:同上,唯一区别是job以前台作业运行 (2)实现三个信号(SIGCHLD、SIGINT、SIGTSTP)的处理函数: · 因此我们再来具体了解一下这三个信号: ? ·?再来了解一下需要用到的辅助函数: 2、了解实验资源: 以上文件中,我们要实现的七个函数均在tsh.c中,tshref是参考文件。图中的txt文件均是测试文件。 3、如何比对我们实现的同时是否正确: (1)首先执行make指令编译tsh.c得到可执行文件tsh: ? ?(2)然后就执行make rtest01 ;make test01进行比对,如果我们的执行结果与参考结果一致,则实现正确,如下: 否则不正确,如下: (输出不一致,说明功能未成功实现) 二、具体实现
? ? ? ? ? 可成功运行。 ? ? ?2.trace 02 ->实现内置的quit
?trace02.txt文件中只有quit,WAIT两条命令。 先执行看看: 可以看到无法正常终止,因为tsh的quit内置命令还未编写,所以不能正常退出。 因此需要我们实现终止命令(quit。? (2)实现之前我们来了解eval()与execve()执行流程和fork()多进程运行方式: ? 程序会首先执行 eval(),在 eval中进行判断(使用buildin_cmp()函数),如果发现命令不是内置命令,则会调用 fork()函数来新建一个子进程,在子进程中调用 execve()函数通过 argv[0]来寻找路径,并在子进程中运行路径中的可执行文件,如果找不到可执行文件,则说明命令为无效命令,输出命令无效,并用 exit(0)结束该子进程即可。 (3)实现quit: ?·补齐文件tsh.c中的函数eval()函数和函数builtin_cmd()与quit相关的部分。 ?· 实现思路:首先从命令中提取参数,然后判断是否为内置命令,如果为内置命令,则直接在当前进程执行即可;如果不是内置命令,则需要新建一个子进程,并利用 execve 来通过参数给出的路径寻找出可执行文件并在子进程中执行,如果找不到该可执行文件,则输出命令未找到,并结束子进程。 · 代码:
? 2.然后是判断是否为内置命令的函数builtin_cmd(): ?(4)验证:因为tset03功能是运行一个前台job,并且也是以quit终止,因此一起验证: 可以看出,成功! 3 . trace04 -->实现eval()的后台作业(BK job)管理功能(1)思路: ·在原有的eval函数基础之上添加将作业添加至后台作业管理的函数使用(addjobs())。 ·加以信号的阻塞和取消阻塞。 ***(注意) 那为什么在这里要控制信号的阻塞呢? 答:总的来说,为了保证处理程序回收终止的子进程(delete job)在父进程(addjob)之后进行,否则父子进程之间会出现经典的同步错误---竞争。 详细理解:因为当父进程创建一个子进程时,它就会将这个子进程添加到作业列表(addjobs)。当父进程在SIGCHLD处理程序中回收一个僵尸子进程时,就要从作业列表中删除子进程。理想状态下,这个过程很正确,但是往往真实的运行情况下,会出现问题,如下图: ?总结来说,就是会出现在addjob之前调用deletejob,导致出错。 *** (2)具体实现: ?1.首先使用一个标记符号(bg): ?2.因为要分析传入指令是否要在后台执行进程,因此要补充分析命令的函数builtin_cmd(): ?3.接着在eval中进行判断是否为后台进程: ? ?4.再将waitfg()函数补充完整: 让父进程正确地等待。 ? ?5.接下来实现信号的控制,这里使用sigprocmask()函数显式地阻塞和取消阻塞: (1)初了解: (2)在eval()中的使用: ?上述图片的操作中,我们就保证了父进程先addjob(),然后子进程再deletejob(); 6.接下来实现对应的sigcld_handler()以释放僵尸的子进程: 详情看红框操作。 综上,我们的addjobs就成功实现了! 7.验证: 通过! 4. trace05 -->处理jobs内置命令:? (1)思路:直接调用自带的listjobs()方法,就是在原有builtin_cmd函数中添加一个判断函数,如果参数是jobs,则执行listjobs函数的功能(即将所有的作业打印出来)。 (2)实现: (3)测试: 成功! 5.trace06、trace07 ->处理SIGINT信号(1)目的: 要实现的功能是:trace06->将SIGINT信号转发到前台作业; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?trace07->仅仅将SIGINT信号转发到前台作业; 因此这里放在一起实现。 SIGINT:来自键盘的中断(ctrl+c)。 (2)文档提示: ?综上,就是说要保证ctrl+c只会终止你当前的shell进程,而不会影响其他进程。 (3)实现: ???根据文档中的解决方法,我们来一步步实现。 1.首先更改一下eval函数,在其中调用setpgid(0,0): 添加了红框中的代码,解释也在注释中。 2.更改信号处理函数sigint_handler(),实现转发到前台作业的操作(包含前台作业的进程组) ? 3.还要修改sigchld_handler()函数: **** 为什么呢? ???答:是为了区分进程终止的原因(符合测试文件)(后边也会用到) ????1.是正常终止(exit或return) ? ? ?2.还是因为收到其他信号如:SIGINT而终止。(这里我们是收到SIGINT信号终止的) **** 修改如下: 综上,就成功实现! (4)测试: 成功! 6、Trace08 -> 仅仅将SIGSTP(ctrl+z)转发到前台作业(与上一题实现大同小异)(1)因此我们就直接实现其信号处理函数sigtstp_handler(): (2)依旧来修改一下sigchld_handler()函数。区分终止/停止。 思路:因此在上一题的基础上加上对于SIGTSTP(ctrl+z)的判断和信息显示。 如下: ?·加了红框框的内容,实现。 ·还要加多一个WUNTRACED(见绿框),变成WNOHANG | WUNTRACED。 *** 为什么呢? WNOHANG:挂起调用进程,直到有子进程终止。 WUNTRACED:挂起调用进程,直到等待集合中的一个进程变成已终止或者被停止。 WNOHANG | WUNTRACED:等待集合中的子进程都没有被停止或终止,则返回值为0;如果有一个停止或终止,则返回值为该子进程的PID。 可以理解为WNOHANG接收终止,WUNTRACED接收停止。 两个合在一起就是接收终止和停止(ctrl+z和ctrl+c)。 *** (3)测试: 成功! 7.trace 09 ---> 实现进程内置命令bg?????bg <job>:命令会向一个已经停止的job发送SIGCNOT信号来重启这个job,并作为后台作业运行,参数可以是PID或JID。 (1)首先是完成识别命令: 要将bg命令添加到识别命令的函数builtin_cmd()中: ??? (2)接下来实现其处理函数: ·修改do_bgfg()方法: (3)测试: 成功! 8 .trace 10 ---> 实现进程内置命令fg(与上一题差不多)???????fg <job>:将一个已停止或正在运行的后台作业更改为正在前台运行的作业。 (1)老方法,先往builtin_cmd()方法添加内容: (2)然后往do_bgfg()函数中加入相关处理: 需要注意的一点就是红框所圈的内容,也是与BG实现区别的地方。 (3)测试: 成功! 9 .trace 11 ---> 将SIGINT转发给前台进程组中的每个进程? ? ?trace 12 ----> 将SIGSTP转发给前台进程组中的每个进程 ???这两个实验在之前的trace06-trace07的分析中已经实现了,因此我们直接执行即可: (1)sigint_handler()函数: (2)sigtstp_handler()函数: ? (3)测试: 成功! 10.trace13 -->重新启动进程组中每个已经停止的进程
在trace09和trace10中对BG和FG的处理中(do_bgfg()函数),我们是有条件地唤醒进程,并将之修改为对应需要的状态(前台或后台),如下图: 继续分析: · 因为此时需要唤醒所有停止的进程,因此要将唤醒函数kill(pid,SIGCONT)的第一个参数改为-pid,因为当其第一个参数<0时,kill就会将SIGCONT信号传递给整个进程组。 · 因为在FG中,有一步是需要等待当前的前台进程完成之后,才会唤醒进程组中的进程,所以为了保证唤醒所有进程,就要去掉FG中,job->state == ST才传递SIGCONT信号的判断,因为当前运行进程可能没有停止(ST),但是进程组中是有停止的,进程组中停止的这些也需要被唤醒。 (2)综上,我们得到以下实现: (3)测试: 成功! 11.trace14- 简单的错误处理(就是处理输入未实现的命令、fg、bg参数不正确等错误情况)(1)先运行看看怎么处理: 从上图看出一共有五种处理方式,因此我们在do_bgfg()中进行对应的处理即可。 (2)处理: ·第一个错误:Command not found,未实现的命令。 我们再次回顾一下shell的执行流程:程序会首先执行 eval(),在 eval中进行判断(使用buildin_cmp()函数),如果发现命令不是内置命令,则会调用 fork()函数来新建一个子进程,在子进程中调用 execve()函数通过 argv[0]来寻找路径,并在子进程中运行路径中的可执行文件,如果找不到可执行文件,则说明命令为无效命令。因此我们在此处加入输出语句即可,如下图: · 第二个错误是:fg command requires PID or %jobid argument,fg命令时没有传入pid或jid。 ??因此在do_bgfg()中实现: ·第三个错误是:fg: argument must be a PID or %jobid,传入了pid或jid,但是不符合规范(pid或jid必须为数字)。 处理: ·第四个错误是:No such process,通过传入的pid找不到对应的作业(job=null) 处理: ·第五个错误:No such job,通过传入的jid找不到对应的job(job=null) 此外,我们发现还有一行(绿色框所圈),这里我们和trace15 一起解决。所以接下来看一下trace15. 12. trace15-->所有命令一起运行(1)先make rtest一下,看看缺什么: ? 如上图,缺少这两条消息的处理,因此我们要加上处理: 那么首先查看一下文件trace15.txt,看看是因为什么信号出现这种情况: 可以看到,INT信号将job10终止。 ?TSTP信号将job1中断。 因此我们就在终止信号处理函数sihchld_handler()中进行判断处理,并输出上述错误信息: 处理: ? OK,完成! (3)测试: Trace14: 成功! Trace15(测试所有命令): 成功! 13. trace16 -->测试shell是否能够处理来自其他进程而不是终端的SIGTSTP和SIGINT信号。(1)查看一下trace16.txt: 可以看到测试文件的操作是: ?????测试shell能否处理来自mystop和myint的SICINT和SINTSTP信号。 (2)测试: ?分析:可以看到上图中小旗帜标识的位置,在jobs执行内置命令之后,对于SIGINT和SINTSTP信号均做出了处理。成功! ps:trace16其实还有点不太清楚。 最后贴上所有代码:
|
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/15 14:06:14- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |