SIGNALS \(em INTERRUPTS AND ALL THAT
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:
caused by hanging up the phone;
When one of these events occurs,
processes which were started
from the corresponding terminal;
unless other arrangements have been made,
case, a core image file is written for debugging
The routine which alters the default action
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.
gives names for the various arguments, and should always be included
causes interrupts to be ignored, while
restores the default action of process termination.
returns the previous value of the 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
Most commonly this facility is used
to allow the program to clean up
unfinished business before terminating, for example to
if (signal(SIGINT, SIG_IGN) != SIG_IGN)
Why the test and the double call to
Recall that signals like interrupt are sent to
processes started from a particular terminal.
Accordingly, when a program is to be run
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
that would undo the shell's effort to protect it
when run in the background.
The solution, shown above, is to test the state of interrupt handling,
and to continue to ignore interrupts if they are already being ignored.
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.
A more sophisticated program may wish to intercept
an interrupt and interpret it as a request
and return to its own command-processing loop.
interrupting a long printout should not cause it
to terminate and lose the work
The outline of the code for this case is probably best written like this:
int (*istat)(), onintr();
istat = signal(SIGINT, SIG_IGN); /* save original status */
setjmp(sjbuf); /* save current stack position */
/* main processing loop */
longjmp(sjbuf); /* return to saved state */
an object in which the state
is such an object; it is an array of some sort.
When an interrupt occurs,
which can print a message, set flags, or whatever.
takes as argument an object stored into by
to the location after the call to
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.
gets set again after an interrupt occurs.
This is necessary; most signals are automatically
reset to their default action when they occur.
Some programs that want to detect signals simply can't be stopped
for example in the middle of updating a linked list.
If the routine called on occurrence of a signal
returns instead of calling
at the exact point it was interrupted.
The interrupt flag can then be tested later.
There is one difficulty associated with this
Suppose the program is reading the
terminal when the interrupt is sent.
The specified routine is duly called; it sets its flag
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.
Thus programs which catch and resume
execution after signals should be prepared for ``errors''
which are caused by interrupted
(The ones to watch out for are reads from a terminal,
resets the interrupt signal, and returns,
should usually include code like the following when it reads
/* EOF caused by interrupt */
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:
signal(SIGINT, SIG_IGN); /* ignore interrupts */
wait(&status); /* until the child is done */
signal(SIGINT, onintr); /* restore interrupts */
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
system(s) /* run command string s */
register int (*istat)(), (*qstat)();
if ((pid = fork()) == 0) {
execl("/bin/sh", "sh", "-c", s, 0);
istat = signal(SIGINT, SIG_IGN);
qstat = signal(SIGQUIT, SIG_IGN);
while ((w = wait(&status)) != pid && w != -1)
As an aside on declarations,
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.
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.
#define SIG_DFL (int (*)())0
#define SIG_IGN (int (*)())1