/* * 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)// cmdline-->argv_array { 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, ' '); //这两个delim指明的是当前参数后面的分隔符 }
/* * 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) { sigset_tset; pid_t pid; int state = UNDEF; char *argv[MAXLINE]; if (1 == parseline(cmdline, argv)) state = BG; else state = FG; if (argv[0] == NULL) return; if (!builtin_cmd(argv)) { sigemptyset(&set); sigaddset(&set, SIGINT), sigaddset(&set, SIGCHLD), sigaddset(&set, SIGSTOP); sigprocmask(SIG_BLOCK, &set, NULL);
/* * 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); elseif (!strcmp(argv[0], "bg") || !strcmp(argv[0], "fg")) do_bgfg(argv); elseif (!strcmp(argv[0], "jobs")) listjobs(jobs); else return0; /* not a builtin command */ return1; }
/* * do_bgfg - Execute the builtin bg and fg commands */ voiddo_bgfg(char **argv) { int parsed; structjob_t *job; if (!argv[1]) { printf("%s commond requires PID or %%JID argument\n", argv[0]); return; }
if (argv[1][0] == '%') //错误检查以及取出PID或者JID { if ((parsed = strtol(argv[1] + 1, NULL, 10)) <= 0) { printf("%s: arguments must be a PID or JID\n", argv[0]); return; } if ((job = getjobjid(jobs, parsed)) == NULL) { printf("(%d): No such job\n", parsed); return; } } else { if ((parsed = strtol(argv[1], NULL, 10)) <= 0) { printf("%s: arguments must be a PID or JID\n", argv[0]); return; } if ((job = getjobpid(jobs, parsed)) == NULL) { printf("(%d): No such process\n", parsed); return; } }
/***************** * 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) { pid_t pid; int status; while ((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) { if (WIFEXITED(status)) { /*process is exited in normal way*/ deletejob(jobs, pid); } if (WIFSIGNALED(status)) { /*process is terminated by a signal*/ printf("Job [%d] (%d) terminated by signal %d\n", pid2jid(pid), pid, WTERMSIG(status)); deletejob(jobs, pid); } if (WIFSTOPPED(status)) { /*process is stop because of a signal*/ printf("Job [%d] (%d) stopped by signal %d\n", pid2jid(pid), pid, WSTOPSIG(status)); structjob_t *job = getjobpid(jobs, pid); if (job != NULL) job->state = ST; } } 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) { pid_t pid = fgpid(jobs);
if (pid != 0) { kill(-pid, SIGINT); } 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) { pid_t pid = fgpid(jobs);
/* * tsh - A tiny shell program with job control * * <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 */ 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) /* * 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)
/* * builtin_cmd - If the user has typed a built-in command then execute * it immediately. */ intbuiltin_cmd(char **argv)
/* * do_bgfg - Execute the builtin bg and fg commands */ voiddo_bgfg(char **argv) /* * waitfg - Block until process pid is no longer the foreground process */ voidwaitfg(pid_t pid)
/***************** * 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)
/* * 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)
/* * 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)
/********************* * End signal handlers *********************/
/*********************************************** * Helper routines that manipulate the job list **********************************************/
/* clearjob - Clear the entries in a job struct */ voidclearjob(struct job_t *job) { job->pid = 0; job->jid = 0; job->state = UNDEF; job->cmdline[0] = '\0'; }
/* initjobs - Initialize the job list */ voidinitjobs(struct job_t *jobs) { int i;
for (i = 0; i < MAXJOBS; i++) clearjob(&jobs[i]); }
/* maxjid - Returns largest allocated job ID */ intmaxjid(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 */ intaddjob(struct job_t *jobs, pid_t pid, int state, char *cmdline) { int i;
if (pid < 1) return0;
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); } return1; } } printf("Tried to create too many jobs\n"); return0; }
/* deletejob - Delete a job whose PID=pid from the job list */ intdeletejob(struct job_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(struct job_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 */ struct job_t *getjobpid(struct job_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 */ struct job_t *getjobjid(struct job_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(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 */ 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); }