[已完成+附代码]CS:APP:Lab6-ShellLab
由于我的第五个实验的partB部分一直出问题。而且修了好久没解决先不管了
这个实验建议一定要认真读完csapp全书的第八章。不然可能会毫无思路。千万不要上来直接做。
0. 环境配置和实验下载
利用docker配置Linux环境。无论你是mac还是windows都可以轻松搞定 https://www.cnblogs.com/JayL-zxl/p/14286789.html
1. 实验目的
你的任务为补全tsh.c
里面的一些空缺的函数。
• eval
: 解析命令行 [70 lines]
• builtin cmd
: 识别命令是否为内置命令: quit, fg, bg, and jobs. [25 lines]
• do bgfg
:实现bg
和fg
命令. [50 lines]
• waitfg
: 等待前台程序完成 [20 lines]
• sigchld handler
: Catches SIGCHILD signals. 80 lines]
• sigint handler
: Catches SIGINT (ctrl-c) signals. [15 lines]
• sigtstp handler
: Catches SIGTSTP (ctrl-z) signals. [15 lines]
每一次你修改tsh.c
函数之后都要重新编译它,然后运行你的shell
make
./tsh
在writeup
里面给了非常多的提示。这里简单给大家列举一下
- 要详细阅读书本的第八章内容
- 如果用户输入
ctrl-c
(ctrl-z
),那么SIGINT
(SIGTSTP
)信号应该被送给每一个在前台进程组中的进程,如果没有进程,那么这两个信号应该不起作用。 - 如果一个命令以“&”结尾,那么tsh应该将它们放在后台运行,否则就放在前台运行(并等待它的结束)
- tsh应该回收(reap)所有僵尸进程,如果一个工作是因为收到了一个它没有捕获的(没有按照信号处理函数)而终止的,那么tsh应该输出这个工作的PID和这个信号的相关描述。
tsh
应该支持如下的内置命令
– The quit command terminates the shell.
– The jobs command lists all background jobs.
– The bg <job> command restarts <job> by sending it a SIGCONT signal, and then runs it in
the background. The <job> argument can be either a PID or a JID.
– The fg <job> command restarts <job> by sending it a SIGCONT signal, and then runs it in
the foreground. The <job> argument can be either a PID or a JID.
书上有展示过一个非常简单的Shell程序这里先复习一下。
当然上面的程序并没有回收后台运行的程序。而且它非常不完整,但足够帮助我们理解整个shell的基本逻辑了
1.1 信号简介
当一个进程收到信号之后就会调用信号处理程序。这个也是我们任务之一。
信号--->进程---->信号处理程序---执行------结果
当然关于信号有很多可以说的。比如信号不会排队啊。信号处理程序的阻塞,以及并发情况如何处理啊。这些请大家仔细阅读课本第八章。
2. 实验开始
好了下面开始实验了。首先根据writeup我们知道我们要依次根据test文件进行测试。就是说我们的make test0x的执行结果要和make rtest0x的参考结果一样就可以了。
这里我们发现对于test02的测试就出现了问题。这里去看一下trace02看看到底发生了什么。
这里有一条quit指令。不通过的原因就是我们的tsh程序还没有实现对于这个命令的处理。所以它不会退出而会停在这里。
下面正式开始我们的实验啦
由于我们没有办法预估信号到达的顺序。因此对于并发编程的处理就变得非常的重要。
考虑下面这种情况
因此我们要考虑这种情况。在父进程addjob
之前先把SIGCHLD
信号阻塞掉。
1. eval函数的实现
void eval(char *cmdline)
{
char *argv[MAXLINE]; /*Argument list execve()*/
char buf[MAXLINE]; /*Hold modified commend line*/
int bg; /*Should the job run in bg or fg?*/
pid_t pid;
int state;
sigset_t mask_all, mask_one, prev_one;
strcpy(buf,cmdline);
bg=parseline(cmdline,argv);
if(argv[0]==0){
return ;//ignore empty line
}
if(!builtin_cmd(argv)){
sigfillset(&mask_all);
sigemptyset(&mask_one);
sigaddset(&mask_one, SIGCHLD);
sigprocmask(SIG_BLOCK, &mask_one, &prev_one); //Block SIGCHLD
if((pid==fork())==0){
sigprocmask(SIG_SETMASK,&prev_one,NULL);//UnBlock SIGCHLD
if (setpgid(0, 0) < 0)
{
perror("SETPGID ERROR");
exit(0);
}
if (execve(argv[0], argv, environ) < 0) //if execve error return 0
{
printf("%s: Command not found\n", argv[0]);
exit(0);
}
}
else{
state = bg ? BG : FG;
sigprocmask(SIG_BLOCK, &mask_all, NULL); //parent process
addjob(jobs, pid, state, cmdline);
sigprocmask(SIG_SETMASK, &prev_one, NULL); //unblock SIGCHLD
}
bg?printf("[%d] (%d) %s",pid2jid(pid), pid, cmdline):waitfg(pid);
}
return;
}
基本参考了书上p525
的shell
代码和p543
的代码。考虑了上述提到的并发访问问题。
2. builtin_cmd
的实现
如果用户输入的是built-in command则立即执行否则返回0
一共需要支持四个内置命令
分别处理即可
int builtin_cmd(char **argv)
{
if(!strcmp(argv[0],"quit")){
exit(0);
}
if(!strcmp(argv[0],"&"))
return 1;
if(!strcmp(argv[0],"bg")||!strcmp(argv[0],"fg"){
do_bgfg(argv);
return 1;
}
if(!strcmp(argv[0],"jobs")){
listjobs(jobs);
return 1;
}
return 0; /* not a builtin command */
}
3. do_bgfg
的实现
Execute the builtin bg and fg commands
每个job都可以由进程ID(PID)或job ID(JID)标识,该ID是一个正整数tsh分配。 JID应该在命令行上以前缀“%”表示。 例如,“%5”
表示JID 5,“ 5”表示PID5。(我们已为您提供了所需的所有例程处理工作清单。)
tsh> fg %1
Job [1] (9723) stopped by signal 20
kill(pid,signal)的规则
if (pid < 0) 则向|pid|的组中全部发送信号
if(pid > 0) 则就向单个进程发送
if(pid=0) 信号将送往所有与调用kill()的那个进程属同一个使用组的进程。
void do_bgfg(char **argv)
{
struct job_t *job;
int id;
if(argv[1]==NULL){
printf("%s command requires PID or %%jobid argument\n", argv[0]);
return ;
}
if (sscanf(argv[1], "%%%d", &id) > 0)
{
job = getjobjid(jobs, id);
if (job == NULL)
{
printf("%%%d: No such job\n", id);
return ;
}
}
else if (sscanf(argv[1], "%d", &id) > 0)
{
job = getjobpid(jobs, id);
if (job == NULL)
{
printf("(%d): No such process\n", id);
return ;
}
}
else
{
printf("%s: argument must be a PID or %%jobid\n", argv[0]);
return;
}
if(!strcmp(argv[0], "bg"))
{
kill(-(job->pid), SIGCONT);
job->state = BG;
printf("[%d] (%d) %s",job->jid, job->pid, job->cmdline);
}
else
{
kill(-(job->pid), SIGCONT);
job->state = FG;
waitfg(job->pid);
}
return;
}
3. waitfg
函数实现
Block until process pid is no longer the foreground process
void waitfg(pid_t pid)
{
sigset_t mask;
sigemptyset(&mask);
while (fgpid(jobs) > 0)
sigsuspend(&mask);
return;
}
这里注意一下
4. sigchld_handler
的实现
The kernel sends a SIGCHLD to the shell whenever
* a child job terminates (becomes a zombie),
* stops because it received a SIGSTOP or SIGTSTP signal.
The handler reaps all available zombie children, but doesn't wait for any other currently running children to terminate.
关于waitpid
参数的全介绍
void sigchld_handler(int sig)
{
int olderrno = errno;
pid_t pid;
int status;
sigset_t mask_all, prev;
sigfillset(&mask_all);
while((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0)
{
if (WIFEXITED(status)) //正常
{
sigprocmask(SIG_BLOCK, &mask_all, &prev);
deletejob(jobs, pid);
sigprocmask(SIG_SETMASK, &prev, NULL);
}
else if (WIFSIGNALED(status))
{
struct job_t* job = getjobpid(jobs, pid);
sigprocmask(SIG_BLOCK, &mask_all, &prev);
printf("Job [%d] (%d) terminated by signal %d\n", job->jid, job->pid, WTERMSIG(status));
deletejob(jobs, pid);
sigprocmask(SIG_SETMASK, &prev, NULL);
}
else /
{
struct job_t* job = getjobpid(jobs, pid);
sigprocmask(SIG_BLOCK, &mask_all, &prev);
printf("Job [%d] (%d) stopped by signal %d\n", job->jid, job->pid, WSTOPSIG(status));
job->state= ST;
sigprocmask(SIG_SETMASK, &prev, NULL);
}
}
errno = olderrno;
return;
}
5. sigint_handler
的实现
The kernel sends a SIGINT to the shell whenver the user types ctrl-c at the keyboard. Catch it and send it along to the foreground job.
void sigint_handler(int sig)
{
int pid=fgpid(jobs);
int jid=pid2jid(pid);
sigset_t mask_all, prev;
sigfillset(&mask_all);
if(pid!=0){
sigprocmask(SIG_BLOCK, &mask_all, &prev);
printf("Job [%d] terminated by SIGINT.\n",jid);
deletejob(jobs,pid);
sigprocmask(SIG_SETMASK, &prev, NULL);
kill(-pid,sig);
}
return;
}
6. sigstp_handler
的实现
The kernel sends a SIGSTP to the shell whenver the user types ctrl-z at the keyboard. Catch it and suspend the
foreground job by sending it a SIGTSTP.
这个的整体和实现和上面的几乎一摸一样。
void sigtstp_handler(int sig)
{
int pid=fgpid(jobs);
int jid=pid2jid(pid);
sigset_t mask_all, prev;
sigfillset(&mask_all);
if(pid!=0){
sigprocmask(SIG_BLOCK, &mask_all, &prev);
printf("Job [%d] stopped by SIGSTP.\n",jid);
(*getjobpid(jobs,pid)).state = ST;;
sigprocmask(SIG_SETMASK, &prev, NULL);
kill(-pid,sig);
}
return;
}
[已完成+附代码]CS:APP:Lab6-ShellLab的更多相关文章
- 分布式消息总线,基于.NET Socket Tcp的发布-订阅框架之离线支持,附代码下载
一.分布式消息总线以及基于Socket的实现 在前面的分享一个分布式消息总线,基于.NET Socket Tcp的发布-订阅框架,附代码下载一文之中给大家分享和介绍了一个极其简单也非常容易上的基于.N ...
- 零行代码为App添加异常加载占位图
前文提要 近期准备重构项目,需要重写一些通用模块,正巧需要设置App异常加载占位图的问题,心血来潮设想是否可以零行代码解决此问题,特在此分享实现思路. 思路分享 对于App占位图,通常需要考虑的控件有 ...
- 零行代码为 App 添加异常加载占位图
前文提要 近期准备重构项目,需要重写一些通用模块,正巧需要设置App异常加载占位图的问题,心血来潮设想是否可以零行代码解决此问题,特在此分享实现思路. 思路分享 对于App占位图,通常需要考虑的控件有 ...
- 数据挖掘领域十大经典算法之—C4.5算法(超详细附代码)
https://blog.csdn.net/fuqiuai/article/details/79456971 相关文章: 数据挖掘领域十大经典算法之—K-Means算法(超详细附代码) ...
- iOS开发 swift 3dTouch实现 附代码
iOS开发 swift 3dTouch实现 附代码 一.What? 从iphone6s开始,苹果手机加入了3d touch技术,最简单的理解就是可以读取用户的点击屏幕力度大小,根据力度大小给予不同的反 ...
- 深入理解计算机系统 (CS:APP) Lab2 - Bomb Lab 解析
原文地址:https://billc.io/2019/04/csapp-bomblab/ 写在前面 CS:APP是这学期的一门硬核课程,应该是目前接触到最底层的课程了.学校的教学也是尝试着尽量和CMU ...
- 图文并茂-超详解 CS:APP: Lab3-Attack(附带栈帧分析)
CS:APP:Lab3-ATTACK 0. 环境要求 关于环境已经在lab1里配置过了.lab1的连接如下 实验的下载地址如下 说明文档如下 http://csapp.cs.cmu.edu/3e/at ...
- error MSB6006: “CL.exe”已退出,代码为X —— 的解决办法
错误 : error MSB6006: “CL.exe”已退出,代码为X . 解决方法: 1.有少可能是执行目录引起的. 参考 http://bbs.csdn.net/topics/370064083 ...
- 解决VS下“LC.exe已退出,代码为-1”问题
今天使用VS2015开发一个Winform程序,手一抖拖错了一个第三方控件,然后将其去掉并删除相关的引用,结果导致了LC.exe错误:"Lc.exe已退出,代码为-1 ". 经过上 ...
随机推荐
- Win 10 Docker安装和简单使用
Win 10 Docker安装和简单使用 1.环境准备 Docker for Windows需要运行在64位Windows 10 Pro专业版.企业版或教育版(1607年纪念更新,版本14393或更高 ...
- 【VNC】vnc远程连接的时候无法显示图像已解决
介绍一个 VNC连接工具:iis7服务器管理工具 IIs7服务器管理工具可以批量连接并管理VNC服务器 作为服务器集成管理器,它最优秀的功能就是批量管理windows与linux系统服务器.vps.能 ...
- Jenkins 部署打包文件 并通过SSH上传到 linux服务器
编译 发布 打包成zip文件 dotnet clean : dotnet的命令清除解决方案 dotnet build : dotnet的命令重新生成 dotnet publish .\Hy.MyDem ...
- Python入门之修改jupyter启动目录
[导读]在给大家分享知识的过程中,我们也会分享一些小技巧,能够帮助大家在学习过程中有更好的体验.之前我们给大家分享了anaconda安装教程以及jupyter notebook使用方法,今天我们为大家 ...
- MYSQL基础知识的复习1
数据库(是存放数据的仓库) 1.根据存储量以及安全性上来划分: 大型数据库:DB2 Oracle(毕业) Hbase 银行 公安局(不加班 没网) 移动 中型数据库:mysql sqlserver(. ...
- Bitter.Core系列二:Bitter ORM NETCORE ORM 全网最粗暴简单易用高性能的 NETCore ORM 之数据库连接
Bitter.Core NETCore 相当的简单易用,下面附上使用示例: 数据中连接:请在你的NETCORE 项目中 创建:Bitter.json 配置文件,然后追加如下配置内容: MSSQL 连接 ...
- python RecursionError: maximum recursion depth exceeded while calling
import copyimport sys # 导入sys模块sys.setrecursionlimit(8192) # 将默认的递归深度修改为r = sys.getrecursionlimit()_ ...
- Springboot中mybatis控制台打印sql语句
Springboot中mybatis控制台打印sql语句 https://www.jianshu.com/p/3cfe5f6e9174 https://www.jianshu.com/go-wild? ...
- C++ Primer Plus读书笔记(五)循环和关系表达式
1.前缀运算符的优先级是从右到左,例如: 1 *++p; //这句话的含义是先对p进行++,再取* 2.循环 c++11新增了一种循环方式,有点和python类似,如下: 1 array<int ...
- loj10005数列极差
题目描述 佳佳的老师在黑板上写了一个由 n个正整数组成的数列,要求佳佳进行如下操作:每次擦去其中的两个数 a 和 b,然后在数列中加入一个数 a*b+1,如此下去直至黑板上剩下一个数为止,在所有按这 ...