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 }