PageRenderTime 19ms CodeModel.GetById 11ms app.highlight 3ms RepoModel.GetById 2ms app.codeStats 0ms

/share/doc/psd/04.uprog/p6

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