/* * 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. */ void eval(char *cmdline) { int bg; /* should the job run in bg or fg? */ struct cmdline_tokens tok; sigset_t prev, mask_all; int state; pid_t pid; struct job_t *job_ins; /* Parse command line */ bg = parseline(cmdline, &tok); if (bg == -1) /* parsing error */ return; if (tok.argv[0] == NULL) /* ignore empty lines */ return; /* Fig. 8.40 */ if (!builtin_command(&tok)) { Sigemptyset(&mask_all); Sigaddset(&mask_all, SIGCHLD); Sigaddset(&mask_all, SIGINT); Sigaddset(&mask_all, SIGTSTP); Sigprocmask(SIG_BLOCK, &mask_all, &prev); /* Block at beginning */ if ((pid = Fork()) == 0) { /* Child process */ Sigprocmask(SIG_SETMASK, &prev, NULL); /* Unblock inside child */ Setpgid(0, 0); /* one proc and shell in pgrp */ open_dup2_in(tok.infile); open_dup2_out(tok.outfile); if (execve(tok.argv[0], tok.argv, environ) < 0) { printf("%s: Command not found.\n", tok.argv[0]); exit(0); } } /* Parent process */ state = bg ? BG : FG; addjob(job_list, pid, state, cmdline); job_ins = getjobpid(job_list, pid); Sigprocmask(SIG_SETMASK, &prev, NULL); /* Unblock 3 signals */ if (bg) { printf("[%d] (%d) %s", pid2jid(pid), pid, cmdline); } else { wait_fg_job(job_ins -> state); } } return; }
/* * 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. */ void eval(char *cmdline) { char *argv[MAXARGS]; /* Argument list execve() */ char buf[MAXLINE]; /* Holds modified command line */ int bg; /* Should the job run in bg or fg? */ pid_t pid; /* Process id */ sigset_t mask; strcpy(buf, cmdline); bg = parseline(buf, argv); if (argv[0] == NULL) { return; /* Ignore empty lines */ } if (!builtin_cmd(argv)) { /* Block SIGCHLD, SIGINT, and SIGSTP until the job to be run is on the list */ Sigemptyset(&mask); Sigaddset(&mask, SIGCHLD); Sigaddset(&mask, SIGINT); Sigaddset(&mask, SIGTSTP); Sigprocmask(SIG_BLOCK, &mask, NULL); if ((pid = Fork()) == 0) { /* Child runs user job */ Setpgid(0, 0); // give the child a new group /* unblock signals for child */ Sigprocmask(SIG_UNBLOCK, &mask, NULL); if (execve(argv[0], argv, environ) < 0) { printf("%s: Command not found.\n", argv[0]); exit(0); } } addjob(jobs, pid, bg?BG:FG, cmdline); if (bg) { // Notify user that job is running in the background printf("[%i] (%i) %s", getjobpid(jobs, pid)->jid, pid, cmdline); } Sigprocmask(SIG_UNBLOCK, &mask, NULL); /* Unblock signals */ if (!bg) { /* Parent waits for foreground job to terminate */ waitfg(pid); } } return; }
/* * 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. */ void eval(char *cmdline) { char *argv[MAXARGS]; //arguments for execve() int bg; //Determines whether the job will run in foreground or background pid_t pid; //Contains the process id struct job_t *jd; sigset_t mask; //The signal set which has to be bloacked before adding the job to jobs bg = parseline(cmdline, argv); //Copies contents of cmdline into argv and returns whether the job should run in background or foreground Sigemptyset(&mask); //Generate an empty signal set in mask Sigaddset(&mask, SIGCHLD); //Add SIGCHLD to the signal set to be blocked Sigaddset(&mask, SIGINT); //Add SIGINT to the signal set to be blocked Sigaddset(&mask, SIGTSTP); //Add SIGTSTP to the signal set to be blocked if(!builtin_cmd(argv)){ //Checks whether command is built-in and executes it if yes, else enters if block Sigprocmask(SIG_BLOCK, &mask, NULL); //Blocked the signal set if((pid = Fork()) == 0){ //Run user process in a child Sigprocmask(SIG_UNBLOCK, &mask, NULL); //Unblock the signal sets in child Setpgid(0,0); //New jobs should have new process ids else signal will kill shell also if(execve(argv[0], argv, environ) < 0){ //executes user command if successful printf("%s: Command not found.\n", argv[0]); //Throw error if execution unsuccessful exit(0); } } if(!bg){ //If process is foreground, parent waits for the job to terminate addjob(jobs, pid, FG, cmdline); //Add the process to jobs Sigprocmask(SIG_UNBLOCK, &mask, NULL); //Unblock the signal set afet adding the job waitfg(pid); //Parent waits for the foreground process to terminate} } else{ //If process is a background addjob(jobs, pid, BG, cmdline); //Add the process to jobs Sigprocmask(SIG_UNBLOCK, &mask, NULL); //Unblock the signal set afet adding the job jd = getjobpid(jobs, pid); //Get the jobpid printf("[%d] (%d) %s", jd->jid, jd->pid, jd->cmdline); //Print the details of background job } } return; }
/* * 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. */ void eval(char *cmdline) { char *argv[MAXARGS]; int bg; pid_t pid; // will return 1 if background job bg = parseline(cmdline, argv); //Error checking for no input if (argv[0] == NULL){ return; } if(!builtin_cmd(argv)){ pid = fork(); // child runs the current user job if(pid == 0){ Setpgid(0,0); //CSAPP function //Checks to see if the argument is valid or not if(execve(argv[0], argv, environ) < 0){ printf("%s: command is not found.\n", argv[0]); exit(0); } } // Parent waits for foreground to terminate before running if(!bg){ //Foreground addjob(jobs, pid, FG, cmdline); waitfg(pid); } else { //Background addjob(jobs, pid, BG, cmdline); printf("[%d] (%d) %s",pid2jid(pid),pid,cmdline); } } return; }
/* * 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. */ void eval(char *cmdline) { int bg; /* should the job run in bg or fg? */ struct cmdline_tokens tok; pid_t pid; sigset_t mask; /* Parse command line */ bg = parseline(cmdline, &tok); if (bg == -1) return; /* parsing error */ if (tok.argv[0] == NULL) return; /* ignore empty lines */ sigemptyset(&mask); sigaddset(&mask,SIGCHLD); sigaddset(&mask,SIGINT); sigaddset(&mask,SIGTSTP); sigprocmask(SIG_BLOCK,&mask,NULL); if(!builtin_handler(tok)) { if((pid=Fork())==0) { /*restore default handlers*/ Signal(SIGINT,SIG_DFL); Signal(SIGTSTP,SIG_DFL); sigprocmask(SIG_UNBLOCK,&mask,NULL); /*Put child process in its own group*/ Setpgid(0,0); IO_redirection(tok); Execve(tok.argv[0],tok.argv,environ); } else /*Shell Processing*/ { if(bg) { addjob(job_list,pid,BG,cmdline); printf("[%d] (%d) %s\n", pid2jid(pid), pid, cmdline); sigprocmask(SIG_UNBLOCK,&mask,NULL); } else { addjob(job_list,pid,FG,cmdline); sigprocmask(SIG_UNBLOCK,&mask,NULL); while(fgpid(job_list) != 0) { } } } } }
/* * 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 (SIGSTP) from the kernel * when we type ctrl-c (ctrl-z) at the keyboard. */ void eval(char *cmdline) { char *argv[MAXARGS], buf[MAXLINE]; int bg, status, state; volatile pid_t pid; int jid; sigset_t mask_all, mask_one, prev_one; Log("EVAL [0]\n", 9); strcpy(buf, cmdline); bg = parseline(buf, argv); if (argv[0] == NULL) { return; } Log("EVAL [1]\n", 9); if (builtin_cmd(argv)) { return; } Log("EVAL [2]\n", 9); Sigfillset(&mask_all); Sigemptyset(&mask_one); Sigaddset(&mask_one, SIGCHLD); Sigprocmask(SIG_BLOCK, &mask_one, &prev_one); pid = Fork(); if (pid == CHILD) { Sigprocmask(SIG_SETMASK, &prev_one, NULL); Setpgid(0, 0); Log("EVAL [3]\n", 9); Execve(argv[0], argv, environ); } Sigprocmask(SIG_BLOCK, &mask_all, NULL); state = bg ? BG : FG; Log("EVAL [4]\n", 9); status = addjob(jobs, pid, state, cmdline); Log("EVAL [5]\n", 9); /* Stores jid while process has not been removed */ jid = getjobpid(jobs, pid)->jid; atomic_fggpid = bg ? 0 : pid; Log("EVAL [5a]\n", 10); Sigprocmask(SIG_SETMASK, &prev_one, NULL); Log("EVAL [5b]\n", 10); /* Handle errors when generating a new job */ if (!status) { return; } /* Parent waits for foreground job to terminate */ if (!bg) { Log("EVAL [6]\n", 9); waitfg(pid); } else { Log("EVAL [7]\n", 9); printf("[%d] (%d) %s", jid, pid, cmdline); } }
/* * 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. */ void eval(char *cmdline) { int bg; /* should the job run in bg or fg? */ struct cmdline_tokens tok; /* Parse command line */ bg = parseline(cmdline, &tok); if (bg == -1) return; /* parsing error */ if (tok.argv[0] == NULL) return; /* ignore empty lines */ /* Built-in Command */ if (tok.builtins!=BUILTIN_NONE){ builtin_command(&tok); return; } /* Common tasks for both foreground and background */ pid_t pid; sigset_t mask; Sigemptyset(&mask); Sigaddset(&mask, SIGCHLD); Sigaddset(&mask, SIGINT); Sigaddset(&mask, SIGTSTP); /* Block signal receving in parent */ Sigprocmask(SIG_BLOCK, &mask, NULL); if ((pid=Fork())==0){ /* Unblock signal receiving in child */ Sigprocmask(SIG_UNBLOCK, &mask, NULL); /* Changes the child's process group from the shell's */ Setpgid(0,0); /* Change the input and output file descriptors */ IOredirection(&tok); Execve(tok.argv[0], tok.argv, environ); /* Command not found */ printf("Executable file not found\n"); exit(0); } /* Foreground job*/ if (!bg){ if (!addjob(job_list, pid, FG, cmdline)) return; Sigprocmask(SIG_UNBLOCK, &mask, NULL); wait_for_fg(pid); return; } /* Background job */ if (bg){ if (!addjob(job_list, pid, BG, cmdline)) return; Sigprocmask(SIG_UNBLOCK, &mask, NULL); printf("[%d] (%d) %s\n", pid2jid(pid), pid, cmdline); } return; }
/* * 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. */ void eval(char *cmdline) { int bg; /* should the job run in bg or fg? */ struct cmdline_tokens tok; pid_t pid; /* if the pid is 0, it is the child. */ int jid; /* if the child runs in background, we should know the jid and print the infomation */ /* * Use new_mask to mask three signals. * Before the child process execve(), we should unblock these signals. * When it comes to parent process, we should set the old_mask set to * it after we add the job in job_list. */ sigset_t new_mask, old_mask; /* Parse command line */ bg = parseline(cmdline, &tok); /* * Maybe there are some more errors that we should return. * I did not meet them. If I met an error condition, I will * make an improvement as soon as possible. */ if (bg == -1) return; /* parsing error */ if (tok.argv[0] == NULL) return; /* ignore empty lines */ /* * As it is said in the writeup, If the first word is a built-in command, the * shell immediately executes the command in the current process. Otherwise, * the word is assumed to be the pathname of an executable program. In this * case, the shell forks a child process, then loads and runs the program in * the context of the child. * 1. If it is a built-in command, builtinCommand function will handle it. * 2. If it is a executable program, the function returns 0. * 3. Then, we should block the signals before we fork a child. * 4. Put the child in a new process group. * 5. Set the I/O Redirection and make the signal default. * 6. Execute the program and exit. * 7. Add the job and unblock the signals in parent. * 8. If the child runs in foreground, wait for it. */ if (!builtinCommand(tok)) { Sigemptyset(&new_mask); Sigaddset(&new_mask, SIGCHLD); Sigaddset(&new_mask, SIGINT); Sigaddset(&new_mask, SIGTSTP); Sigprocmask(SIG_BLOCK, &new_mask, &old_mask); if (0 == (pid = Fork())) { int fdout, fdin; Setpgid(0, 0); dup2(fdout = getfd(OUT, tok.outfile), STDOUT_FILENO); dup2(fdin = getfd(IN, tok.infile), STDIN_FILENO); if (!bg) { tcsetpgrp(STDIN_FILENO, getpgrp()); } Signal(SIGCHLD, SIG_DFL); Signal(SIGINT, SIG_DFL); Signal(SIGTSTP, SIG_DFL); Sigprocmask(SIG_UNBLOCK, &new_mask, NULL); if (execve(tok.argv[0], tok.argv, environ) < 0) { printf("%s: Command not found.\n", tok.argv[0]); exit(0); } if (STDOUT_FILENO != fdout) { close(fdout); } if (STDIN_FILENO != fdin) { close(fdin); } exit(0); } if (bg) { addjob(job_list, pid, BG, cmdline); Sigprocmask(SIG_SETMASK, &old_mask, NULL); jid = pid2jid(pid); printf("[%d] (%d) %s\n", jid, pid, cmdline); } else { addjob(job_list, pid, FG, cmdline); Sigprocmask(SIG_SETMASK, &old_mask, NULL); /* * If the child runs in foreground, we should wait for it. I think it is * the most tricky thing in this shell lab. I test everything I know. Then, * I found the sigsuspend() function had the best performance. * 1. pause(). * It is the first way I think about. When I do manually from the * command line, It is OK. However, when it comes to trace file, sometimes * it is OK, sometimes it will cause an error. * 2. sleep(1), usleep(n), or something like these. * Although Michael said that we cannot use sleep, I do not know what to do * and what the performance is. So, I test it. When I ran it from the * command line, I could not find any wrong. When it comes to the trace * files, there are lots of errors. It seems that it is not a good way. -_-b * 3. sleep(0), sleep(0), sleep(0)... * When I found the information in the Internet, someone said 3 or more * sleep(0) could be a good choice for wait. The fact is that it can pass * a lot of trace files, but the performance is not stable. Also, I think * it will waste the CPU resource. * sigsuspend() is the only way that I found could pass the ./sdriver. If there * is annother better way, I will be very interested in it. If you would like * to tell a better solution, I will be very happy. */ while (fgpid(job_list)) { sigsuspend(&old_mask); } } } return; }