diff options
Diffstat (limited to 'share/doc/psd/04.uprog/p6')
-rw-r--r-- | share/doc/psd/04.uprog/p6 | 361 |
1 files changed, 361 insertions, 0 deletions
diff --git a/share/doc/psd/04.uprog/p6 b/share/doc/psd/04.uprog/p6 new file mode 100644 index 000000000000..c323d94b64e4 --- /dev/null +++ b/share/doc/psd/04.uprog/p6 @@ -0,0 +1,361 @@ +.\" Copyright (C) Caldera International Inc. 2001-2002. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions are +.\" met: +.\" +.\" Redistributions of source code and documentation must retain the above +.\" copyright notice, this list of conditions and the following +.\" disclaimer. +.\" +.\" Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" +.\" This product includes software developed or owned by Caldera +.\" International, Inc. Neither the name of Caldera International, Inc. +.\" nor the names of other contributors may be used to endorse or promote +.\" products derived from this software without specific prior written +.\" permission. +.\" +.\" USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA +.\" INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +.\" DISCLAIMED. IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE +.\" FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +.\" BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +.\" OR OTHERWISE) RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +.\" IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.\" @(#)p6 8.1 (Berkeley) 6/8/93 +.\" +.NH +SIGNALS \(em INTERRUPTS AND ALL THAT +.PP +This section is concerned with how to +deal gracefully with signals from +the outside world (like interrupts), and with program faults. +Since there's nothing very useful that +can be done from within C about program +faults, which arise mainly from illegal memory references +or from execution of peculiar instructions, +we'll discuss only the outside-world signals: +.IT interrupt , +which is sent when the +.UC DEL +character is typed; +.IT quit , +generated by the +.UC FS +character; +.IT hangup , +caused by hanging up the phone; +and +.IT terminate , +generated by the +.IT kill +command. +When one of these events occurs, +the signal is sent to +.IT all +processes which were started +from the corresponding terminal; +unless other arrangements have been made, +the signal +terminates the process. +In the +.IT quit +case, a core image file is written for debugging +purposes. +.PP +The routine which alters the default action +is +called +.UL signal . +It has two arguments: the first specifies the signal, and the second +specifies how to treat it. +The first argument is just a number code, but the second is the +address is either a function, or a somewhat strange code +that requests that the signal either be ignored, or that it be +given the default action. +The include file +.UL signal.h +gives names for the various arguments, and should always be included +when signals are used. +Thus +.P1 +#include <signal.h> + ... +signal(SIGINT, SIG_IGN); +.P2 +causes interrupts to be ignored, while +.P1 +signal(SIGINT, SIG_DFL); +.P2 +restores the default action of process termination. +In all cases, +.UL signal +returns the previous value of the signal. +The second argument to +.UL signal +may instead be the name of a function +(which has to be declared explicitly if +the compiler hasn't seen it already). +In this case, the named routine will be called +when the signal occurs. +Most commonly this facility is used +to allow the program to clean up +unfinished business before terminating, for example to +delete a temporary file: +.P1 +#include <signal.h> + +main() +{ + int onintr(); + + if (signal(SIGINT, SIG_IGN) != SIG_IGN) + signal(SIGINT, onintr); + + /* Process ... */ + + exit(0); +} + +onintr() +{ + unlink(tempfile); + exit(1); +} +.P2 +.PP +Why the test and the double call to +.UL signal ? +Recall that signals like interrupt are sent to +.ul +all +processes started from a particular terminal. +Accordingly, when a program is to be run +non-interactively +(started by +.UL & ), +the shell turns off interrupts for it +so it won't be stopped by interrupts intended for foreground processes. +If this program began by announcing that all interrupts were to be sent +to the +.UL onintr +routine regardless, +that would undo the shell's effort to protect it +when run in the background. +.PP +The solution, shown above, is to test the state of interrupt handling, +and to continue to ignore interrupts if they are already being ignored. +The code as written +depends on the fact that +.UL signal +returns the previous state of a particular signal. +If signals were already being ignored, the process should continue to ignore them; +otherwise, they should be caught. +.PP +A more sophisticated program may wish to intercept +an interrupt and interpret it as a request +to stop what it is doing +and return to its own command-processing loop. +Think of a text editor: +interrupting a long printout should not cause it +to terminate and lose the work +already done. +The outline of the code for this case is probably best written like this: +.P1 +#include <signal.h> +#include <setjmp.h> +jmp_buf sjbuf; + +main() +{ + int (*istat)(), onintr(); + + istat = signal(SIGINT, SIG_IGN); /* save original status */ + setjmp(sjbuf); /* save current stack position */ + if (istat != SIG_IGN) + signal(SIGINT, onintr); + + /* main processing loop */ +} +.P2 +.P1 +onintr() +{ + printf("\enInterrupt\en"); + longjmp(sjbuf); /* return to saved state */ +} +.P2 +The include file +.UL setjmp.h +declares the type +.UL jmp_buf +an object in which the state +can be saved. +.UL sjbuf +is such an object; it is an array of some sort. +The +.UL setjmp +routine then saves +the state of things. +When an interrupt occurs, +a call is forced to the +.UL onintr +routine, +which can print a message, set flags, or whatever. +.UL longjmp +takes as argument an object stored into by +.UL setjmp , +and restores control +to the location after the call to +.UL setjmp , +so control (and the stack level) will pop back +to the place in the main routine where +the signal is set up and the main loop entered. +Notice, by the way, that +the signal +gets set again after an interrupt occurs. +This is necessary; most signals are automatically +reset to their default action when they occur. +.PP +Some programs that want to detect signals simply can't be stopped +at an arbitrary point, +for example in the middle of updating a linked list. +If the routine called on occurrence of a signal +sets a flag and then +returns instead of calling +.UL exit +or +.UL longjmp , +execution will continue +at the exact point it was interrupted. +The interrupt flag can then be tested later. +.PP +There is one difficulty associated with this +approach. +Suppose the program is reading the +terminal when the interrupt is sent. +The specified routine is duly called; it sets its flag +and returns. +If it were really true, as we said +above, that ``execution resumes at the exact point it was interrupted,'' +the program would continue reading the terminal +until the user typed another line. +This behavior might well be confusing, since the user +might not know that the program is reading; +he presumably would prefer to have the signal take effect instantly. +The method chosen to resolve this difficulty +is to terminate the terminal read when execution +resumes after the signal, returning an error code +which indicates what happened. +.PP +Thus programs which catch and resume +execution after signals should be prepared for ``errors'' +which are caused by interrupted +system calls. +(The ones to watch out for are reads from a terminal, +.UL wait , +and +.UL pause .) +A program +whose +.UL onintr +program just sets +.UL intflag , +resets the interrupt signal, and returns, +should usually include code like the following when it reads +the standard input: +.P1 +if (getchar() == EOF) + if (intflag) + /* EOF caused by interrupt */ + else + /* true end-of-file */ +.P2 +.PP +A final subtlety to keep in mind becomes important +when signal-catching is combined with execution of other programs. +Suppose a program catches interrupts, and also includes +a method (like ``!'' in the editor) +whereby other programs can be executed. +Then the code should look something like this: +.P1 +if (fork() == 0) + execl(...); +signal(SIGINT, SIG_IGN); /* ignore interrupts */ +wait(&status); /* until the child is done */ +signal(SIGINT, onintr); /* restore interrupts */ +.P2 +Why is this? +Again, it's not obvious but not really difficult. +Suppose the program you call catches its own interrupts. +If you interrupt the subprogram, +it will get the signal and return to its +main loop, and probably read your terminal. +But the calling program will also pop out of +its wait for the subprogram and read your terminal. +Having two processes reading +your terminal is very unfortunate, +since the system figuratively flips a coin to decide +who should get each line of input. +A simple way out is to have the parent program +ignore interrupts until the child is done. +This reasoning is reflected in the standard I/O library function +.UL system : +.P1 +#include <signal.h> + +system(s) /* run command string s */ +char *s; +{ + int status, pid, w; + register int (*istat)(), (*qstat)(); + + if ((pid = fork()) == 0) { + execl("/bin/sh", "sh", "-c", s, 0); + _exit(127); + } + istat = signal(SIGINT, SIG_IGN); + qstat = signal(SIGQUIT, SIG_IGN); + while ((w = wait(&status)) != pid && w != -1) + ; + if (w == -1) + status = -1; + signal(SIGINT, istat); + signal(SIGQUIT, qstat); + return(status); +} +.P2 +.PP +As an aside on declarations, +the function +.UL signal +obviously has a rather strange second argument. +It is in fact a pointer to a function delivering an integer, +and this is also the type of the signal routine itself. +The two values +.UL SIG_IGN +and +.UL SIG_DFL +have the right type, but are chosen so they coincide with +no possible actual functions. +For the enthusiast, here is how they are defined for the PDP-11; +the definitions should be sufficiently ugly +and nonportable to encourage use of the include file. +.P1 +#define SIG_DFL (int (*)())0 +#define SIG_IGN (int (*)())1 +.P2 |