实验报告

验(七)

题     目 TinyShell

微壳  

专       业 计算机类

学     号 xxxx

班     级 xxxx

学       生 xxxx

指 导 教 师 xxxx

实 验 地 点 xxxx

实 验 日 期 2019.12.4

计算机科学与技术学院

 

第1章 实验基本信息............................................................................................. - 4 -

1.1 实验目的......................................................................................................... - 4 -

1.2 实验环境与工具............................................................................................. - 4 -

1.2.1 硬件环境................................................................................................. - 4 -

1.2.2 软件环境................................................................................................. - 4 -

1.2.3 开发工具................................................................................................. - 4 -

1.3 实验预习......................................................................................................... - 4 -

第2章 实验预习..................................................................................................... - 6 -

2.1 进程的概念、创建和回收方法(5分)...................................................... - 6 -

2.2信号的机制、种类(5分).......................................................................... - 6 -

2.3 信号的发送方法、阻塞方法、处理程序的设置方法(5分).................. - 6 -

2.4 什么是shell,功能和处理流程(5分)................................................... - 6 -

第3章 TinyShell的设计与实现.................................................................... - 7 -

3.1.1 void eval(char *cmdline)函数(10分)................................................ - 7 -

3. 1.2 int builtin_cmd(char **argv)函数(5分).......................................... - 7 -

3. 1.3 void do_bgfg(char **argv) 函数(5分)............................................ - 7 -

3. 1.4 void waitfg(pid_t pid) 函数(5分)....................................................... - 7 -

3. 1.5 void sigchld_handler(int sig) 函数(10分)...................................... - 8 -

第4章 TinyShell测试.................................................................................... - 9 -

4.1 测试方法......................................................................................................... - 9 -

4.2 测试结果评价................................................................................................. - 9 -

4.3 自测试结果..................................................................................................... - 9 -

4.3.1测试用例trace01.txt的输出截图(1分)............................................ - 9 -

4.3.2测试用例trace02.txt的输出截图(1分)............................................ - 9 -

4.3.3测试用例trace03.txt的输出截图(1分).......................................... - 10 -

4.3.4测试用例trace04.txt的输出截图(1分).......................................... - 10 -

4.3.5测试用例trace05.txt的输出截图(1分).......................................... - 10 -

4.3.6测试用例trace06.txt的输出截图(1分).......................................... - 11 -

4.3.7测试用例trace07.txt的输出截图(1分).......................................... - 11 -

4.3.8测试用例trace08.txt的输出截图(1分).......................................... - 11 -

4.3.9测试用例trace09.txt的输出截图(1分).......................................... - 12 -

4.3.10测试用例trace10.txt的输出截图(1分)........................................ - 12 -

4.3.11测试用例trace11.txt的输出截图(1分)........................................ - 12 -

4.3.12测试用例trace12.txt的输出截图(1分)........................................ - 13 -

4.3.13测试用例trace13.txt的输出截图(1分)........................................ - 13 -

4.3.14测试用例trace14.txt的输出截图(1分)........................................ - 13 -

4.3.15测试用例trace15.txt的输出截图(1分)........................................ - 14 -

4.4 自测试评分................................................................................................... - 14 -

第4章 总结........................................................................................................... - 15 -

4.1 请总结本次实验的收获............................................................................... - 15 -

4.2 请给出对本次实验内容的建议................................................................... - 15 -

参考文献................................................................................................................. - 16 -

第1章 实验基本信息

1.1 实验目的

理解现代计算机系统进程与并发的基本知识 

掌握 linux 异常控制流和信号机制的基本原理和相关 系统函数 

掌握 shell 的基本原理和实现方法 

深入理解 Linux 信号响应可能导致的并发冲突及解决方法 

培养 Linux 下的软件系统开发与测试能力

1.2 实验环境与工具

1.2.1 硬件环境

Windows7 64 位以上;VirtualBox/Vmware 11 以上;Ubuntu 16.04 LTS 64 位/ 优麒麟 64 位

1.2.2 软件环境

Windows7 64 位以上;VirtualBox/Vmware 11 以上;1.2.3 开发工具

1.2.3 开发工具

填写……

1.3 实验预习

上实验课前,必须认真预习实验指导书(PPT 或 PDF) 

了解实验的目的、实验环境与软硬件工具、实验操作步骤,复习 与实验有关的理论知识。 

了解进程、作业、信号的基本概念和原理 

了解 shell 的基本原理

熟知进程创建、回收的方法和相关系统函数 熟知信号机制和信号处理相关的系统函数

第2章 实验预习

总分20

2.1 进程的概念、创建和回收方法(5分)

进程:一个执行程序中的实例,提供给我吗一种错觉:我们的程序好像是系统中当前运行的唯一程序,我们的程序独占使用处理器和内存,处理器好像是无间断的执行我们程序中的指令,我们程序的代码和数据好像是系统中内存唯一的对象。

创建进程:使用fork函数,父进程调用fork函数创建一个新的运行的子进程,子进程得到和父进程用户及虚拟地址空间完全相同的一个副本。

回收方法:(1)当一个进程由于某种原因终止时,进程保持在一种已经终止的状态,直到被他的父进程回收,当父进程回收他的子进程时,内核将子进程的退出状态传递给父进程,然后抛弃已经终止的进程,从此开始,该进程就不存在了。(2)如果一个父进程终止了,内核会安排init进程成为它的孤儿进程的养分,init进程将会回收这些孤儿进程。

2.2信号的机制、种类(5分)

信号的机制:一个信号就是一条消息,它通知进程系统中发生了一个某种类型的事件。每种信号类型都对应某种系统的事件。信号类型是用小整数ID来标识,每一个信号对应唯一的ID。

发送信号:内核通过更新目的进程上下文的某种状态,发送一个信号给目的进程。

接收信号:内核强迫目的进程以某种方式对信号的发送做出反应,则接收了这种信号。

信号的类型:

2.3 信号的发送方法、阻塞方法、处理程序的设置方法(5分)

1.信号的发送方法:

  1. 用/bin/kill程序发送信号,/bin/kill程序可以向另外的进程发送任意的信号
  2. 从键盘发送信号,在键盘上输入Ctrl+C会导致内核发送一个SIGINT信号到前台进程中的每个进程,默认情况下是终止前台作业
  3. 用kill函数发送信号给其他进程(包括他们自己)
  4. 使用alarm函数发送信号,进程可以通过调用 alarm 函数在指定 secs 秒后发送一个 SIGALRM 信号给调用进程。
  1. 信号的阻塞方法:

隐式阻塞机制:内核默认阻塞任何当前处理程序正在处理信号类 和待处理信号。

显示阻塞进制:应用程序可以调用 sigprocmask 函数和它的辅助函 数,明确地阻塞和解除阻塞选定的信号。调用要求如下:

3.处理程序的设置方法:

  1. 调用 signal 函数,调用 signal(SIG,handler),SIG 代表信号类型,handler 代表接收到 SIG 信号之后对应的处理程序。
  2. 因为 signal 的语义各有不同,所以我们需要一个可移植的信号处理函 数设置方法,Posix 标准定义了 sigaction 函数,它允许用户在设置信号

处理时明确指定他们想要的信号处理语义,调用如下:

2.4 什么是shell,功能和处理流程(5分)

shell:shell是一个用c语言编写的程序,它是用户使用Linux的桥梁,shell既是一种命令语言,又是一种程序设计语言,shell是一种应用程序。

功能:shell应用程序提供了一个界面,用户通过访问这个界面访问操作系统内 核的服务。

处理流程: 1)从终端读入输入的命令。

2)将输入字符串切分获得所有的参数

3)如果是内置命令则立即执行

4)否则调用相应的程序执行

5)shell 应该接受键盘输入信号,并对这些信号进行相应处理

第3章 TinyShell的设计与实现

总分45

3.1 设计

3.1.1 void eval(char *cmdline)函数(10分)

函数功能:对用户输入的参数进行解析并运行计算。如果用户输入内建的命令行(quit,bg,fg,jobs)那么立即执行。 否则,fork一个新的子进程并且将该任务在子进程的上下文中运行。如果该任务是前台任务那么需要等到它运行结束才返回。

参    :Char* cmdline

处理流程

  1. 首先调用 parseline 函数。将 cmdline 字符串切分为参数数组 argv,另外 parseline 函数返回 bg,可以得知该操作是否需要后台运行。
  2. 调用 buildin_cmd 判断该函数是否为内置函数。如果是内置函数则立即执 行。
  3. 如果不是内置函数,创建一个子进程,然后在子进程的上下文中运行命令 行要求运行的函数(调用 execve 函数)。

要点分析

  1. 如何创建子进程并运行命令:首先将 SIGCHLD、SIGINT、SIGSTP 信号 阻塞,因为在这些信号的信号处理函数中我们将使用到 job 相关的函数, 这些函数会对全局变量工作列表 jobs 进行操作,而我们接下来在父进程中 还要 addjob 将 job 加入的 jobs 中,所以为了避免子进程与信号处理函数竞 争 jobs,我们需要使用 sigprocmask 阻塞以上信号。

2) 父进程创建完成子进程并用 addjob 记录后,需要用 sigprocmask 解除阻塞。

3) 子进程从父进程继承了信号阻塞向量,所以子进程必须确保在执行新程序 之前接触对信号的阻塞。

4) Ctr-c 会给所有前台进程组中的进程发送 SIGINT 信号,包括 tsh 和 tsh 创建 的进程,但是这样使不正确的。所以我们需要在子进程中,execve 之前, 调用函数 setpgid(0,0)将子进程加入到一个 pgid=pid 的进程组之中。

5).整个过程中,需要仔细考虑显式阻塞的安排和设计,什么地方需要阻塞哪些信号。

原则1:在访问全局变量(jobs)必须阻塞所有信号,包括调用老师给的那些函数。由于这些函数基本都是使用for循环遍历完成功能的,所以,务必保证函数执行中不能被中断。

原则2:在一些函数或者指令有必须的前后执行顺序时,请阻塞,保证前一个函数调用完成后(比如必须先addjob,再deletejob)

3. 1.2 int builtin_cmd(char **argv)函数(5分)

函数功能:判断命令是否是内置指令,是的话立即执行,不是则返回,对单独的‘&’无视

参    数:char **argv

处理流程:依次判断用户输入的命令名称 argv 是否是

  1. 内置函数”quit” ,如果是则调用 exit(0)退出 tsh。

2) 内置函数”bg” | “fg”,如果是则调用函数 do_fgbg。返回值 1,tsh 继续。

3)内置函数”jobs”,如果是则调用 listjobs 列出所有的 job 信息。返回值 1, tsh 继续。

4) 如果以上都不是,则不是内置函数,返回 0,tsh 会将它作为一个 job 处理。

要点分析

分别对三种不同的情况调用相应的处理函数。填对参数。了解三种内 置函数的意义,注意只有 quit 是需要 exit(0)的,其他两种情况都需要返回 1,这是 tsh 会继续自己的处理流程。

3. 1.3 void do_bgfg(char **argv) 函数(5分)

函数功能:实现内置命令bg 和 fg.

参    数:char **argv

处理流程:

  1. 首先对传入的命令行输入进行解析。首先 bg fg的调用有两种格式,对 这两种格式分别判断然后读入输入的 job pid,通过 pid 调用 getjobpid 获得 job。
  2. 如果是 bg 命令,向 job 所在的进程组发送 SIGCONT 信号,更改 job 的 state 为 BG。

3) 如果是 fg 命令, 向 job 所在的进程组发送 SIGCONT 信号,更改 job 的 state 为 FG,然后等待当前的程序运行直到当前的 job 不再是前台程 序。

要点分析:

1. 区分bg和fg命令,以及传入pid或者jid参数对应的进程的状态。前者if,后者switch就可以包括所用的情况

2. 注意用户输入错误处理,比如参数数量不够或者参数传入错误的情况

3. 对例外进行报错,分别有:命令为空,找不到 PID 的 job,不符合 bgfg 的格式,命令不是 bgfg。

4.按照 bgfg 命令的特性,我们需要向目标 job 所在的进程组发送 SIGCONT 信号,代表如果该进程组中的进程停止,则需要重新进行。

5.在 fg 命中,我们是要将目标 job 放到前台运行,因此我们需要调用 waitfg 阻塞 tsh 进行,使前台一直都是该 job,直到该 job 不再是前台进 程。触发 job 退出前台的条件是 job 结束运行,从而退出子进程使父进 程调用 SIGCHLD 的处理函数,处理函数中对 job 进行了回收,此时前 台程序不再是 job

3. 1.4 void waitfg(pid_t pid) 函数(5分)

函数功能:阻塞直到指定 pid 的进程不再是前台进程。

参    数:pid_t pid

处理流程:

进行 while 循环,每次 循环 sleep 1 秒,while 的终止条件是前台程序的 PID 不再是 pid。

要点分析:

调用 fgpid 函数向 jobs 查询当前 state 是 FG 的 job 的 PID。

3. 1.5 void sigchld_handler(int sig) 函数(10分)

函数功能:父进程中接收到 SIGCHLD 信号的处理函数。

参    数:int sig  处

处理流程:

1) 保存 errno

  1. 处理所有子进程集合中已经停止或终止的子进程。
  2. 3) 如果该子进程通过调用 exit 或者一个返回正常终止,则阻塞信号,删 除 job,恢复信号。
  3. 如果该子进程当前已经停止,向屏幕打印信息。
  4. 如果该当前子进程是因为一个未被捕获的信号终止的,则向屏幕打印 信息,阻塞信号,删除 job,恢复信号。

6) 恢复 errno

要点分析:

(1)如何处理所有的子进程:利用 while 循环重复判断,判断条件是:      while ((child_pid = waitpid(-1, &status, WNOHANG|WUNTRACED)) > 0) ,其 中waitpid中的option设置为WNOHANG|WUNTRACED的       含义是立即 返回,如果等待集合中的子进程都没有停止或终止的则返  回为 0,如果 有一个,则返回该进程的 PID。

2) 如何判断子进程不同的返回状态:通过 waitpid 传入的 status,分别调 用 WIFEXITED,WIFSTOPPED,WIFSIGNALED,如果为真,则分别 代表的返回情况是:子进程通过调用 exit 或者一个返回正常终止、该 子进程当前已经停止、该当前子进程是因为一个未被捕获的信号终止 的。之所以判断是因为我们需要对这三种不同的情况进行不同的处理。

3) 因为在信号处理函数中可能会调用修改 errno 的函数,所以我们   需要保 存恢复 errono。

3.2 程序实现(tsh.c的全部内容)(10分)

重点检查代码风格:

  1. 用较好的代码注释说明——5
  2. 检查每个系统调用的返回值——5

tsh.c(如下所示)

/** tsh - A tiny shell program with job control*1180800811张瑞豪* <Put your name and login ID here>*/#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <ctype.h>#include <signal.h>#include <sys/types.h>#include <sys/wait.h>#include <errno.h>/* Misc manifest constants */#define MAXLINE    1024   /* max line size */#define MAXARGS     128   /* max args on a command line */#define MAXJOBS      16   /* max jobs at any point in time */#define MAXJID    1<<16   /* max job ID *//* Job states */#define UNDEF 0 /* undefined */#define FG 1    /* running in foreground */#define BG 2    /* running in background */#define ST 3    /* stopped *//** Jobs states: FG (foreground), BG (background), ST (stopped)* Job state transitions and enabling actions:*     FG -> ST  : ctrl-z*     ST -> FG  : fg command*     ST -> BG  : bg command*     BG -> FG  : fg command* At most 1 job can be in the FG state.*//* Global variables */extern char **environ;      /* defined in libc */char prompt[] = "tsh> ";    /* command line prompt (DO NOT CHANGE) */int verbose = 0;            /* if true, print additional output */int nextjid = 1;            /* next job ID to allocate */char sbuf[MAXLINE];         /* for composing sprintf messages */struct job_t {              /* The job struct */pid_t pid;              /* job PID */int jid;                /* job ID [1, 2, ...] */int state;              /* UNDEF, BG, FG, or ST */char cmdline[MAXLINE];  /* command line */};struct job_t jobs[MAXJOBS]; /* The job list *//* End global variables *//* Function prototypes *//* Here are the functions that you will implement */void eval(char *cmdline);int builtin_cmd(char **argv);void do_bgfg(char **argv);void waitfg(pid_t pid);void sigchld_handler(int sig);void sigtstp_handler(int sig);void sigint_handler(int sig);/* Here are helper routines that we've provided for you */int parseline(const char *cmdline, char **argv);void sigquit_handler(int sig);void clearjob(struct job_t *job);void initjobs(struct job_t *jobs);int maxjid(struct job_t *jobs);int addjob(struct job_t *jobs, pid_t pid, int state, char *cmdline);int deletejob(struct job_t *jobs, pid_t pid);pid_t fgpid(struct job_t *jobs);struct job_t *getjobpid(struct job_t *jobs, pid_t pid);struct job_t *getjobjid(struct job_t *jobs, int jid);int pid2jid(pid_t pid);void listjobs(struct job_t *jobs);void usage(void);void unix_error(char *msg);void app_error(char *msg);typedef void handler_t(int);handler_t *Signal(int signum, handler_t *handler);/** main - The shell's main routine*//** shell的执行main函数*/int main(int argc, char **argv){char c;char cmdline[MAXLINE];int emit_prompt = 1; /* emit prompt (default) *//*将stderr重定位到stdout*/dup2(1, 2);/* 解析命令行 讨论命令参数*/while ((c = getopt(argc, argv, "hvp")) != EOF) {switch (c) {case 'h':             /*打印帮助信息*/usage();break;case 'v':             /* 打印格外调试信息*/verbose = 1;break;case 'p':             /* 不打印prompt */emit_prompt = 0;break;default:usage();}}/* 注册自己编写的信号处理函数 */Signal(SIGINT,  sigint_handler);   /* ctrl-c */Signal(SIGTSTP, sigtstp_handler);  /* ctrl-z */Signal(SIGCHLD, sigchld_handler);  /* 终止 或者 子进程返回 *//*干净地杀死terminal*/Signal(SIGQUIT, sigquit_handler);/*初始化工作列表*/initjobs(jobs);/* 执行死循环流程*/while (1) {/* 读入命令行 */if (emit_prompt) {printf("%s", prompt);fflush(stdout);}if ((fgets(cmdline, MAXLINE, stdin) == NULL) && ferror(stdin))app_error("fgets error");if (feof(stdin)) { /* End of file (ctrl-d) */fflush(stdout);exit(0);}/* 处理命令行 */eval(cmdline);fflush(stdout);fflush(stdout);}exit(0); /* control never reaches here */}/** eval - Evaluate the command line that the user has just typed in** If the user has requested a built-in command (quit, jobs, bg or fg)* then execute it immediately. Otherwise, fork a child process and* run the job in the context of the child. If the job is running in* the foreground, wait for it to terminate and then return.  Note:* each child process must have a unique process group ID so that our* background children don't receive SIGINT (SIGTSTP) from the kernel* when we type ctrl-c (ctrl-z) at the keyboard.*//** eval函数是用来处理命令行输入的主要逻辑,所有的处理执行逻辑在这里实现*/void eval(char *cmdline){/* $begin handout */char *argv[MAXARGS]; /* argv for execve() */int bg;              /* should the job run in bg or fg? */pid_t pid;           /* process id */sigset_t mask;       /* signal mask *//*解析命令行*/bg = parseline(cmdline, argv);if (argv[0] == NULL)return;   /* ignore empty lines */if (!builtin_cmd(argv)) {/* 将SIGCHLD SIGINT SIGSTP的信号阻塞 因为在这些处理程序中都是用了job相关的函数* 也就是同时调用了全局变量jobs 为了避免竞争所以对这些信号进行阻断* 同时需要注意 需要输出错误信息*/if (sigemptyset(&mask) < 0)unix_error("sigemptyset error");if (sigaddset(&mask, SIGCHLD))unix_error("sigaddset error");if (sigaddset(&mask, SIGINT))unix_error("sigaddset error");if (sigaddset(&mask, SIGTSTP))unix_error("sigaddset error");if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0)unix_error("sigprocmask error");/* 创建一个子进程 */if ((pid = fork()) < 0)unix_error("fork error");/** 子进程中的执行逻辑 用pid=0来区分*/if (pid == 0) {/* 子进程 解锁信号 */sigprocmask(SIG_UNBLOCK, &mask, NULL);/* 对每一个子进程开一个单独的进程组 用setpid(0,0)来实现 */if (setpgid(0, 0) < 0)unix_error("setpgid error");/* 执行新的程序 */if (execve(argv[0], argv, environ) < 0) {printf("%s: Command not found\n", argv[0]);exit(0);}}/** 父进程*//* 为了解决父进程与子进程之前存在的race问题,上面设置了信号的封锁,* 这里在添加了job之后需要对上面封锁的信号进行解锁*/addjob(jobs, pid, (bg == 1 ? BG : FG), cmdline);sigprocmask(SIG_UNBLOCK, &mask, NULL);if (!bg)waitfg(pid);elseprintf("[%d] (%d) %s", pid2jid(pid), pid, cmdline);}/* $end handout */return;}/** parseline - Parse the command line and build the argv array.** Characters enclosed in single quotes are treated as a single* argument.  Return true if the user has requested a BG job, false if* the user has requested a FG job.*//* 解析命令行 */int parseline(const char *cmdline, char **argv){static char array[MAXLINE]; /* holds local copy of command line */char *buf = array;          /* ptr that traverses command line */char *delim;                /* points to first space delimiter */int argc;                   /* number of args */int bg;                     /* background job? */strcpy(buf, cmdline);buf[strlen(buf)-1] = ' ';  /* replace trailing '\n' with space */while (*buf && (*buf == ' ')) /* ignore leading spaces */buf++;/* Build the argv list */argc = 0;if (*buf == '\'') {buf++;delim = strchr(buf, '\'');}else {delim = strchr(buf, ' ');}while (delim) {argv[argc++] = buf;*delim = '\0';buf = delim + 1;while (*buf && (*buf == ' ')) /* ignore spaces */buf++;if (*buf == '\'') {buf++;delim = strchr(buf, '\'');}else {delim = strchr(buf, ' ');}}argv[argc] = NULL;if (argc == 0)  /* ignore blank line */return 1;/* should the job run in the background? */if ((bg = (*argv[argc-1] == '&')) != 0) {argv[--argc] = NULL;}return bg;}/** builtin_cmd - If the user has typed a built-in command then execute*    it immediately.*/int builtin_cmd(char **argv){/** 判断是否是内置函数* 如果是内置函数 则执行 返回1* 如果不是内置函数 返回0*/if(!strcmp(argv[0],"quit")){    //quit 退出tshexit(0);}if(!strcmp(argv[0],"&")){//忽略单独的&return 1;}if(!strcmp(argv[0],"bg") || (!strcmp(argv[0],"fg"))) {  //fg bg任务显示,调用相应函数即可do_bgfg(argv);return 1;}if(!strcmp(argv[0],"jobs")) {     //列出所有任务,调用 listjobs 即可listjobs(jobs);return 1;}return 0;     /* not a builtin command */}/** do_bgfg - Execute the builtin bg and fg commands*/void do_bgfg(char **argv){/* 执行内置命令bg fg *//* $begin handout */struct job_t *jobp=NULL;/* Ignore command if no argument */if (argv[1] == NULL) {printf("%s command requires PID or %%jobid argument\n", argv[0]);return;}/* Parse the required PID or %JID arg */if (isdigit(argv[1][0])) {  //开头是数字的格式pid_t pid = atoi(argv[1]);      //数字转换函数 可以跳过前面的空格,转换负数if (!(jobp = getjobpid(jobs, pid))) {printf("(%d): No such process\n", pid);return;}}else if (argv[1][0] == '%') {   //开头是%的格式int jid = atoi(&argv[1][1]);    //传入的是地址 直接取地址if (!(jobp = getjobjid(jobs, jid))) {printf("%s: No such job\n", argv[1]);return;}}else {printf("%s: argument must be a PID or %%jobid\n", argv[0]);return;}/* bg command */if (!strcmp(argv[0], "bg")) {if (kill(-(jobp->pid), SIGCONT) < 0)        //向job所在的进程组发送SIGCONT信号unix_error("kill (bg) error");jobp->state = BG;                           //更改state为BGprintf("[%d] (%d) %s", jobp->jid, jobp->pid, jobp->cmdline);}/* fg command */else if (!strcmp(argv[0], "fg")) {if (kill(-(jobp->pid), SIGCONT) < 0)        //向job所在的进程组发送SIGCONT信号unix_error("kill (fg) error");jobp->state = FG;                           //更改state为FGwaitfg(jobp->pid);                          //调用waitfg等待job,直到前台进程不是job}else {printf("do_bgfg: Internal error\n");exit(0);}/* $end handout */return;}/** waitfg - Block until process pid is no longer the foreground process*/void waitfg(pid_t pid){while(fgpid(jobs)==pid) {       //busy_loop 等待前台程序不再是pidsleep(1);}}/****************** Signal handlers*****************//** sigchld_handler - The kernel sends a SIGCHLD to the shell whenever*     a child job terminates (becomes a zombie), or 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.*/void sigchld_handler(int sig){/** SIGCHLD 信号处理程序*/int olderrno = errno;       //记录下errno 防止在信号处理过程中 调用函数 出错 产生errno变化int status;sigset_t mask_all, prev_all;pid_t pid;sigfillset(&mask_all);while((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {  //while循环 回收所有僵死进程//对导致退出的情况进行屏幕回显// WHOANG|WUNTRACED 代表立即返回,如果等待集合中的子进程都没有停止或终止则返回为0,如果有一个,则返回他的PIDif (WIFEXITED(status)) {        //子进程通过调用exit或者一个返回正常终止sigprocmask(SIG_BLOCK, &mask_all, &prev_all);       //阻塞所有的信号deletejob(jobs, pid);                             //在任务列表中删除任务sigprocmask(SIG_SETMASK, &prev_all, NULL);          //解除阻塞}if (WIFSTOPPED(status)) {      //引起返回的子进程当前是停止的printf("Job [%d] (%d) stopped by signal %d\n", pid2jid(pid), pid, WSTOPSIG(status));}if (WIFSIGNALED(status)) {      //子进程是因为一个未被捕获的信号终止的printf("Job [%d] (%d) terminated by signal %d\n", pid2jid(pid), pid, WTERMSIG(status));sigprocmask(SIG_BLOCK, &mask_all, &prev_all);     //在任务列表中删除任务deletejob(jobs, pid);sigprocmask(SIG_SETMASK, &prev_all, NULL);}}errno = olderrno;       //恢复errno}/** 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){/*中断信号SIGINT处理函数*/int olderrno = errno;       //保存errnopid_t pid = fgpid(jobs);if (pid == 0) {return;}kill(-pid, SIGINT);       //向处于foreground正在运行的进程所处的进程组发送SIGINTerrno = olderrno;}/** sigtstp_handler - The kernel sends a SIGTSTP to the shell whenever*     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) {//    puts("handle sigstop");/*SIGSTP信号的处理函数*/pid_t pid = fgpid(jobs);if(pid == 0) return ;getjobpid(jobs, pid)->state = ST;       //设置state为ST//  Job [2] (29481) stopped by signal 20kill(-pid,SIGTSTP);printf("Job [%d] (%d) stopped by signal %d\n", pid2jid(pid), pid, sig);return;}/********************** End signal handlers*********************//************************************************ Helper routines that manipulate the job list**********************************************//* clearjob - Clear the entries in a job struct */void clearjob(struct job_t *job) {job->pid = 0;job->jid = 0;job->state = UNDEF;job->cmdline[0] = '\0';}/* initjobs - Initialize the job list */void initjobs(struct job_t *jobs) {int i;for (i = 0; i < MAXJOBS; i++)clearjob(&jobs[i]);}/* maxjid - Returns largest allocated job ID */int maxjid(struct job_t *jobs){int i, max=0;for (i = 0; i < MAXJOBS; i++)if (jobs[i].jid > max)max = jobs[i].jid;return max;}/* addjob - Add a job to the job list */int addjob(struct job_t *jobs, pid_t pid, int state, char *cmdline){int i;if (pid < 1)return 0;for (i = 0; i < MAXJOBS; i++) {if (jobs[i].pid == 0) {jobs[i].pid = pid;jobs[i].state = state;jobs[i].jid = nextjid++;if (nextjid > MAXJOBS)nextjid = 1;strcpy(jobs[i].cmdline, cmdline);if(verbose){printf("Added job [%d] %d %s\n", jobs[i].jid, jobs[i].pid, jobs[i].cmdline);}return 1;}}printf("Tried to create too many jobs\n");return 0;}/* deletejob - Delete a job whose PID=pid from the job list */int deletejob(struct job_t *jobs, pid_t pid){int i;if (pid < 1)return 0;for (i = 0; i < MAXJOBS; i++) {if (jobs[i].pid == pid) {clearjob(&jobs[i]);nextjid = maxjid(jobs)+1;return 1;}}return 0;}/* fgpid - Return PID of current foreground job, 0 if no such job */pid_t fgpid(struct job_t *jobs) {int i;for (i = 0; i < MAXJOBS; i++)if (jobs[i].state == FG)return jobs[i].pid;return 0;}/* getjobpid  - Find a job (by PID) on the job list */struct job_t *getjobpid(struct job_t *jobs, pid_t pid) {int i;if (pid < 1)return NULL;for (i = 0; i < MAXJOBS; i++)if (jobs[i].pid == pid)return &jobs[i];return NULL;}/* getjobjid  - Find a job (by JID) on the job list */struct job_t *getjobjid(struct job_t *jobs, int jid){int i;if (jid < 1)return NULL;for (i = 0; i < MAXJOBS; i++)if (jobs[i].jid == jid)return &jobs[i];return NULL;}/* pid2jid - Map process ID to job ID */int pid2jid(pid_t pid){int i;if (pid < 1)return 0;for (i = 0; i < MAXJOBS; i++)if (jobs[i].pid == pid) {return jobs[i].jid;}return 0;}/* listjobs - Print the job list */void listjobs(struct job_t *jobs){int i;for (i = 0; i < MAXJOBS; i++) {if (jobs[i].pid != 0) {printf("[%d] (%d) ", jobs[i].jid, jobs[i].pid);switch (jobs[i].state) {case BG:printf("Running ");break;case FG:printf("Foreground ");break;case ST:printf("Stopped ");break;default:printf("listjobs: Internal error: job[%d].state=%d ",i, jobs[i].state);}printf("%s", jobs[i].cmdline);}}}/******************************* end job list helper routines******************************//************************ Other helper routines***********************//** usage - print a help message*/void usage(void){printf("Usage: shell [-hvp]\n");printf("   -h   print this message\n");printf("   -v   print additional diagnostic information\n");printf("   -p   do not emit a command prompt\n");exit(1);}/** unix_error - unix-style error routine*/void unix_error(char *msg){fprintf(stdout, "%s: %s\n", msg, strerror(errno));exit(1);}/** app_error - application-style error routine*/void app_error(char *msg){fprintf(stdout, "%s\n", msg);exit(1);}/** Signal - wrapper for the sigaction function*//* 给出的通过sigaction进行的包装 完成信号注册的功能 */handler_t *Signal(int signum, handler_t *handler){struct sigaction action, old_action;action.sa_handler = handler;sigemptyset(&action.sa_mask); /* block sigs of type being handled */action.sa_flags = SA_RESTART; /* restart syscalls if possible */if (sigaction(signum, &action, &old_action) < 0)unix_error("Signal error");return (old_action.sa_handler);}/** sigquit_handler - The driver program can gracefully terminate the*    child shell by sending it a SIGQUIT signal.*/void sigquit_handler(int sig){printf("Terminating after receipt of SIGQUIT signal\n");exit(1);}

第4章 TinyShell测试

总分15

4.1 测试方法

针对tsh和参考shell程序tshref,完成测试项目4.1-4.15的对比测试,并将测试结果截图或者通过重定向保存到文本文件(例如:./sdriver.pl -t trace01.txt -s ./tsh -a "-p" > tshresult01.txt)。

4.2 测试结果评价

tsh与tshref的输出在一下两个方面可以不同:

(1)PID

(2)测试文件trace11.txt, trace12.txt和trace13.txt中的/bin/ps命令,每次运行的输出都会不同,但每个mysplit进程的运行状态应该相同。

除了上述两方面允许的差异,tsh与tshref的输出相同则判为正确,如不同则给出原因分析。

4.3 自测试结果

4.3.1测试用例trace01.txt的输出截图(1分)

tsh测试结果

tshref测试结果

测试结论

相同

4.3.2测试用例trace02.txt的输出截图(1分)

tsh测试结果

tshref测试结果

测试结论

相同

4.3.3测试用例trace03.txt的输出截图(1分)

tsh测试结果

tshref测试结果

测试结论

相同

4.3.4测试用例trace04.txt的输出截图(1分)

tsh测试结果

tshref测试结果

测试结论

相同

4.3.5测试用例trace05.txt的输出截图(1分)

tsh测试结果

tshref测试结果

测试结论

相同

4.3.6测试用例trace06.txt的输出截图(1分)

tsh测试结果

tshref测试结果

测试结论

相同

4.3.7测试用例trace07.txt的输出截图(1分)

tsh测试结果

tshref测试结果

测试结论

相同

4.3.8测试用例trace08.txt的输出截图(1分)

tsh测试结果

tshref测试结果

测试结论

相同

4.3.9测试用例trace09.txt的输出截图(1分)

tsh测试结果

tshref测试结果

测试结论

相同

4.3.10测试用例trace10.txt的输出截图(1分)

tsh测试结果

tshref测试结果

测试结论

相同

4.3.11测试用例trace11.txt的输出截图(1分)

tsh测试结果

tshref测试结果

测试结论

相同

4.3.12测试用例trace12.txt的输出截图(1分)

tsh测试结果

tshref测试结果

测试结论

相同

     

4.3.13测试用例trace13.txt的输出截图(1分)

tsh测试结果

tshref测试结果

测试结论

相同

4.3.14测试用例trace14.txt的输出截图(1分)

tsh测试结果

tshref测试结果

测试结论

相同

4.3.15测试用例trace15.txt的输出截图(1分)

tsh测试结果

tshref测试结果

测试结论

相同

4.4 自测试评分

根据节4.3的自测试结果,程序的测试评分为: 15

第4章 总结

4.1 请总结本次实验的收获

*了解了信号的处理机制,对一些信号的函数有更深的了解

*了解了shell对命令行的处理机制

4.2 请给出对本次实验内容的建议

注:本章为酌情加分项。

参考文献

为完成本次实验你翻阅的书籍与网站等

[1]  林来兴. 空间控制技术[M]. 北京:中国宇航出版社,1992:25-42.

[2]  辛希孟. 信息技术与信息服务国际研讨会论文集:A集[C]. 北京:中国科学出版社,1999.

[3]  赵耀东. 新时代的工业工程师[M/OL]. 台北:天下文化出版社,1998 [1998-09-26]. http://www.ie.nthu.edu.tw/info/ie.newie.htm(Big5).

[4]  谌颖. 空间交会控制理论与方法研究[D]. 哈尔滨:哈尔滨工业大学,1992:8-13.

[5]  KANAMORI H. Shaking Without Quaking[J]. Science,1998,279(5359):2063-2064.

[6]  CHRISTINE M. Plant Physiology: Plant Biology in the Genome Era[J/OL]. Science,1998,281:331-332[1998-09-23]. http://www.sciencemag.org/cgi/ collection/anatmorp.

哈工大计算机系统实验七——微壳相关推荐

  1. 哈工大计算机系统实验二——DataLab数据表示

    计算机系统实验二特别难,和上一届的实验不一样,没有学长的火炬,当时做的时候特别崩溃.幸好有一帮志同道合的伙伴们,一起慢慢把实验解决了. 把火炬传下去! 实验报告 实 验(二) 题     目 Data ...

  2. 哈工大计算机系统实验一:计算机系统漫游

    计算机系统实验一:计算机系统漫游 寒假直接copy文档上传,有错误很正常(有对不上的地方请私聊我改正一下) 把火炬传下去把! 实验报告 实 验(一) 题     目 计算机系统漫游 专       业 ...

  3. 哈工大计算机系统实验六——高速缓冲器模拟

    实验报告 实 验(六) 题     目 Cachelab 高速缓冲器模拟 专       业 xxxx 学    号 xxxx 班    级 xxxx 学       生 xxxx 指 导 教 师 x ...

  4. 哈工大计算机系统实验四——链接

    链接这一块呢,先看了李春凤老师的慕课,然后看一遍课本,然后来做实验,感觉真的懂了一样 把火炬传下去! 实验报告 实 验(四 题     目 LinkLab 链接 专       业 xxxx 学    ...

  5. 哈工大计算机系统lab7——微壳

          实验报告 实 验(七) 题     目 TinyShell 微壳 专       业 计算机类 学    号 ********** 班    级 1903003 学       生 李*涵 ...

  6. 计算机组成实验脱机,计算机组成原理 实验七 脱机方式下微代码装入与执行实验.doc...

    计算机组成原理 实验七 脱机方式下微代码装入与执行实验 实验七 脱机方式下微代码装入与执行实验 一.实验目的 通过微程序的编制.装入.执行,验证微程序控制的工作方法. 观察微程序的运行过程,未进行简单 ...

  7. 计算机组成原理微代码的修改,计算机组成原理实验七脱机方式下微代码装入与执行实验...

    <计算机组成原理实验七脱机方式下微代码装入与执行实验>由会员分享,可在线阅读,更多相关<计算机组成原理实验七脱机方式下微代码装入与执行实验(6页珍藏版)>请在人人文库网上搜索. ...

  8. 计算机组成原理微代码的修改,计算机组成原理实验七脱机方式下微代码装入与执行实验.doc...

    文档介绍: 计算机组成原理--实验七--脱机方式下微代码装入与执行实验--------------------------------作者:------------------------------ ...

  9. 操作系统实验七 地址映射与共享(哈工大李治军)

    实验七 地址映射与共享 实验目的 深入理解操作系统的段.页式内存管理,深入理解段表.页表.逻辑地址.线性地址.物理地址等概念: 实践段.页式内存管理的地址映射过程: 编程实现段.页式内存管理上的内存共 ...

最新文章

  1. java循环队列_java实现循环队列(顺序队列)
  2. php函数表达式,正规表达式函数_php
  3. c语言 函数的参数传递示例_C语言中带有示例的remove()函数
  4. API的非向后兼容性无论如何通常代表着一种比较差的设计
  5. 【SpringMVC框架】springmvc的基础知识
  6. mysql 的标识符_MySQL查询或标识符在Jupyter中太长?
  7. Tab与TabHost
  8. springboot启动命令linux,springboot项目命linux环境下命令启动
  9. Android Sensor传感器系统架构初探
  10. 浅析如何让Spring 来管理Action的配置方法
  11. php单列模式_PHP单例模式详解及实例代码
  12. Qt_ERROR qt闪退 竟然是因为pro文件中的冒出来的\n
  13. 所谓厉害的人,遇到问题时的思维模式与我们的差别在哪?(转自知乎)
  14. 找到某个关键字 同义词词林 python_读一读我——无废话Python(三)条件、循环、函数、表达式
  15. 计算机二级excel常见函数函数多表求和,excel sumif函数多条件求和 sumif的高级用法:跨多表条件求和...
  16. 网页title如何优化
  17. Tomcat日志切割工具 logrotate
  18. 机器人设计的步骤有哪些
  19. html 的页眉页脚,html – 带滚动条的页眉,页脚和内容的CSS布局
  20. java 房贷计算器代码_用JAVA编程一个房贷计算器

热门文章

  1. WPF Border 设置只加一边
  2. 得了糖尿病,千万不能这么做
  3. 2014年最新web前端开发面试题
  4. 一个不成熟的模板引擎思路
  5. [游戏技术]L4D1支持L4D2官方地图运行方法
  6. ScyllaDB安装
  7. MATLAB提取矩阵一部分
  8. 王川: 深度学习有多深, 学了究竟有几分?
  9. 【生成PDF】Java如何根据前台Echarts图表生成PDF,并下载
  10. 法规标准-UN R157标准解读