summaryrefslogtreecommitdiffstats
path: root/share/doc/psd/04.uprog/p6
diff options
context:
space:
mode:
authorgrog <grog@FreeBSD.org>2002-05-19 06:11:50 +0000
committergrog <grog@FreeBSD.org>2002-05-19 06:11:50 +0000
commit2d717c6f9fef0e6fda4e55557f91a024e131d99e (patch)
tree80e9a5cb42879768b1004d8e85813a58aca2fb9d /share/doc/psd/04.uprog/p6
parent559398948817b63678ed666fe48329ec9dd78db7 (diff)
downloadFreeBSD-src-2d717c6f9fef0e6fda4e55557f91a024e131d99e.zip
FreeBSD-src-2d717c6f9fef0e6fda4e55557f91a024e131d99e.tar.gz
Initial checkin: 4.4BSD version. These files need to be updated with
current license information and adapted to the FreeBSD build environment before they will build. Approved by: David Taylor <davidt@caldera.com>
Diffstat (limited to 'share/doc/psd/04.uprog/p6')
-rw-r--r--share/doc/psd/04.uprog/p6328
1 files changed, 328 insertions, 0 deletions
diff --git a/share/doc/psd/04.uprog/p6 b/share/doc/psd/04.uprog/p6
new file mode 100644
index 0000000..8d93a36
--- /dev/null
+++ b/share/doc/psd/04.uprog/p6
@@ -0,0 +1,328 @@
+.\" This module is believed to contain source code proprietary to AT&T.
+.\" Use and redistribution is subject to the Berkeley Software License
+.\" Agreement and your Software Agreement with AT&T (Western Electric).
+.\"
+.\" @(#)p6 8.1 (Berkeley) 6/8/93
+.\"
+.\" $FreeBSD$
+.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
OpenPOWER on IntegriCloud