/* Install the signal handlers */ // 对各种信号进行处理 /* These are the ones you will need to implement */ Signal(SIGINT, sigint_handler); /* ctrl-c */ Signal(SIGTSTP, sigtstp_handler); /* ctrl-z */ Signal(SIGCHLD, sigchld_handler); /* Terminated or stopped child */
/* This one provides a clean way to kill the shell */ Signal(SIGQUIT, sigquit_handler);
/* Initialize the job list */ // 初始化作业列表 initjobs(jobs);
/* Execute the shell's read/eval loop */ while (1) {
/* Read command line */ // 打印一个prompt符 if (emit_prompt) { printf("%s", prompt); fflush(stdout); } // 从标准获得命令行 if ((fgets(cmdline, MAXLINE, stdin) == NULL) && ferror(stdin)) app_error("fgets error"); // EOF: 当输入ctrl-d的时候表示标准输入(文件)结束,此时直接退出 if (feof(stdin)) { /* End of file (ctrl-d) */ fflush(stdout); exit(0); }
/* Evaluate the command line */ eval(cmdline); // 解析命令行 fflush(stdout); fflush(stdout); }
/* * 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. */
这个函数主要主要功能就是解析命令行字符串,然后建立argv参数数组,用来给后面execve使用等。
所以代码实现起来就是如下
1 2 3 4 5 6 7 8 9 10 11 12
voideval(char *cmdline) { // 参数字符串数组 char *argv[MAXARGS]; /* argv for execve() */ int bg; /* should the job run in bg or fg? */ /* parse command line */ // 解析cmdline获得argv bg = parseline(cmdline, argv); if (!builtin_cmd(argv)) { ... }
1 2 3 4 5 6 7 8 9
/* * builtin_cmd - If the user has typed a built-in command then execute * it immediately. */ intbuiltin_cmd(char **argv) { if (!strcmp(argv[0], "quit")) exit(0); /* terminate shell */ ...
然后我们make编译以下,查看测试情况,
1 2 3 4 5
puitar@ubuntu:~/Desktop/LAB4/shlab-handout$ make test02 ./sdriver.pl -t trace02.txt -s ./tsh -a "-p" # # trace02.txt - Process builtin quit command. #
和参考一样,通关~
test03 & test04
测试3和测试4的内容放在一起讲好了,因为
1 2 3 4 5 6 7 8 9 10 11 12 13
puitar@ubuntu:~/Desktop/LAB4/shlab-handout$ make rtest03 ./sdriver.pl -t trace03.txt -s ./tshref -a "-p" # # trace03.txt - Run a foreground job. # tsh> quit puitar@ubuntu:~/Desktop/LAB4/shlab-handout$ make rtest04 ./sdriver.pl -t trace04.txt -s ./tshref -a "-p" # # trace04.txt - Run a background job. # tsh> ./myspin 1 & [1] (9950) ./myspin 1 &
/* * 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. */ voidsigchld_handler(int sig) { pid_t pid; int status;
if (verbose) printf("sigchld_handler: entering \n");
voidsigtstp_handler(int sig) { // 为了异步信号安全防止errno被覆盖 int olderrno = errno; // get the foreground job pid pid_t fg_pid = fgpid(jobs); // kill the group in the foreground kill(-fg_pid, sig); errno = olderrno; }
/* * 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. */ voidsigchld_handler(int sig) { int old_errno = errno;
pid_t pid; int status; if (verbose) printf("sigchld_handler: entering \n");
/* * 回收僵尸进程 * 这里的WNOHANG是非常重要的。 * 它的本意是如果所有孩子都没有僵尸(终止)状态的,直接退出 * 这个能够避免在这里等待所有前台的running和stopped程序终止 * 这样tsh就不能正常接受用户的输入了 * WUNTRACED是等待直到有一个子进程变成僵尸退出,返回它的pid */ while ((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) { /* 这里实验指导书说不能用while,但是不用会出现错误。他说的while和我的判断条件不一样 */ // 子进程正常退出 if (WIFEXITED(status)) { deletejob(jobs, pid); } // 子进程因为ctrl-c退出 if (WIFSIGNALED(status)) { // terminated by ctrl-c printf("Job [%d] (%d) terminated by signal %2d\n", pid2jid(pid), pid, WTERMSIG(status)); deletejob(jobs, pid); } if (WIFSTOPPED(status)) { // stopped by ctrl-z printf("Job [%d] (%d) stopped by signal %2d\n", pid2jid(pid), pid, WSTOPSIG(status)); // 修改子进程状态为ST getjobpid(jobs, pid)->state = ST; } } errno = old_errno; if (verbose) printf("sigchld_handler: exiting\n"); }
/* * 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. */ voidsigchld_handler(int sig) { int old_errno = errno;
pid_t pid; int status; if (verbose) printf("sigchld_handler: entering \n");
/* * 回收僵尸进程 * 这里的WNOHANG是非常重要的。 * 它的本意是如果所有孩子都没有僵尸(终止)状态的,直接退出 * 这个能够避免在这里等待所有前台的running和stopped程序终止 * 这样tsh就不能正常接受用户的输入了 * WUNTRACED是等待直到有一个子进程变成僵尸退出,返回它的pid */ while ((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) { // 子进程正常退出 if (WIFEXITED(status)) { deletejob(jobs, pid); } // 子进程因为ctrl-c退出 if (WIFSIGNALED(status)) { // terminated by ctrl-c /* printf("Job [%d] (%d) terminated by signal %2d\n", pid2jid(pid), pid, WTERMSIG(status)); */ { Sio_puts("Job ["); Sio_putl(pid2jid(pid)); Sio_puts("] ("); Sio_putl(pid); Sio_puts(") terminated by signal "); Sio_putl(WTERMSIG(status)); Sio_puts("\n"); } deletejob(jobs, pid); } if (WIFSTOPPED(status)) { // stopped by ctrl-z /* printf("Job [%d] (%d) stopped by signal %2d\n", pid2jid(pid), pid, WSTOPSIG(status)); */ { Sio_puts("Job ["); Sio_putl(pid2jid(pid)); Sio_puts("] ("); Sio_putl(pid); Sio_puts(") stopped by signal "); Sio_putl(WSTOPSIG(status)); Sio_puts("\n"); } // 修改子进程状态为ST getjobpid(jobs, pid)->state = ST; } } errno = old_errno; if (verbose) { /* printf("sigchld_handler: exiting\n"); */ { Sio_puts("sigchld_handler: exiting\n"); } } }
puitar@ubuntu:~/Desktop/LAB4/shlab-handout$ make rtest10 ./sdriver.pl -t trace10.txt -s ./tshref -a "-p" # # trace10.txt - Process fg builtin command. # tsh> ./myspin 4 & [1] (7350) ./myspin 4 & tsh> fg %1 Job [1] (7350) stopped by signal 20 tsh> jobs [1] (7350) Stopped ./myspin 4 & tsh> fg %1 tsh> jobs puitar@ubuntu:~/Desktop/LAB4/shlab-handout$ make test10 ./sdriver.pl -t trace10.txt -s ./tsh -a "-p" # # trace10.txt - Process fg builtin command. # tsh> ./myspin 4 & [1] (7361) ./myspin 4 & tsh> fg %1 Job [1] (7361) stopped by signal 20 tsh> jobs [1] (7361) Stopped ./myspin 4 & tsh> fg %1 tsh> jobs
与参考一致,通关~
test11 & test12 & test13
先看看test11
1 2 3 4 5
puitar@ubuntu:~/Desktop/LAB4/shlab-handout$ make rtest11 ./sdriver.pl -t trace11.txt -s ./tshref -a "-p" # # trace11.txt - Forward SIGINT to every process in foreground process group #
puitar@ubuntu:~/Desktop/LAB4/shlab-handout$ make rtest12 ./sdriver.pl -t trace12.txt -s ./tshref -a "-p" # # trace12.txt - Forward SIGTSTP to every process in foreground process group #
puitar@ubuntu:~/Desktop/LAB4/shlab-handout$ make rtest13 ./sdriver.pl -t trace13.txt -s ./tshref -a "-p" # # trace13.txt - Restart every stopped process in process group #
Here are the different values that the s, stat and state output specifiers (header "STAT" or "S") will display to describe the state of a process:
D uninterruptible sleep (usually IO) R running or runnable (on run queue) S interruptible sleep (waiting for an event to complete) T stopped by job control signal t stopped by debugger during the tracing W paging (not valid since the 2.6.xx kernel) X dead (should never be seen) Z defunct ("zombie") process, terminated but not reaped by its parent
For BSD formats and when the stat keyword is used, additional characters may be displayed: < high-priority (not nice to other users) N low-priority (nice to other users) L has pages locked into memory (for real-time and custom IO) s is a session leader l is multi-threaded (using CLONE_THREAD, like NPTL pthreads do) + is in the foreground process group
/* * do_bgfg - Execute the builtin bg and fg commands */ voiddo_bgfg(char** argv) { structjob_t* job; char* id = argv[1];
/* ADD PART bg和fg后边不跟任何参数不执行直接返回 */ // no argument for bg/fg if (id == NULL) { printf("%s command requires PID or %%jobid argument\n", argv[0]); return; } // 判断输入的是jid还是pid if (id[0] == '%') { /* jid */ /* ADD PART 检查输入的jid是不是数字 */ if (!checkNum(id + 1)) { printf("%s: argument must be a PID or %%jobid\n", argv[0]); return; } //去掉'%'开始读jid,根据jid返回这个子进程的结构指针 int jid = atoi(id + 1); job = getjobjid(jobs, jid); /* ADD PART 找不到输入的这个作业 */ if (job == NULL) { printf("%%%d: No such job\n", jid); return; } } else { /* pid */ /* ADD PART 检查输入的pid是不是数字 */ if (!checkNum(id)) { printf("%s: argument must be a PID or %%jobid\n", argv[0]); return; } int pid = atoi(id); job = getjobpid(jobs, pid); /* ADD PART 找不到这个作业 */ if (job == NULL) { printf("(%d): No such process\n", pid); return; } }
puitar@ubuntu:~/Desktop/LAB4/shlab-handout$ make rtest14 ./sdriver.pl -t trace14.txt -s ./tshref -a "-p" # # trace14.txt - Simple error handling # tsh> ./bogus ./bogus: Command not found tsh> ./myspin 4 & [1] (7521) ./myspin 4 & tsh> fg fgcommand requires PID or %jobid argument tsh> bg bgcommand requires PID or %jobid argument tsh> fg a fg: argument must be a PID or %jobid tsh> bg a bg: argument must be a PID or %jobid tsh> fg 9999999 (9999999): No such process tsh> bg 9999999 (9999999): No such process tsh> fg %2 %2: No such job tsh> fg %1 Job [1] (7521) stopped by signal 20 tsh> bg %2 %2: No such job tsh> bg %1 [1] (7521) ./myspin 4 & tsh> jobs [1] (7521) Running ./myspin 4 & puitar@ubuntu:~/Desktop/LAB4/shlab-handout$ make test14 ./sdriver.pl -t trace14.txt -s ./tsh -a "-p" # # trace14.txt - Simple error handling # tsh> ./bogus tsh> ./myspin 4 & [1] (7540) ./myspin 4 & tsh> fg fgcommand requires PID or %jobid argument tsh> bg bgcommand requires PID or %jobid argument tsh> fg a fg: argument must be a PID or %jobid tsh> bg a bg: argument must be a PID or %jobid tsh> fg 9999999 (9999999): No such process tsh> bg 9999999 (9999999): No such process tsh> fg %2 %2: No such job tsh> fg %1 Job [1] (7540) stopped by signal 20 tsh> bg %2 %2: No such job tsh> bg %1 [1] (7540) ./myspin 4 & tsh> jobs [1] (7540) Running ./myspin 4 &
puitar@ubuntu:~/Desktop/LAB4/shlab-handout$ make rtest15 ./sdriver.pl -t trace15.txt -s ./tshref -a "-p" # # trace15.txt - Putting it all together #
测试15是要我们把所有的方法放在一起。这个都不要做,本来就在一个文件tsh.c下写的。。。
1 2 3 4 5 6
puitar@ubuntu:~/Desktop/LAB4/shlab-handout$ make rtest16 ./sdriver.pl -t trace16.txt -s ./tshref -a "-p" # # trace16.txt - Tests whether the shell can handle SIGTSTP and SIGINT # signals that come from other processes instead of the terminal. #
/* * tsh - A tiny shell program with job control * * <Puitar> */ #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 */ externchar** 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 */
structjob_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 */ }; structjob_tjobs[MAXJOBS];/* The job list */ /* End global variables */
/* Function prototypes */
/* Here are the functions that you will implement */ voideval(char* cmdline); intbuiltin_cmd(char** argv); voiddo_bgfg(char** argv); voidwaitfg(pid_t pid);
/* * main - The shell's main routine */ intmain(int argc, char** argv) { char c; char cmdline[MAXLINE]; int emit_prompt = 1; /* emit prompt (default) */
/* Redirect stderr to stdout (so that driver will get all output * on the pipe connected to stdout) */ dup2(1, 2);
/* Parse the command line */ while ((c = getopt(argc, argv, "hvp")) != EOF) { switch (c) { case'h': /* print help message */ usage(); break; case'v': /* emit additional diagnostic info */ verbose = 1; break; case'p': /* don't print a prompt */ emit_prompt = 0; /* handy for automatic testing */ break; default: usage(); } }
/* Install the signal handlers */
/* These are the ones you will need to implement */ Signal(SIGINT, sigint_handler); /* ctrl-c */ Signal(SIGTSTP, sigtstp_handler); /* ctrl-z */ Signal(SIGCHLD, sigchld_handler); /* Terminated or stopped child */
/* This one provides a clean way to kill the shell */ Signal(SIGQUIT, sigquit_handler);
/* Initialize the job list */ initjobs(jobs);
/* Execute the shell's read/eval loop */ while (1) {
/* Read command line */ 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); }
/* Evaluate the command line */ 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. */ voideval(char* cmdline) { char* argv[MAXARGS]; /* argv for execve() */ int bg; /* Should the job run in bg or fg? */ pid_t pid; /* Process id */ sigset_t mask, prev;
/* Parent waits for foreground job to terminate */ if (!bg) // 前台运行则等待子进程 waitfg(pid); else// background printf("[%d] (%d) %s", pid2jid(pid), pid, cmdline); } }
/* * 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. */ intparseline(constchar* cmdline, char** argv) { staticchararray[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, ' '); }
/* 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. */ intbuiltin_cmd(char** argv) { // quit command if (!strcmp(argv[0], "quit")) exit(0);
// jobs command if (!strcmp(argv[0], "jobs")) { listjobs(jobs); return1; }
// bg or fg command if (!strcmp(argv[0], "bg") || !strcmp(argv[0], "fg")) { do_bgfg(argv); return1; }
// ignore singleton & (不处理单独的 '&') if (!strcmp(argv[0], "&")) return1;
// not a build-in command return0; }
// check if arg is a string of nums intcheckNum(char* arg) { int len = strlen(arg); int i; for (i = 0; i < len; ++i) { if (!isdigit(arg[i])) // not num return0; } return1; }
/* * do_bgfg - Execute the builtin bg and fg commands */ voiddo_bgfg(char** argv) { structjob_t* job; char* id = argv[1];
// no argument for bg/fg if (id == NULL) { printf("%s command requires PID or %%jobid argument\n", argv[0]); return; } // 判断输入的是jid还是pid if (id[0] == '%') { /* jid */ if (!checkNum(id + 1)) { printf("%s: argument must be a PID or %%jobid\n", argv[0]); return; } //去掉'%'开始读jid,根据jid返回这个子进程的结构指针 int jid = atoi(id + 1); job = getjobjid(jobs, jid); if (job == NULL) { printf("%%%d: No such job\n", jid); return; } } else { /* pid */ if (!checkNum(id)) { printf("%s: argument must be a PID or %%jobid\n", argv[0]); return; } int pid = atoi(id); job = getjobpid(jobs, pid); if (job == NULL) { printf("(%d): No such process\n", pid); return; } }
/* * waitfg - Block until process pid is no longer the foreground process */ voidwaitfg(pid_t pid) { while (pid == fgpid(jobs)) sleep(0); // 这里是主动让出CPU }
/***************** * 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. */ voidsigchld_handler(int sig) { int old_errno = errno;
pid_t pid; int status; if (verbose) printf("sigchld_handler: entering \n");
/* * 回收僵尸进程 * 这里的WNOHANG是非常重要的。 * 它的本意是如果所有孩子都没有僵尸(终止)状态的,直接退出 * 这个能够避免在这里等待所有前台的running和stopped程序终止 * 这样tsh就不能正常接受用户的输入了 * WUNTRACED是等待直到有一个子进程变成僵尸退出,返回它的pid */ while ((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) { // 子进程正常退出 if (WIFEXITED(status)) { deletejob(jobs, pid); } // 子进程因为ctrl-c退出 if (WIFSIGNALED(status)) { // terminated by ctrl-c /* printf("Job [%d] (%d) terminated by signal %2d\n", pid2jid(pid), pid, WTERMSIG(status)); */ { Sio_puts("Job ["); Sio_putl(pid2jid(pid)); Sio_puts("] ("); Sio_putl(pid); Sio_puts(") terminated by signal "); Sio_putl(WTERMSIG(status)); Sio_puts("\n"); } deletejob(jobs, pid); } if (WIFSTOPPED(status)) { // stopped by ctrl-z /* printf("Job [%d] (%d) stopped by signal %2d\n", pid2jid(pid), pid, WSTOPSIG(status)); */ { Sio_puts("Job ["); Sio_putl(pid2jid(pid)); Sio_puts("] ("); Sio_putl(pid); Sio_puts(") stopped by signal "); Sio_putl(WSTOPSIG(status)); Sio_puts("\n"); } // 修改子进程状态为ST getjobpid(jobs, pid)->state = ST; } } errno = old_errno; if (verbose) { /* printf("sigchld_handler: exiting\n"); */ { Sio_puts("sigchld_handler: exiting\n"); } } }
/* * 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. */ voidsigint_handler(int sig) { int olderrno = errno; // get the foreground job pid pid_t fg_pid = fgpid(jobs); // kill the group in the foreground /* * int kill(pid_t pid,int signo) * 功能: 向进程或进程组发送一个信号 (成功返回 0; 否则,返回 -1 ) */ kill(-fg_pid, sig); // -fg_pid表示向进程组号为pid的组中的每个进程发sig信号 // 在这里就是向前台进程以及它的每一个子进程(子进程都在自己的父进程的pid为组id的组下) errno = 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. */ voidsigtstp_handler(int sig) { // 为了异步信号安全防止errno被覆盖 int olderrno = errno; // get the foreground job pid pid_t fg_pid = fgpid(jobs); // kill the group in the foreground kill(-fg_pid, sig); errno = olderrno; }
/********************* * End signal handlers *********************/
/*********************************************** * Helper routines that manipulate the job list **********************************************/
/* clearjob - Clear the entries in a job struct */ voidclearjob(structjob_t* job) { job->pid = 0; job->jid = 0; job->state = UNDEF; job->cmdline[0] = '\0'; }
/* initjobs - Initialize the job list */ voidinitjobs(structjob_t* jobs) { int i;
for (i = 0; i < MAXJOBS; i++) clearjob(&jobs[i]); }
/* maxjid - Returns largest allocated job ID */ intmaxjid(structjob_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 */ intaddjob(structjob_t* jobs, pid_t pid, int state, char* cmdline) { int i;
if (pid < 1) return0;
for (i = 0; i < MAXJOBS; i++) { /* 如果jobs数组里面有pid等于0等于0的表项,表示这个位置可以用,把这个作业放进去 */ 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); } return1; } } printf("Tried to create too many jobs\n"); return0; }
/* deletejob - Delete a job whose PID=pid from the job list */ intdeletejob(structjob_t* jobs, pid_t pid) { int i;
if (pid < 1) return0;
for (i = 0; i < MAXJOBS; i++) { if (jobs[i].pid == pid) { clearjob(&jobs[i]); nextjid = maxjid(jobs) + 1; return1; } } return0; }
/* fgpid - Return PID of current foreground job, 0 if no such job */ pid_tfgpid(structjob_t* jobs) { int i;
for (i = 0; i < MAXJOBS; i++) if (jobs[i].state == FG) return jobs[i].pid; return0; }
/* getjobpid - Find a job (by PID) on the job list */ structjob_t* getjobpid(structjob_t* jobs, pid_t pid) { int i;
if (pid < 1) returnNULL; for (i = 0; i < MAXJOBS; i++) if (jobs[i].pid == pid) return &jobs[i]; returnNULL; }
/* getjobjid - Find a job (by JID) on the job list */ structjob_t* getjobjid(structjob_t* jobs, int jid) { int i;
if (jid < 1) returnNULL; for (i = 0; i < MAXJOBS; i++) if (jobs[i].jid == jid) return &jobs[i]; returnNULL; }
/* pid2jid - Map process ID to job ID */ intpid2jid(pid_t pid) { int i;
if (pid < 1) return0; for (i = 0; i < MAXJOBS; i++) if (jobs[i].pid == pid) { return jobs[i].jid; } return0; }
/* listjobs - Print the job list */ voidlistjobs(structjob_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 */ voidusage(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); }
/* * Signal - wrapper for the sigaction function */ handler_t* Signal(int signum, handler_t* handler) { structsigactionaction, 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 */
/* * sigquit_handler - The driver program can gracefully terminate the * child shell by sending it a SIGQUIT signal. */ voidsigquit_handler(int sig) { printf("Terminating after receipt of SIGQUIT signal\n"); exit(1); }
// The following function are from csapp.c
/************************************************************* * Wrappers for blocking signals *************************************************************/
voidSigaddset(sigset_t* set, int signum) { if (sigaddset(set, signum) < 0) unix_error("Sigaddset error"); return; }
/************************************************************* * The Sio (Signal-safe I/O) package - simple reentrant output * functions that are safe for signal handlers. *************************************************************/
/* Private sio functions */
/* $begin sioprivate */ /* sio_reverse - Reverse a string (from K&R) */ staticvoidsio_reverse(char s[]) { int c, i, j;
for (i = 0, j = strlen(s) - 1; i < j; i++, j--) { c = s[i]; s[i] = s[j]; s[j] = c; } }
/* sio_ltoa - Convert long to base b string (from K&R) */ staticvoidsio_ltoa(long v, char s[], int b) { int c, i = 0; int neg = v < 0;
if (neg) v = -v;
do { s[i++] = ((c = (v % b)) < 10) ? c + '0' : c - 10 + 'a'; } while ((v /= b) > 0);
if (neg) s[i++] = '-';
s[i] = '\0'; sio_reverse(s); }
/* sio_strlen - Return length of string (from K&R) */ staticsize_tsio_strlen(char s[]) { int i = 0;