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