CSC 357 Lecture Notes Week 8
Introduction to Signals and Pipes
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <unistd.h> 4 #include <termios.h> 5 #include <signal.h> 6 #include <sys/time.h> 7 8 9 /**** 10 * 11 * This program is a simple example of using setitimer and sigaction to 12 * generate and handle SIGALRM signals. The program sets up an interval timer 13 * that generates a SIGALRM once a second. The SIGALRM handler simply 14 * increments a signal counter and prints out its value. 15 * 16 * Before starting the timer, the program puts the terminal in "raw mode". 17 * This means that it does not echo input characters, wait for a newline to 18 * complete an input, or respond to interrupt characters. 19 * 20 * After starting the timer and setting up the handler, the main function waits 21 * for the user to type the character 'q' to quit. Since the terminal is in 22 * raw mode, typing 'q' is the only way to stop, since all other printable 23 * characters are ignored, and terminal interrupt characters are disabled. 24 * 25 */ 26 27 28 /** 29 * The ticks variable counts the number of one-second ticks since the program 30 * started. 31 */ 32 static int ticks; 33 34 /** 35 * The tick function is the handler for SIGALRM. 36 */ 37 void tick(int sig) { 38 printf("%d\r", ++ticks); 39 } 40 41 /** 42 * Set the terminal to "raw" input mode. This is defined by setting terminal 43 * control flags to noncanonical mode, turning off character echoing, and 44 * ignoring signaling characters. Before setting to raw mode, save the 45 * current mode so it can be restored later. After setting, return the 46 * saved mode. 47 * 48 * For explanatory details, see Sections 18.10, 18.11 of Stevens, the 49 * termio(7I) man page, and the tcsetattr(3C) man page. (To see a particular 50 * man page section, use the "-s" argument, e.g., "man -s 7I termio" on 51 * falcon/hornet.) 52 */ 53 struct termios set_raw_term_mode() { 54 struct termios cur_term_mode, raw_term_mode; 55 56 tcgetattr(STDIN_FILENO, &cur_term_mode); 57 raw_term_mode = cur_term_mode; 58 raw_term_mode.c_lflag &= ~(ICANON | ECHO | ISIG) ; 59 raw_term_mode.c_cc[VMIN] = 1 ; 60 raw_term_mode.c_cc[VTIME] = 0; 61 tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw_term_mode); 62 63 return cur_term_mode; 64 } 65 66 /** 67 * Restore the terminal mode to that saved by set_raw_term_mode. 68 */ 69 void restore_term_mode(struct termios saved_term_mode) { 70 tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_term_mode); 71 } 72 73 /** 74 * Set the terminal to raw mode, set up a one-second timer, set up the SIGARLM 75 * handler, and then wait for the user to type 'q'. 76 * 77 * For details of timer setup, see Stevens Section 6.1 and the man pages for 78 * setitimer(2) and gettimeofday(3C). 79 * 80 * For details of signal setup, see Stevens Section 10.14, and the man page for 81 * sigaction. 82 */ 83 int main(int argc, char** argv) { 84 85 struct termios saved_term_mode; /* saved entering terminal mode */ 86 struct itimerval tbuf; /* interval timer structure */ 87 struct sigaction action; /* signal action structure */ 88 89 /* 90 * Explain to the user how to quit. 91 */ 92 printf("Type 'q' to quit.\n\n"); 93 94 /* 95 * Initialize ticks to 0. 96 */ 97 ticks = 0; 98 99 /* 100 * Set up the SIGALRM handler. 101 */ 102 action.sa_handler = tick; /* set tick to be the handler function */ 103 sigemptyset(&action.sa_mask); /* clear out masked functions */ 104 action.sa_flags = 0; /* no special handling */ 105 106 /* 107 * Use the sigaction function to associate the signal action with SIGALRM. 108 */ 109 if (sigaction(SIGALRM, &action, NULL) < 0 ) { 110 perror("SIGALRM"); 111 exit(-1); 112 } 113 114 /* 115 * Define a one-second timer. 116 */ 117 tbuf.it_interval.tv_sec = 1; 118 tbuf.it_interval.tv_usec = 0; 119 tbuf.it_value.tv_sec = 1; 120 tbuf.it_value.tv_usec = 0; 121 122 /* 123 * Use the setitimer function to start the timer. 124 */ 125 if ( setitimer(ITIMER_REAL, &tbuf, NULL) == -1 ) { 126 perror("setitimer"); 127 exit(-1); /* should only fail for serious reasons */ 128 } 129 130 /* 131 * Set the terminal to raw mode. 132 */ 133 saved_term_mode = set_raw_term_mode(); 134 135 /* 136 * Busy wait until the user types 'q'. 137 */ 138 while (getchar() != 'q') {} 139 140 /* 141 * Restore the terminal to the mode it was in at program entry. 142 */ 143 restore_term_mode(saved_term_mode); 144 145 }
int sigaction(int sig, const struct sigaction *restrict act, struct sigaction *restrict oact);
struct sigaction { union { /* handling function */ void (*sa_handler)(int); /* void (*sa_sigaction)( int, siginfo_t*, void*); } funcptr; int sa_flags; /* options flags */ sigset_t sa_mask; /* signals to block */ }
typedef void Sigfunc(int); Sigfunc* signal(int, Sigfunc*);
We'll return to some additional signal topics from chapter 10 in a later lecture; in the meantime, we'll introduce the important concept of pipes, which you will use in Lab 7, and the upcoming programming assignment.
which pipes the output of "ps" through grep to find all processes owned by "gfisher".ps -fe | grep "gfisher"
int pipe(int filedes[2]);
1 #include <unistd.h> 2 #include <stdlib.h> 3 #include <stdio.h> 4 #include <string.h> 5 6 #define MAXLINE 100 7 #define err_sys(msg) perror(msg); exit(-1) 8 9 int main() { 10 int n; /* number of bytes read from the pipe */ 11 int fd[2]; /* the pipe read/write file descriptors */ 12 pid_t pid; /* pid of the child process */ 13 char line[MAXLINE]; /* line of input read from the pipe */ 14 15 /* 16 * Create a pipe. 17 */ 18 if (pipe(fd) < 0) { 19 err_sys("pipe"); 20 } 21 22 /* 23 * Fork a child process. 24 */ 25 if ((pid = fork()) < 0) { 26 err_sys("fork"); 27 } 28 29 /* 30 * Parent writes down the pipe, closing the unneeded read end of the pipe. 31 */ 32 else if (pid > 0) { 33 close(fd[0]); 34 write(fd[1], "Hello world0, strlen("Hello world0)); 35 } 36 37 /* 38 * Child reads the pipe, closing the unneded write end. It then writes the 39 * read data to stdout, just for confirmation. 40 */ 41 else { 42 close(fd[1]); 43 n = read(fd[0], line, MAXLINE); 44 write(STDOUT_FILENO, line, n); 45 } 46 exit(0); 47 }
1 /** 2 * Exec a program and read the first 80 chars of its stdout. 3 */ 4 5 #include <stdio.h> 6 #include <unistd.h> 7 #include <stdlib.h> 8 #include <sys/wait.h> 9 10 #define READ_SIZE 80 11 12 int main(int argc, char** argv) { 13 14 int fd[2]; 15 int pid; 16 char read_buf[READ_SIZE + 1]; 17 int chars_read; 18 19 /* 20 * Check for adequate args. 21 */ 22 if (argc < 1) { 23 fprintf(stderr, "usage: grab-stdout prog arg ..."); 24 exit(-1); 25 } 26 27 /* 28 * Create a pipe that will have its output written to by the executed 29 * program. 30 */ 31 pipe(fd); 32 33 /* 34 * Fork off a process to exec the program. 35 */ 36 if ((pid = fork()) < 0) { 37 perror("fork"); 38 exit(-1); 39 } 40 41 /* 42 * Forked child has its stdout be the write end of the pipe. 43 */ 44 else if (pid == 0) { 45 46 /* 47 * Don't need read end of pipe in child. 48 */ 49 close(fd[0]); 50 51 /* 52 * This use of dup2 makes the output end of the pipe be stdout. 53 */ 54 dup2(fd[1], STDOUT_FILENO); 55 56 /* 57 * Don't need fd[1] after the dup2. 58 */ 59 close(fd[1]); 60 61 /* 62 * Exec the program given in the command line, including any args. 63 */ 64 execvp(argv[1], &(argv[1])); 65 perror("exec"); 66 exit(-1); 67 } 68 69 /* 70 * Parent takes its input from the read end of the pipe. 71 */ 72 else { 73 /* 74 * Don't need write end of pipe in parent. 75 */ 76 close(fd[1]); 77 78 /* 79 * Read the data coming in on the read end of the pipe. 80 */ 81 chars_read = read(fd[0], read_buf, READ_SIZE); 82 83 /* 84 * Print data, if any read, otherwise print "no output" message. 85 */ 86 if (chars_read > 0) { 87 88 /* 89 * Null terminate the read string. 90 */ 91 read_buf[chars_read] = '\0'; 92 93 /* 94 * Print the string out, for confirmation. 95 */ 96 printf("read_buf:\n%s\n", read_buf); 97 } 98 99 else { 100 fprintf(stderr, "Program produced no output.\n"); 101 exit(-1); 102 } 103 } 104 105 exit(0); 106 }