提示

  • 利用测试文件逐步构建tsh,例如先从trace01.txt开始。
  • 当在进程在前台运行时(例如我们的tsh),你键入Ctrl+C,那么内核就会发送一个SIGINT信号(信号代码2)给这个前台进程组中的每个进程。也就是说假如你开始创建子进程的时候,没有将子进程的进程组给设置到另外一个进程组(因为创建子进程的进程组ID默认是父进程的进程组ID),那么这个终止信号将会发送到你每一个创建的子进程中,包括前台和后台。然后会就导致你的程序出现bug,sigchld_handler可能会比sigint_handler运行的还快,因为你自己直接把子进程都给杀死了。

    解决方法就是将我们创建的子进程分到外面去就行了,就如同上图的pid=32,和pid=40一样。创建子进程的时候调用函数setgpid(0,0)即可。
  • 当子进程从停止状态恢复时,父进程的sigchld_handler也会接受到信号,此时可能会导致一个bug产生,就是父进程会一直挂起等待恢复的子进程结束。

贴上代码

void eval(char *cmdline)
{char *argv[MAXLINE];/* param array */int bg = parseline(cmdline,argv);pid_t pid = 0;sigset_t mask,prev;sigemptyset(&mask);sigaddset(&mask,SIGCHLD);if(argv[0] == NULL)return;if(!builtin_cmd(argv)){if(access(argv[0],F_OK)){printf("文件不存在!\n");return;}sigprocmask(SIG_BLOCK,&mask,&prev);if((pid = fork())==0) // 子进程{sigprocmask(SIG_SETMASK,&prev,NULL);setpgid(0,0); // 将该子进程独立变成一个独立的进程if(!execv(argv[0],argv)){printf("输入的参数有误!!\n");_exit(0);}}else // 父进程{if(!bg){addjob(jobs,pid,FG,cmdline);sigprocmask(SIG_SETMASK,&prev,NULL);fg_flag = 1;waitfg(pid);}else{addjob(jobs,pid,BG,cmdline);struct job_t *job = getjobpid(jobs,pid);printf("[%d] (%d) %s\n",job->jid,pid,cmdline);sigprocmask(SIG_SETMASK,&prev,NULL);}}}return;
}int builtin_cmd(char **argv)
{if(!strcmp(argv[0],"quit"))exit(0);if(!strcmp(argv[0],"&"))return 1;if(!strcmp(argv[0],"jobs")){listjobs(jobs);return 1;}if(!strcmp(argv[0],"fg") || !strcmp(argv[0],"bg")){do_bgfg(argv);return 1;}return 0; /* not a builtin command */
}void do_bgfg(char **argv)
{struct job_t* job = NULL;if(!strcmp(argv[0],"bg")){if(argv[1] == NULL){printf("bg command requires PID or %%jobid argument\n");return ;}if(argv[1][0]=='%'){job = getjobjid(jobs,atoi(argv[1]+1));if(!job){printf("%s: No such job\n",argv[1]);return ;}}else{if(atoi(argv[1]) == 0){printf("bg: argument must be a PID or %%jobid\n");return;}job = getjobpid(jobs,atoi(argv[1]));if(!job){printf("(%s): No such process\n",argv[1]);return ;}}job->state = BG;printf("[%d] (%d) %s\n",job->jid,job->pid,job->cmdline);stopped_resume_child = job->pid;killpg(job->pid,SIGCONT);return;}if(!strcmp(argv[0],"fg")){if(argv[1] == NULL){printf("fg command requires PID or %%jobid argument\n");return;}if(argv[1][0]=='%'){job = getjobjid(jobs,atoi(argv[1]+1));if(!job){printf("%s: No such job\n",argv[1]);return ;}}else{if(atoi(argv[1]) == 0){printf("fg: argument must be a PID or %%jobid");return;}job = getjobpid(jobs,atoi(argv[1]));if(!job){printf("(%s): No such process\n",argv[1]);return ;}}if(job->state != ST){job->state = FG;fg_flag = 1;waitfg(job->pid);return;}if(job->state == ST){job->state = FG;fg_flag = 1;stopped_resume_child = job->pid;killpg(job->pid,SIGCONT);fg_flag = 1;waitfg(job->pid);return;}}return;
}void waitfg(pid_t pid)
{while (fg_flag){sleep(0);}return;
}void sigchld_handler(int sig)
{if(stopped_resume_child>0){stopped_resume_child = 0;return;}pid_t pid = 0;int status;struct job_t *job = NULL;if((pid = waitpid(-1,&status,WUNTRACED))>0){fg_flag = 0;/* 解开前台锁 */ if(WIFEXITED(status)) /* 正常退出时才可删除任务 */{deletejob(jobs,pid);}else if(WIFSIGNALED(status)){job = getjobpid(jobs,pid);printf("job [%d] (%d) terminated by signal 2\n",job->jid,pid);deletejob(jobs,pid);}else{job = getjobpid(jobs,pid);printf("job [%d] (%d) stopped by signal 20\n",job->jid,pid);}}return;
}void sigint_handler(int sig)
{pid_t pid = 0;if((pid = fgpid(jobs))==0){printf("找不到任何的前台任务!\n");return;}killpg(pid,SIGINT);return;
}void sigtstp_handler(int sig)
{pid_t pid = 0;struct job_t* job = NULL;if((pid = fgpid(jobs))==0){printf("找不到任何的前台任务!\n");return;}fg_flag = 1;job = getjobpid(jobs,pid);job->state = ST;killpg(pid,SIGTSTP);return;
}

参考博客
李秋豪

深入理解计算机系统(CSAPP) 实验详解:ShellLab相关推荐

  1. 深入理解计算机系统(CSAPP) 实验详解:CacheLab

    近一段时间项目太忙导致没有继续,还好最近空下来一些,咱们继续冲! 更新历史 20210104开始更新 20210107完成实验一内容 本文介绍的是CSAPP书籍中的第四个lab: Cache lab. ...

  2. 深入理解计算机系统(CSAPP) 实验详解:DataLab

    更新历史 20200911开始做这个实验 20200915更新文章-题目更新到isTmax 20200918更新文章-题目更新到isAsciiDigit 20200927更新文章-题目更新到isLes ...

  3. STM32F429第二十五篇之MCU屏实验详解

    文章目录 前言 硬件 软件 结构体 SRAM_HandleTypeDef Instance(FMC寄存器地址) Extended(拓展寄存器地址) Init(初始化变量) Lock(锁) State( ...

  4. CSAPP之详解Labs

    CSAPP-labs-RECORD 前言 读万卷书,不如行万里路.如果你读到了这本书,可千万不要错过本书的配套实验. 其中几个纵享盛名的实验,分别是: BombLab MollocLab ShellL ...

  5. 实验详解——Cobbler自动部署最小化安装

    实验详解--Cobbler自动部署最小化安装 一.实验:自动部署 二.Cobbler自动装机服务搭建步骤 1.导入epel源并加载在线安装源 2.安装Cobbler以及其相关服务软件包 3.修改cob ...

  6. 实验详解——DNS网关服务器的分离解析

    实验详解--DNS网关服务器的分离解析 一.实验图 二.要求 三.实验开始 1.网关服务器的配置 ①.新添加一块网卡,用双网卡来演示网关服务器的两个端口 ②.对两个网卡进行配置的修改 ③.重启网卡并查 ...

  7. 实验详解——DNS反向解析、DNS主服务器和从服务器的配置

    实验详解--DNS反向解析.DNS主服务器和从服务器的配置 实验一:DNS反向解析 1.安装bind 2.查找配置文件路径 3.配置/etc/named.conf主配置文件 4.修改/etc/name ...

  8. 详解FTP服务完成Linux和WIN10之间的信息传输(实验详解)

    详解FTP服务完成Linux和WIN10之间的信息传输(实验详解) 一.FTP简介 1. FTP服务--用来传输文件的协议 2.端口 3.数据连接模式 二.相关配置 1.安装FTP服务 2.设置匿名用 ...

  9. 详解 Linux环境中DHCP分配IP地址(实验详解)

    Linux中DHCP小实验详解 一.DHCP中继概述 二.DHCP在linux系统中的相关配置 1.配置DHCP服务器 2.设置全局配置参数 3.subnet网段声明 4.host主机声明 三.实验例 ...

  10. 实验详解——parted单磁盘分区并进行配额

    实验详解--parted单磁盘分区并进行配额 一.实验要求 二.实验开始 1.添加新硬盘 2.对新硬盘进行parted分区,格式设置为ext4 3.格式化分区,格式为ext4 4.设置配额方式和挂载 ...

最新文章

  1. nodejs linux 32位下载安装,Linux32位怎么装nodejs?
  2. oracle监听 客户 实例,oracle 数据库实例 监听
  3. 轨道角度分布图_高分五号:大气气溶胶多角度偏振探测仪
  4. vim 函数列表插件
  5. python实现学生成绩聚类分析_聚类分析Python实现
  6. JBox2d入门学习二 -----我的小鸟
  7. Mr.J-- jQuery学习笔记(六)--attrprop方法
  8. ShuffleNetV2论文译读笔记
  9. wget下载太慢问题
  10. androidQ(10.0) 预装集成apk到data分区
  11. 手机怎么看php格式的视频教程,wmv格式用手机怎么看
  12. 微信小程序的标签及常见样式
  13. [转帖]一些不该被埋没的好歌【全版】【2】
  14. 光明行动:共同呵护好孩子的眼睛——广西实施光明行动实地考察调研综述
  15. cpy几天爬出密道问题
  16. cuda 和 pytorch版本不一致torch.cuda.is_available()返回false——解决办法
  17. 跟我一起学PyTorch-07:嵌入与表征学习
  18. 锦上添花之元音音标学习(上)
  19. 绿蓝色悖论(新归纳之谜)?
  20. ASCII表完整版(包含16进制对应表)——看看16进制与10进制的转化

热门文章

  1. 计算机重装系统 英语,重装系统还看不懂BIOS?中英文详细对照表,进入BIOS如此简单...
  2. 如何优雅的快速下载谷歌云盘的大文件 (二)
  3. 软件体系结构风格整理
  4. VMware:虚拟机(xp)与主机(win10)连接步骤(超详细)
  5. asp.net网页版斗地主 已提供下载(开源) 测试版
  6. 免费中文版Adobe Acrobat Reader DC PDF文件阅读软件
  7. 厂商服务器存储默认管理口登录信息 默认IP、用户名、密码
  8. 微信浏览器视频自动播放
  9. MPC5744p时钟模块
  10. 如何使用离线网站,打开离线网站