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 */
/* * 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 buf[MAXLINE], *argv[MAXARGS];//buf存放输入的命令行,argv存放具体指令的参数,格式与main中的argv一样 strcpy(buf, cmdline); int background = parseline(buf, argv); int status = background ? BG : FG; if(argv[0] == NULL) return;
/* * waitfg - Block until process pid is no longer the foreground process */ voidwaitfg(pid_t pid) { sigset_t mask; sigemptyset(&mask); while(fgpid(jobs) != 0) sigsuspend(&mask); return; }
/* * 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 olderror = errno; int status, pid; sigset_t mask, prev; sigfillset(&mask); if((pid = Waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) { sigprocmask(SIG_BLOCK, &mask, &prev); if(WIFEXITED(status)) { deletejob(jobs, pid); } if(WIFSIGNALED(status)) { printf("Job [%d] (%d) terminated by signal %d\n", pid2jid(pid), pid, WTERMSIG(status)); deletejob(jobs, pid); } if(WIFSTOPPED(status)) { printf("Job [%d] (%d) stopped by signal %d\n", pid2jid(pid), pid, WSTOPSIG(status)); structjob_t* ptr = getjobpid(jobs, pid); ptr->state = ST; } sigprocmask(SIG_SETMASK, &prev, NULL); } errno = olderror; return; }
/* * 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 olderror = errno; sigset_t mask, prev; int pid; sigfillset(&mask); sigprocmask(SIG_BLOCK, &mask, &prev); if((pid = fgpid(jobs)) > 0) { sigprocmask(SIG_SETMASK, &prev, NULL); Kill(-pid, SIGINT); } errno = olderror; return; }
/* * 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) { int olderror = errno; sigset_t mask, prev; int pid; sigfillset(&mask); sigprocmask(SIG_BLOCK, &mask, &prev); if((pid = fgpid(jobs)) > 0) { sigprocmask(SIG_SETMASK, &prev, NULL); Kill(-pid, SIGSTOP); } errno = olderror; return; }
$ make test14 ./sdriver.pl -t trace14.txt -s ./tsh -a "-p" # # trace14.txt - Simple error handling # tsh> ./bogus ./bogus: command not found tsh> ./myspin 4 & [1] (17766) ./myspin 4 & tsh> fg fgcommand requires PID or %jobid argument tsh> bg bgcommand requires PID or %jobid argument tsh> fg a fgcommand requires PID or %jobid argument tsh> bg a bgcommand requires PID or %jobid argument tsh> fg 9999999 (9999999): No such job tsh> bg 9999999 (9999999): No such job tsh> fg %2 %2: No such job tsh> fg %1 Job [1] (17766) stopped by signal 19 tsh> bg %2 %2: No such job tsh> bg %1 [1] (17766) ./myspin 4 & tsh> jobs [1] (17766) Running ./myspin 4 &
$ 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] (17712) ./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] (17712) stopped by signal 20 tsh> bg %2 %2: No such job tsh> bg %1 [1] (17712) ./myspin 4 & tsh> jobs [1] (17712) Running ./myspin 4 &
Bug 遗留
我的这个 tsh 虽然通过测试程序没有说明问题,但有
bug
如果运行 ./myspin 5 &,那么 5 秒后运行
jobs 依旧会打印对应的任务,表面该进程并没有被回收