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