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