int main(int argc, char **argv) { int pid; sigset_t mask; Signal(SIGCHLD, handler); initjobs(); /* Initialize the job list */ while (1) { Sigemptyset(&mask); Sigaddset(&mask, SIGCHLD); Sigprocmask(SIG_BLOCK, &mask, NULL); /* Block SIGCHLD */ /* Child process */ if ((pid = Fork()) == 0) { Sigprocmask(SIG_UNBLOCK, &mask, NULL); /* Unblock SIGCHLD */ Execve("/bin/ls", argv, NULL); } /* Parent process */ addjob(pid); /* Add the child to the job list */ Sigprocmask(SIG_UNBLOCK, &mask, NULL); /* Unblock SIGCHLD */ } exit(0); }
int main(int argc, char **argv) { int i, nloop, signo; pid_t childpid, parentpid; sigset_t newmask; if (argc != 2) err_quit("usage: lat_sigwait <#loops>"); nloop = atoi(argv[1]); Sigemptyset(&newmask); Sigaddset(&newmask, SIGUSR1); Sigprocmask(SIG_BLOCK, &newmask, NULL); /* block SIGUSR1 */ parentpid = getpid(); if ( (childpid = Fork()) == 0) { for (i = 0; i < nloop; i++) { /* child */ Sigwait(&newmask, &signo); Kill(parentpid, SIGUSR1); } exit(0); } /* 4parent */ Start_time(); for (i = 0; i < nloop; i++) { Kill(childpid, SIGUSR1); Sigwait(&newmask, &signo); } printf("latency: %.3f usec\n", Stop_time() / nloop); exit(0); }
/* wait_fg_job - Use Sigsuspend to save some time :-) */ void wait_fg_job(int state) { sigset_t emptymask; Sigemptyset(&emptymask); while ((fgpid(job_list) != 0) && (FG == state)) { Sigsuspend(&emptymask); } }
int main(int argc, char **argv) { sigset_t mask, prev; Signal(SIGCHLD, sigchld_handler); Signal(SIGINT, sigint_handler); Sigemptyset(&mask); Sigaddset(&mask, SIGCHLD); while (1) { Sigprocmask(SIG_BLOCK, &mask, &prev); /* Block SIGCHLD */ if (Fork() == 0) /* Child */ exit(0); /* Wait for SIGCHLD to be received */ pid = 0; while (!pid) Sigsuspend(&prev); /* Optionally unblock SIGCHLD */ Sigprocmask(SIG_SETMASK, &prev, NULL); /* Do some work after receiving SIGCHLD */ printf("."); } exit(0); }
void sigio_dg_echo(int sockfd_arg, SA *pcliaddr, socklen_t clilen_arg) { int i; const int on = 1; sigset_t zeromask, newmask, oldmask; sockfd = sockfd_arg; clilen = clilen_arg; for (i = 0; i < QSIZE; i++) { /* init queue of buffers */ dg[i].dg_data = Malloc(MAXDG); dg[i].dg_sa = Malloc(clilen); dg[i].dg_salen = clilen; } iget = iput = nqueue = 0; Signal(SIGHUP, sig_hup); Signal(SIGIO, sig_io); Fcntl(sockfd, F_SETOWN, getpid()); if (ioctl(sockfd, FIOASYNC, &on) < 0) err_sys("ioctl error"); if (ioctl(sockfd, FIONBIO, &on) < 0) err_sys("ioctl error"); Sigemptyset(&zeromask); Sigemptyset(&oldmask); Sigemptyset(&newmask); Sigaddset(&newmask, SIGIO); /* signal we want to block */ Sigprocmask(SIG_BLOCK, &newmask, &oldmask); for (;;) { while (nqueue == 0) sigsuspend(&zeromask); Sigprocmask(SIG_SETMASK, &oldmask, NULL); Sendto(sockfd, dg[iget].dg_data, dg[iget].dg_len, 0, dg[iget].dg_sa, dg[iget].dg_salen); if (++iget >= QSIZE) iget = 0; /* Block SIGIO */ Sigprocmask(SIG_BLOCK, &newmask, &oldmask); nqueue--; } }
// Initialise la gestion des signaux utilisées par le shell // (assigne les handlers et prépare les bloquages) void jobs_signals_init () { Signal(SIGCHLD, handler_sigchld); Signal(SIGINT, handler_sigint); Signal(SIGTSTP, SIG_IGN); Sigemptyset(&signals_to_block); Sigaddset(&signals_to_block, SIGCHLD); }
int main(int argc, char **argv) { mqd_t mqd; void *buff; ssize_t n; sigset_t zeromask, newmask, oldmask; struct mq_attr attr; struct sigevent sigev; if (argc != 2) err_quit("Usage: nfs3 <name>"); mqd = Mq_open(argv[1], O_RDONLY | O_NONBLOCK); Mq_getattr(mqd, &attr); buff = (void *)Malloc(attr.mq_msgsize); Sigemptyset(&zeromask); Sigemptyset(&newmask); Sigemptyset(&oldmask); Sigaddset(&newmask, SIGUSR1); Signal(SIGUSR1, sig_usr1); sigev.sigev_notify = SIGEV_SIGNAL; sigev.sigev_signo = SIGUSR1; Mq_notify(mqd, &sigev); for ( ; ; ) { Sigprocmask(SIG_BLOCK, &newmask, &oldmask); while (mqflag == 0) sigsuspend(&zeromask); mqflag = 0; Mq_notify(mqd, &sigev); while ((n = mq_receive(mqd, buff, attr.mq_msgsize, NULL)) >= 0) { printf("read %ld bytes\n", (long)n); } if (errno != EAGAIN) err_sys("mq_receive error"); Sigprocmask(SIG_UNBLOCK, &newmask, NULL); } exit(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 (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; }
int main(int argc, char **argv) { mqd_t mqd; void *buff; ssize_t n; sigset_t zeromask, newmask, oldmask; struct mq_attr attr; struct sigevent sigev; if (argc != 2) err_quit("usage: mqnotifysig2 <name>"); /* 4open queue, get attributes, allocate read buffer */ mqd = Mq_open(argv[1], O_RDONLY); Mq_getattr(mqd, &attr); buff = Malloc(attr.mq_msgsize); Sigemptyset(&zeromask); /* no signals blocked */ Sigemptyset(&newmask); Sigemptyset(&oldmask); Sigaddset(&newmask, SIGUSR1); /* 4establish signal handler, enable notification */ Signal(SIGUSR1, sig_usr1); sigev.sigev_notify = SIGEV_SIGNAL; sigev.sigev_signo = SIGUSR1; Mq_notify(mqd, &sigev); for ( ; ; ) { Sigprocmask(SIG_BLOCK, &newmask, &oldmask); /* block SIGUSR1 */ while (mqflag == 0) sigsuspend(&zeromask); mqflag = 0; /* reset flag */ Mq_notify(mqd, &sigev); /* reregister first */ n = Mq_receive(mqd, buff, attr.mq_msgsize, NULL); printf("read %ld bytes\n", (long) n); Sigprocmask(SIG_UNBLOCK, &newmask, NULL); /* unblock SIGUSR1 */ } exit(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 (SIGTSTP) from the kernel * when we type ctrl-c (ctrl-z) at the keyboard. */ void eval(char *cmdline) { // Get memory for our parsed input char temp[MAXLINE]; char** argv = (char**)temp; // Return if only newline if (strcmp(cmdline, "\n") == 0) { return; } // Parse the input int background = parseline(cmdline, argv); // Check if built in command if (builtin_cmd(argv) == 0) { // Not a built in command char* programName = argv[0]; // Block sigchild signal sigset_t mask; Sigemptyset(&mask); Sigaddset(&mask, SIGCHLD); Sigprocmask(SIG_BLOCK, &mask, NULL); // Create a child process pid_t pid = Fork(); // Unblock sichild signal Sigprocmask(SIG_UNBLOCK, &mask, NULL); if (pid == 0) { // Child // Assign it a new process group id setpgid(0,0); // Run the external program Execve(programName, argv, environ); } else { // Parent if (background == 0) { // Add it to the list addjob(jobs, pid, FG, cmdline); // Make the shell wait for the process in the fg waitfg(pid); } else { // Add it to the list addjob(jobs, pid, BG, cmdline); // Get the job id int jid = pid2jid(pid); // Print info on background job printf("[%d] (%d) %s", jid, pid, cmdline); } } } return; }
void dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen) { int n; const int on = 1; char sendline[MAXLINE], recvline[MAXLINE + 1]; fd_set rset; sigset_t sigset_alrm, sigset_empty; socklen_t len; struct sockaddr *preply_addr; preply_addr = (struct sockaddr *)Malloc(servlen); Setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)); FD_ZERO(&rset); Sigemptyset(&sigset_empty); sigemptyset(&sigset_alrm); Sigaddset(&sigset_alrm, SIGALRM); Signal(SIGALRM, recvfrom_alarm); while (Fgets(sendline, MAXLINE, fp) != NULL) { Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen); Sigprocmask(SIG_BLOCK, &sigset_alrm, NULL); alarm(5); for ( ; ; ) { FD_SET(sockfd, &rset); n = pselect(sockfd+1, &rset, NULL, NULL, NULL, &sigset_empty); if (n < 0) { if (errno = EINTR) break; else err_sys("pselect error"); } else if (n != 1) err_sys("pselect error: returned %d", n); len = servlen; n = Recvfrom(sockfd, recvline, MAXLINE, 0, preply_addr, &len); recvline[n] = 0; //null terminate printf("from %s: %s", (char *)Sock_ntop_host(preply_addr, len), recvline); } } free(preply_addr); }
/* signal block by pselect,and pselect is atom operation */ void dg_cli(FILE *fp,int sockfd,const SA* pservaddr,socklen_t servlen){ int n; const int on = 1; char sendline[MAXLINE+1],recvline[MAXLINE+1]; fd_set rset; socklen_t len; sigset_t sigset_empty,sigset_alrm; struct sockaddr_in *preply_addr; preply_addr = Malloc(sizeof(struct sockaddr_in)); FD_ZERO(&rset); Sigemptyset(&sigset_empty); Sigemptyset(&sigset_alrm); Sigaddset(&sigset_alrm,SIGALRM); Signal(SIGALRM,recvfrom_alrm); while(Fgets(sendline,MAXLINE,fp) != NULL){ len = servlen; sendto(sockfd,sendline,strlen(sendline),0,pservaddr,len); Sigprocmast(SIG_BLOCK,&sigset_alrm,NULL); alarm(5); for(;;){ FD_SET(sockfd,&rset); n = pselect(sockfd+1,&rset,NULL,NULL,NULL,&sigset_empty); if(n < 0){ if(errno == EINTR) break; else err_quit("pselect error!"); }else if(n != 1){ err_quit("pselect error %d\n",n); } n = recvfrom(sockfd,recvline,MAXLINE,0,preply_addr,&len); recvlien[n] = 0; printf("from %s:%s\n",Sock_ntop_host(preply_addr,len),recvfrom); } } }
/* * This helper function can handle the "fg" command * 1. Get the pid and jid. * 2. Set the job state to FG. * 3. Send the SIGCONT signal. * 4. Wait for the process because it is a foreground child process. */ void dofg(char **argv) { pid_t pid; int jid; sigset_t mask; pid = getargvpid(argv); jid = pid2jid(pid); job_list[jid - 1].state = FG; tcsetpgrp(STDIN_FILENO, pid); Kill(pid, SIGCONT); Sigemptyset(&mask); while (fgpid(job_list)) { sigsuspend(&mask); } }
/* * 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 mask_one, prev_one, mask_all; int state; pid_t pid; /* 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 (tok.builtins == BUILTIN_NONE) { Sigfillset(&mask_all); Sigemptyset(&mask_one); Sigaddset(&mask_one, SIGCHLD); Sigprocmask(SIG_BLOCK, &mask_one, &prev_one); /* Block SIGCHLD */ if ((pid = Fork()) == 0) { Sigprocmask(SIG_SETMASK, &prev_one, NULL); /* Unblock SIGCHLD */ if (execve(tok.argv[0], tok.argv, environ) < 0) { printf("%s: Command not found.\n", tok.argv[0]); exit(0); } } /* Parent process */ Sigprocmask(SIG_BLOCK, &mask_all, NULL); /* Block all */ state = bg ? BG : FG; addjob(job_list, pid, state, cmdline); Sigprocmask(SIG_SETMASK, &prev_one, NULL); /* Unblock SIGCHLD */ if (state == BG) { printf("[%d] (%d) %s", pid2jid(pid), pid, cmdline); } else { } // Sigprocmask(SIG_SETMASK, &prev_one, NULL); } 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; }
int main(int argc,char **argv) { int i,j; pid_t pid; sigset_t newset; union sigval val; printf("SIGRTMIN = %d,SIGRTMAX = %d\n",(int) SIGRTMIN,(int )SIGRTMAX ); if((pid=Fork())==0) { Sigemptyset(&newset); Sigaddset(&newset,SIGRTMAX); Sigaddset(&newset,SIGRTMAX-1); Sigaddset(&newset,SIGRTMAX-2); Sigprocmask(SIG_BLOCK,&newset,NULL); Signal_rt(SIGRTMAX,sig_rt); Signal_rt(SIGRTMAX-1,sig_rt); signal_rt(SIGRTMAX-2,sig_rt); sleep(6); Sigprocmask(SIG_UNBLOCK,&newset,NULL); sleep(3); exit(0); } sleep(3); for(i=SIGRTMAX;i>=SIGRTMAX-2;i--) { for(j=0;j<=2;j++) { val.sival_int=j; Sigqueue(pid,i,val); printf("sent signal %d, val= %d\n",i,j); } } exit(0); }
void dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen) { int n; const int on = 1; char sendline[MAXLINE], recvline[MAXLINE + 1]; sigset_t sigset_alrm; socklen_t len; struct sockaddr *preply_addr; preply_addr = Malloc(servlen); Setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)); Sigemptyset(&sigset_alrm); Sigaddset(&sigset_alrm, SIGALRM); Signal(SIGALRM, recvfrom_alarm); while (Fgets(sendline, MAXLINE, fp) != NULL) { Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen); alarm(5); for ( ; ; ) { len = servlen; Sigprocmask(SIG_UNBLOCK, &sigset_alrm, NULL); n = recvfrom(sockfd, recvline, MAXLINE, 0, preply_addr, &len); Sigprocmask(SIG_BLOCK, &sigset_alrm, NULL); if (n < 0) { if (errno == EINTR) break; /* waited long enough for replies */ else err_sys("recvfrom error"); } else { recvline[n] = 0; /* null terminate */ printf("from %s: %s", Sock_ntop_host(preply_addr, len), recvline); } } } free(preply_addr); }
/* * 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_all, mask_one, prev_one; /* Signal mask */ Sigfillset(&mask_all); Sigemptyset(&mask_one); Sigaddset(&mask_one, SIGCHLD); strcpy(buf, cmdline); bg = parseline(buf, argv); if (argv[0] == NULL) return; /* Ignore empty lines */ if (!builtin_cmd(argv)) { Sigprocmask(SIG_BLOCK, &mask_one, &prev_one); /* Block SIGCHLD */ if ((pid = Fork()) == 0) { /* Child runs user job */ Sigprocmask(SIG_SETMASK, &prev_one, NULL); /* Unblock SIGCHLD */ setpgid(0, 0); if (execve(argv[0], argv, environ) < 0) { printf("%s: Command not found\n", argv[0]); exit(0); } } if (!bg) { /* Parent waits for foreground job to terminate */ Sigprocmask(SIG_BLOCK, &mask_all, NULL); addjob(jobs, pid, FG, cmdline); Sigprocmask(SIG_SETMASK, &prev_one, NULL); /* Unblock SIGCHLD */ waitfg(pid); } else { /* Background job */ Sigprocmask(SIG_BLOCK, &mask_all, NULL); addjob(jobs, pid, BG, cmdline); printf("[%d] (%d) %s", pid2jid(pid), pid, cmdline); Sigprocmask(SIG_SETMASK, &prev_one, NULL); /* Unblock SIGCHLD */ } } return; }
int main(int argc, char **argv) { int signo; mqd_t mqd; void *buff; ssize_t n; sigset_t newmask; struct mq_attr attr; struct sigevent sigev; if (argc != 2) err_quit("usage: mqnotifysig4 <name>"); /* 4open queue, get attributes, allocate read buffer */ mqd = Mq_open(argv[1], O_RDONLY | O_NONBLOCK); Mq_getattr(mqd, &attr); buff = Malloc(attr.mq_msgsize); Sigemptyset(&newmask); Sigaddset(&newmask, SIGUSR1); Sigprocmask(SIG_BLOCK, &newmask, NULL); /* block SIGUSR1 */ /* 4establish signal handler, enable notification */ sigev.sigev_notify = SIGEV_SIGNAL; sigev.sigev_signo = SIGUSR1; Mq_notify(mqd, &sigev); for ( ; ; ) { Sigwait(&newmask, &signo); if (signo == SIGUSR1) { Mq_notify(mqd, &sigev); /* reregister first */ while ( (n = mq_receive(mqd, buff, attr.mq_msgsize, NULL)) >= 0) { printf("read %ld bytes\n", (long) n); } if (errno != EAGAIN) err_sys("mq_receive error"); } } exit(0); }
/* * 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, SIGTSTP, SIGTTIN or SIGTTOU 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) { /* Fig. 8.41 8.18 */ int olderrno = errno; int status; pid_t p_id; struct job_t *job_ins; sigset_t prev, mask_all; Sigemptyset(&mask_all); Sigaddset(&mask_all, SIGCHLD); Sigaddset(&mask_all, SIGINT); Sigaddset(&mask_all, SIGTSTP); while((p_id = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) { /* Normally finish/exit */ if (WIFEXITED(status)) { Sigprocmask(SIG_BLOCK, &mask_all, &prev); /* Block */ deletejob(job_list, p_id); Sigprocmask(SIG_SETMASK, &prev, NULL); /* Unblock */ } else if (WIFSIGNALED(status)) { /* Terminated by other signal */ /* Order is fixed, otherwise it cause bugs. */ printf("Job [%d] (%d) terminated by signal %d\n", pid2jid(p_id), p_id, WTERMSIG(status)); Sigprocmask(SIG_BLOCK, &mask_all, &prev); /* Block */ deletejob(job_list, p_id); Sigprocmask(SIG_SETMASK, &prev, NULL); /* Unblock */ } else if (WIFSTOPPED(status)) { /* Stopped by signal, need to modified the state to stopped */ /* Order is fixed, otherwise it cause bugs. */ printf("Job [%d] (%d) stopped by signal %d\n", pid2jid(p_id), p_id, WSTOPSIG(status)); job_ins = getjobpid(job_list, p_id); job_ins -> state = ST; } } errno = olderrno; }
/* * 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; }
/* * builtin_fg_handler - Change a stopped or running * background job into a running foreground job */ void builtin_fg_handler(struct cmdline_tokens *tok) { /* TODO refactored */ sigset_t prev, mask_all; Sigemptyset(&mask_all); Sigaddset(&mask_all, SIGCHLD); Sigaddset(&mask_all, SIGINT); Sigaddset(&mask_all, SIGTSTP); struct job_t *job_ins = id2job(tok, tok -> argc); /* Modify the status of candidate process, fire the signal */ Sigprocmask(SIG_BLOCK, &mask_all, &prev); /* Block */ job_ins -> state = FG; Sigprocmask(SIG_SETMASK, &prev, NULL); /* Unblock */ Kill(job_ins -> pid, SIGCONT); /* Wait to stop */ wait_fg_job(job_ins -> state); }
/* * builtin_bg_handler - Change a stopped background job * into a running background job */ void builtin_bg_handler(struct cmdline_tokens *tok) { /* TODO */ sigset_t prev, mask_all; Sigemptyset(&mask_all); Sigaddset(&mask_all, SIGCHLD); Sigaddset(&mask_all, SIGINT); Sigaddset(&mask_all, SIGTSTP); struct job_t *job_ins = id2job(tok, tok -> argc); /* Fire the signal */ if (ST == (job_ins -> state)) { Sigprocmask(SIG_BLOCK, &mask_all, &prev); /* Block */ job_ins -> state = BG; Sigprocmask(SIG_SETMASK, &prev, NULL); /* Unblock */ Kill(job_ins -> pid, SIGCONT); printf("[%d] (%d) %s\n", job_ins -> jid, job_ins -> pid, job_ins -> cmdline); } }
/* * waitfg - Block until process pid is no longer the * foreground process */ void waitfg(pid_t pid) { sigset_t mask, prev; Log("WAITFG [0]\n", 11); Sigemptyset(&mask); Sigaddset(&mask, SIGCHLD); Sigprocmask(SIG_BLOCK, &mask, &prev); Log("WAITFG [1]\n", 11); while (atomic_fggpid == pid) { Log("WAITFG [2]\n", 11); Sigsuspend(&prev); } Log("WAITFG [3]\n", 11); Sigprocmask(SIG_SETMASK, &prev, NULL); }
/* * 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 cpid ; //child's pid sigset_t mask; sigset_t prev; int parseId; //prepare for the jid or pid in the future int state; struct job_t* job; //use for the fg or bg int infg = 0; //default STDIN int outfg = 1; //default STDOUT /* Create block signal */ Sigemptyset(&mask); /* block int stp child signal with the block signal*/ Sigaddset(&mask, SIGINT); Sigaddset(&mask, SIGTSTP); Sigaddset(&mask, SIGCHLD); /* Parse command line */ bg = parseline(cmdline, &tok); if (bg == -1) /* parsing error */ return; if (tok.argv[0] == NULL) /* ignore empty lines */ return; /* When there is a redirect input or output , change the value infg and out fg to the file */ if (tok.infile != NULL) { infg = Open(tok.infile, O_RDONLY, 0); } if (tok.outfile!=NULL) { outfg = Open(tok.outfile, O_WRONLY,0); } /* There are 5 situations: 1. Not a builtin command 2. Quit 3. list jobs 4. builtin foreground jobs 5. builtin background jobs */ switch (tok.builtins) { case BUILTIN_NONE: //we need to fork and create a child process Sigprocmask(SIG_BLOCK, &mask, &prev); if ((cpid=Fork())==0) { //in the child process //we should unblock all signals Sigprocmask(SIG_UNBLOCK, &mask, NULL); cpid = getpid(); //set child process group to avoid race condition setpgid(cpid,cpid); Sigprocmask(SIG_SETMASK, &prev, NULL); //set the in and out direction if(infg != 0){ Dup2(infg, 0); Close(infg); } if (outfg != 1) { Dup2(outfg, 1); Close(outfg); } //process the outside command Execve(tok.argv[0], tok.argv, environ); }else{ //in the parent process //determin whether it is a bg or fg if (bg==1) { state = BG; } else{ state = FG; } //add the job to job list addjob(job_list, cpid, state, cmdline); //get the job from job list by pid job = getjobpid(job_list, cpid); //unblock all signal Sigprocmask(SIG_UNBLOCK, &mask, NULL); //use while to //set the shell wait until the job is stp or killed if(state == FG){ while (fgpid(job_list)!=0) { Sigsuspend(&prev); } } else{ //if bg, print the background job printf("[%d] (%d) %s\n",job->jid, job->pid,cmdline); } } break; case BUILTIN_QUIT: //use the exit function exit(0); break; case BUILTIN_JOBS: //use the listjobs function listjobs(job_list, outfg); break; case BUILTIN_BG: //get the jid if (tok.argv[1][0]=='%') { parseId = atoi(&tok.argv[1][1]); job = getjobjid(job_list, parseId); } //or get the pid else if ((tok.argv[1][0] >= '0')&&(tok.argv[1][0]<='9')) { parseId = atoi(&tok.argv[1][0]); job = getjobpid(job_list, (pid_t)parseId); } printf("[%d] (%d) %s\n",job->jid, job->pid,job->cmdline); //set the job state to BG job->state = BG; //send SIGCONT Kill(-(job->pid), SIGCONT); break; case BUILTIN_FG: //get jid if (tok.argv[1][0]=='%') { parseId = atoi(&tok.argv[1][1]); job = getjobjid(job_list, parseId); } //get pid else if ((tok.argv[1][0] >= '0')&&(tok.argv[1][0]<='9')) { parseId = atoi(&tok.argv[1][0]); job = getjobpid(job_list, (pid_t)parseId); } //set the job state to FG job->state = FG; //send SIGCONT Kill(-(job->pid), SIGCONT); break; default: break; } 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; }
/* * 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 (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? */ int job_state; /* initial value of job BG or FG based on bg */ int status; int check; struct cmdline_tokens tok; pid_t pid = -255; // Initialzing to int jid = -255; // dummy values sigset_t mask; sigset_t mask2; int flag = 0; //Used while processing "fg" built in command int infile_fd ; //file descriptor to be used for infile if specified in job int outfile_fd ; //file descriptor to be used for outfile if specified in job //Get shell pid tsh_pid = getpid(); //Intialize mask for blocked signal //Block SIGCHLD SIGINT SIGTSTP signals Sigemptyset(&mask); Sigemptyset(&mask2); Sigaddset(&mask,SIGCHLD); Sigaddset(&mask,SIGINT); Sigaddset(&mask2,SIGINT); Sigaddset(&mask,SIGTSTP); Sigaddset(&mask2,SIGTSTP); Sigprocmask(SIG_BLOCK,&mask,NULL); /* Parse command line */ bg = parseline(cmdline, &tok); if (bg == -1) return; /* parsing error */ if (tok.argv[0] == NULL) return; /* ignore empty lines */ /* If tok is a BUILTIN shell command */ if (tok.builtins != BUILTIN_NONE) { switch(tok.builtins) { //Built in command quit : //Send SIGKILL to all processes in shell case BUILTIN_QUIT : tsh_pid = getpid(); kill(-tsh_pid,SIGKILL); break; //List out all jobs when "jobs" called //Also open output file if redirection specified and //redirect jobs output to the new file's descriptor case BUILTIN_JOBS : if (tok.outfile != NULL) { outfile_fd = open(tok.outfile , O_WRONLY); listjobs(job_list,outfile_fd); break; } else listjobs(job_list,STDOUT_FILENO); break; // Parse job id or pid given with bg command // Change state from ST to BG in job_list // Send SIGCONT signal to job case BUILTIN_FG : if(*(char *)(tok.argv[1]) == '%' ) { jid = atoi( (((char *)(tok.argv[1])) + 1) ); pid = jid2pid(jid); } else { pid = atoi(tok.argv[1]); jid = pid2jid(pid); } change_job_state(job_list,pid,FG); flag = 1; //flag set because we want to jump into else clause below // to process resumed job as a foreground job Kill(-pid,SIGCONT); break; //Parse job id or pid given with bg command // Change state from ST to BG in job_list // Send SIGCONT signal to job case BUILTIN_BG : if(*(char *)(tok.argv[1]) == '%' ) { jid = atoi( (((char *)(tok.argv[1])) + 1) ); pid = jid2pid(jid); } else { pid = atoi(tok.argv[1]); jid = pid2jid(pid); } printjob(pid,STDOUT_FILENO); change_job_state(job_list,pid,BG); Kill(-pid,SIGCONT); break; case BUILTIN_NONE : break; default : break; } } //If tok is a external program to be run by shell else if ((tok.builtins == BUILTIN_NONE) || (flag == 1)) { if (flag == 1) bg = 0; if(!bg) job_state = FG; else job_state = BG; //Child process if ((pid = Fork()) == 0) { setpgid(0,0); //Start process in new group Signal(SIGINT, SIG_DFL); /* ctrl-c from child handled by parent's sigchld */ addjob(job_list,getpid(),job_state,cmdline); // If input/output redirection specified open given file and point STDIN/STDOUT descriptor // to new file's file descriptor if (tok.infile != NULL) { infile_fd = open(tok.infile , O_RDONLY); dup2(infile_fd,STDIN_FILENO); } if (tok.outfile != NULL) { outfile_fd = open(tok.outfile , O_WRONLY); dup2(outfile_fd,STDOUT_FILENO); } //Unblock masks inherited from parent process //and execute program using exec Sigprocmask(SIG_UNBLOCK,&mask,NULL); Execve(tok.argv[0],tok.argv,environ); } //Parent process //Add child to job list and unblock signal,also set fg_pid if job is foreground job addjob(job_list,pid,job_state,cmdline); if(!bg) fg_pid = pid; Sigprocmask(SIG_UNBLOCK,&mask2,NULL); //Until foreground process terminates SIGCHLD functionality is done here , SIGINT and SIGTSTP are handled by handlers if(!bg) { check = waitpid(pid,&status,WUNTRACED); if ((check<0) && (errno!=ECHILD)) unix_error("waitfg : wait pid error\n"); if ((check == pid) && WIFSTOPPED(status)){ print_sigtstp_job(job_list,pid,SIGTSTP,STDOUT_FILENO); //Print message that job was stopped by SIGSTP signal //Change stopped job state in list to ST (stopped) change_job_state(job_list,pid,ST); return; } if ((check == pid) && (WIFSIGNALED(status))) print_sigint_job(job_list,pid,WTERMSIG(status),STDOUT_FILENO); //Print message that job/pid was terminated by a signal deletejob(job_list,pid); Sigprocmask(SIG_UNBLOCK,&mask,NULL); } else { Sigprocmask(SIG_UNBLOCK,&mask,NULL); printjob(pid,STDOUT_FILENO); } } return; }