2002-05-19 06:13:52 +00:00
|
|
|
.\" 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$
|
2002-05-19 06:11:50 +00:00
|
|
|
.\"
|
|
|
|
.\" @(#)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
|