Commit | Line | Data |
---|---|---|
025689df TL |
1 | .NH |
2 | SIGNALS \(em INTERRUPTS AND ALL THAT | |
3 | .PP | |
4 | This section is concerned with how to | |
5 | deal gracefully with signals from | |
6 | the outside world (like interrupts), and with program faults. | |
7 | Since there's nothing very useful that | |
8 | can be done from within C about program | |
9 | faults, which arise mainly from illegal memory references | |
10 | or from execution of peculiar instructions, | |
11 | we'll discuss only the outside-world signals: | |
12 | .IT interrupt , | |
13 | which is sent when the | |
14 | .UC DEL | |
15 | character is typed; | |
16 | .IT quit , | |
17 | generated by the | |
18 | .UC FS | |
19 | character; | |
20 | .IT hangup , | |
21 | caused by hanging up the phone; | |
22 | and | |
23 | .IT terminate , | |
24 | generated by the | |
25 | .IT kill | |
26 | command. | |
27 | When one of these events occurs, | |
28 | the signal is sent to | |
29 | .IT all | |
30 | processes which were started | |
31 | from the corresponding terminal; | |
32 | unless other arrangements have been made, | |
33 | the signal | |
34 | terminates the process. | |
35 | In the | |
36 | .IT quit | |
37 | case, a core image file is written for debugging | |
38 | purposes. | |
39 | .PP | |
40 | The routine which alters the default action | |
41 | is | |
42 | called | |
43 | .UL signal . | |
44 | It has two arguments: the first specifies the signal, and the second | |
45 | specifies how to treat it. | |
46 | The first argument is just a number code, but the second is the | |
47 | address is either a function, or a somewhat strange code | |
48 | that requests that the signal either be ignored, or that it be | |
49 | given the default action. | |
50 | The include file | |
51 | .UL signal.h | |
52 | gives names for the various arguments, and should always be included | |
53 | when signals are used. | |
54 | Thus | |
55 | .P1 | |
56 | #include <signal.h> | |
57 | ... | |
58 | signal(SIGINT, SIG_IGN); | |
59 | .P2 | |
60 | causes interrupts to be ignored, while | |
61 | .P1 | |
62 | signal(SIGINT, SIG_DFL); | |
63 | .P2 | |
64 | restores the default action of process termination. | |
65 | In all cases, | |
66 | .UL signal | |
67 | returns the previous value of the signal. | |
68 | The second argument to | |
69 | .UL signal | |
70 | may instead be the name of a function | |
71 | (which has to be declared explicitly if | |
72 | the compiler hasn't seen it already). | |
73 | In this case, the named routine will be called | |
74 | when the signal occurs. | |
75 | Most commonly this facility is used | |
76 | to allow the program to clean up | |
77 | unfinished business before terminating, for example to | |
78 | delete a temporary file: | |
79 | .P1 | |
80 | #include <signal.h> | |
81 | ||
82 | main() | |
83 | { | |
84 | int onintr(); | |
85 | ||
86 | if (signal(SIGINT, SIG_IGN) != SIG_IGN) | |
87 | signal(SIGINT, onintr); | |
88 | ||
89 | /* Process ... */ | |
90 | ||
91 | exit(0); | |
92 | } | |
93 | ||
94 | onintr() | |
95 | { | |
96 | unlink(tempfile); | |
97 | exit(1); | |
98 | } | |
99 | .P2 | |
100 | .PP | |
101 | Why the test and the double call to | |
102 | .UL signal ? | |
103 | Recall that signals like interrupt are sent to | |
104 | .ul | |
105 | all | |
106 | processes started from a particular terminal. | |
107 | Accordingly, when a program is to be run | |
108 | non-interactively | |
109 | (started by | |
110 | .UL & ), | |
111 | the shell turns off interrupts for it | |
112 | so it won't be stopped by interrupts intended for foreground processes. | |
113 | If this program began by announcing that all interrupts were to be sent | |
114 | to the | |
115 | .UL onintr | |
116 | routine regardless, | |
117 | that would undo the shell's effort to protect it | |
118 | when run in the background. | |
119 | .PP | |
120 | The solution, shown above, is to test the state of interrupt handling, | |
121 | and to continue to ignore interrupts if they are already being ignored. | |
122 | The code as written | |
123 | depends on the fact that | |
124 | .UL signal | |
125 | returns the previous state of a particular signal. | |
126 | If signals were already being ignored, the process should continue to ignore them; | |
127 | otherwise, they should be caught. | |
128 | .PP | |
129 | A more sophisticated program may wish to intercept | |
130 | an interrupt and interpret it as a request | |
131 | to stop what it is doing | |
132 | and return to its own command-processing loop. | |
133 | Think of a text editor: | |
134 | interrupting a long printout should not cause it | |
135 | to terminate and lose the work | |
136 | already done. | |
137 | The outline of the code for this case is probably best written like this: | |
138 | .P1 | |
139 | #include <signal.h> | |
140 | #include <setjmp.h> | |
141 | jmp_buf sjbuf; | |
142 | ||
143 | main() | |
144 | { | |
145 | int (*istat)(), onintr(); | |
146 | ||
147 | istat = signal(SIGINT, SIG_IGN); /* save original status */ | |
148 | setjmp(sjbuf); /* save current stack position */ | |
149 | if (istat != SIG_IGN) | |
150 | signal(SIGINT, onintr); | |
151 | ||
152 | /* main processing loop */ | |
153 | } | |
154 | .P2 | |
155 | .P1 | |
156 | onintr() | |
157 | { | |
158 | printf("\nInterrupt\n"); | |
159 | longjmp(sjbuf); /* return to saved state */ | |
160 | } | |
161 | .P2 | |
162 | The include file | |
163 | .UL setjmp.h | |
164 | declares the type | |
165 | .UL jmp_buf | |
166 | an object in which the state | |
167 | can be saved. | |
168 | .UL sjbuf | |
169 | is such an object; it is an array of some sort. | |
170 | The | |
171 | .UL setjmp | |
172 | routine then saves | |
173 | the state of things. | |
174 | When an interrupt occurs, | |
175 | a call is forced to the | |
176 | .UL onintr | |
177 | routine, | |
178 | which can print a message, set flags, or whatever. | |
179 | .UL longjmp | |
180 | takes as argument an object stored into by | |
181 | .UL setjmp , | |
182 | and restores control | |
183 | to the location after the call to | |
184 | .UL setjmp , | |
185 | so control (and the stack level) will pop back | |
186 | to the place in the main routine where | |
187 | the signal is set up and the main loop entered. | |
188 | Notice, by the way, that | |
189 | the signal | |
190 | gets set again after an interrupt occurs. | |
191 | This is necessary; most signals are automatically | |
192 | reset to their default action when they occur. | |
193 | .PP | |
194 | Some programs that want to detect signals simply can't be stopped | |
195 | at an arbitrary point, | |
196 | for example in the middle of updating a linked list. | |
197 | If the routine called on occurrence of a signal | |
198 | sets a flag and then | |
199 | returns instead of calling | |
200 | .UL exit | |
201 | or | |
202 | .UL longjmp , | |
203 | execution will continue | |
204 | at the exact point it was interrupted. | |
205 | The interrupt flag can then be tested later. | |
206 | .PP | |
207 | There is one difficulty associated with this | |
208 | approach. | |
209 | Suppose the program is reading the | |
210 | terminal when the interrupt is sent. | |
211 | The specified routine is duly called; it sets its flag | |
212 | and returns. | |
213 | If it were really true, as we said | |
214 | above, that ``execution resumes at the exact point it was interrupted,'' | |
215 | the program would continue reading the terminal | |
216 | until the user typed another line. | |
217 | This behavior might well be confusing, since the user | |
218 | might not know that the program is reading; | |
219 | he presumably would prefer to have the signal take effect instantly. | |
220 | The method chosen to resolve this difficulty | |
221 | is to terminate the terminal read when execution | |
222 | resumes after the signal, returning an error code | |
223 | which indicates what happened. | |
224 | .PP | |
225 | Thus programs which catch and resume | |
226 | execution after signals should be prepared for ``errors'' | |
227 | which are caused by interrupted | |
228 | system calls. | |
229 | (The ones to watch out for are reads from a terminal, | |
230 | .UL wait , | |
231 | and | |
232 | .UL pause .) | |
233 | A program | |
234 | whose | |
235 | .UL onintr | |
236 | program just sets | |
237 | .UL intflag , | |
238 | resets the interrupt signal, and returns, | |
239 | should usually include code like the following when it reads | |
240 | the standard input: | |
241 | .P1 | |
242 | if (getchar() == EOF) | |
243 | if (intflag) | |
244 | /* EOF caused by interrupt */ | |
245 | else | |
246 | /* true end-of-file */ | |
247 | .P2 | |
248 | .PP | |
249 | A final subtlety to keep in mind becomes important | |
250 | when signal-catching is combined with execution of other programs. | |
251 | Suppose a program catches interrupts, and also includes | |
252 | a method (like ``!'' in the editor) | |
253 | whereby other programs can be executed. | |
254 | Then the code should look something like this: | |
255 | .P1 | |
256 | if (fork() == 0) | |
257 | execl(...); | |
258 | signal(SIGINT, SIG_IGN); /* ignore interrupts */ | |
259 | wait(&status); /* until the child is done */ | |
260 | signal(SIGINT, onintr); /* restore interrupts */ | |
261 | .P2 | |
262 | Why is this? | |
263 | Again, it's not obvious but not really difficult. | |
264 | Suppose the program you call catches its own interrupts. | |
265 | If you interrupt the subprogram, | |
266 | it will get the signal and return to its | |
267 | main loop, and probably read your terminal. | |
268 | But the calling program will also pop out of | |
269 | its wait for the subprogram and read your terminal. | |
270 | Having two processes reading | |
271 | your terminal is very unfortunate, | |
272 | since the system figuratively flips a coin to decide | |
273 | who should get each line of input. | |
274 | A simple way out is to have the parent program | |
275 | ignore interrupts until the child is done. | |
276 | This reasoning is reflected in the standard I/O library function | |
277 | .UL system : | |
278 | .P1 | |
279 | #include <signal.h> | |
280 | ||
281 | system(s) /* run command string s */ | |
282 | char *s; | |
283 | { | |
284 | int status, pid, w; | |
285 | register int (*istat)(), (*qstat)(); | |
286 | ||
287 | if ((pid = fork()) == 0) { | |
288 | execl("/bin/sh", "sh", "-c", s, 0); | |
289 | _exit(127); | |
290 | } | |
291 | istat = signal(SIGINT, SIG_IGN); | |
292 | qstat = signal(SIGQUIT, SIG_IGN); | |
293 | while ((w = wait(&status)) != pid && w != -1) | |
294 | ; | |
295 | if (w == -1) | |
296 | status = -1; | |
297 | signal(SIGINT, istat); | |
298 | signal(SIGQUIT, qstat); | |
299 | return(status); | |
300 | } | |
301 | .P2 | |
302 | .PP | |
303 | As an aside on declarations, | |
304 | the function | |
305 | .UL signal | |
306 | obviously has a rather strange second argument. | |
307 | It is in fact a pointer to a function delivering an integer, | |
308 | and this is also the type of the signal routine itself. | |
309 | The two values | |
310 | .UL SIG_IGN | |
311 | and | |
312 | .UL SIG_DFL | |
313 | have the right type, but are chosen so they coincide with | |
314 | no possible actual functions. | |
315 | For the enthusiast, here is how they are defined for the PDP-11; | |
316 | the definitions should be sufficiently ugly | |
317 | and nonportable to encourage use of the include file. | |
318 | .P1 | |
319 | #define SIG_DFL (int (*)())0 | |
320 | #define SIG_IGN (int (*)())1 | |
321 | .P2 |