In article <l_**************@newsread2.news.pas.earthlink.net >,
Jackie <Ja**********@earthlink.net> wrote:
Hi everyone,
I'd like to know when and how signals are used (e.g. SIGFPE, SIGABRT,
SIGTERM, SIGSEGV, SIGINT)? Thank you so much.
They're typically raised by the system to indicate asynchronous events
that your program might be interested in. (You can also raise them
synchronously by calling raise(), which has the effect of invoking the
appropriate signal handler for the signal number you give it.)
When a signal is raised, the handler for that signal gets invoked; you
can install a signal handler to do something you want done when a signal
is raised. There's not much that you can depend on being able to do
in a signal handler, so usually the best way to do things is to set a
flag that your program checks occasionally and does appropriate things
(and then clears it) if it finds that it's set.
SIGFPE, SIGSEGV, and SIGILL are only raised after your program has somehow
invoked undefined behavior, so if your system supports it you can catch
these and your signal handler can find and attempt to fix the source of
the problem.
SIGABRT indicates an abnormal termination. (This is raised by calling
abort(), which fgets called for assert failures.) Trapping this would
be useful for, f'rexample, dumping post-mortem debugging information if
you run into an internal error.
(If it's raised as a result of calling abort(), it's considered a
synchronous signal, so you can call any library function other than
raise(). It's not immediately clear to me whether you're allowed to call
abort() again (which would apparently call raise() again), but it's also
not clear why you would want to.)
That leaves SIGINT and SIGTERM, for interactive attention requests and
termination requests, as the only ones that are really portably useful
in a working program[1]. Typically, if you're doing a long-running
computation that you want to be able to interrupt (to change the
parameters or provide additional input - if you want the program to be
able to run when the user doesn't have anything to say, this is a good way
to work around the fact that any stdio input will wait until there's input
there) or that needs to be able to be shut down cleanly in the middle of
a run, you'd install handlers that set an appropriate flag, and check the
flags and act appropriately at some appropriate point in your computation.
Here's an (oversimplified) example program of how you might want to
use them:
--------
/*Example of using SIGINT and SIGTERM in a program with a long-running
computation
Not compiled or tested, for instructional use only, standard disclaimers
apply
Written by Dave Vandervies 2004-07-28 to be used, abused, bent, folded,
or mutilated as appropriate.
*/
/*Alphabetical: the One True Order for #including standard headers.*/
#include <errno.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
volatile sig_atomic_t stop;
volatile sig_atomic_t interrupt;
void sighandler(int signum)
{
if(signum==SIGINT)
interrupt=1;
if(signum==SIGTERM)
stop=1;
/*Calling signal like this is specifically permitted (N869 7.14.1.1#5,
see also #4); whether it's needed is implementation-defined (N869
7.14.1.1#3).
*/
signal(signum,sighandler);
}
int do_long_computation(void)
{
unsigned long i,j;
for(i=0;i<ULONG_MAX;i++)
{
for(j=0;j<ULONG_MAX;j++)
{
if(stop)
{
printf("Termination request received, exiting (i=%lu, j=%lu)\n",i,j);
/*If we have any cleanup to do (saving a checkpoint file,
perhaps), we do it here
*/
return 1;
}
if(interrupt)
{
char buf[64];
unsigned long new_val;
printf("i=%lu, j=%lu\n",i,j);
fputs("You wanted a new value for i? Enter it now: ",stdout)
fflush(stdout);
fgets(buf,sizeof buf,stdin);
errno=0;
new_val=strtol(buf,NULL,0);
if(new_val==ULONG_MAX && errno==ERANGE)
puts("Invalid value, using old value");
else
i=new_val;
interrupt=0;
}
/*Do a part of the long computation here*/
}
}
return 0;
}
int main(void)
{
/*Pointer to function of the appropriate type for a signal handler.
We use this to check the return value of signal() against multiple
possible returns.
*/
void (*oldhandler)(int);
/*Start by installing the appropriate signal handlers (we want to do
this before we get into anything nontrivial; if we had enough setup
to justify a separate "initialize" function this would go there)
*/
/*This is, I'm told, the safe way to install a SIGINT handler on unix-
like systems (so that an interrupt from the terminal interrupts the
foreground program as the user expects and not something running in
the background), and does no harm elsewhere.
*/
/*signal() returns the old handler for this signal, or SIG_ERR on
failure.
*/
oldhandler=signal(SIGINT,sighandler);
if(oldhandler==SIG_IGN)
{
puts("SIGINT was ignored (perhaps we're in the background?), re-ignoring\n");
signal(SIGINT,SIG_IGN);
}
else if(oldhandler==SIG_ERR)
{
fputs("Unable to install handler for SIGINT, running in uninterruptible mode\n",stderr);
}
/*Since we're only checking against SIG_ERR here, we can use the return
value of signal() directly
*/
if(signal(SIGTERM,sighandler)==SIG_ERR)
{
fputs("Unable to install handler for SIGTERM, left with system default\n (this may cause an unclean shutdown)\n",stderr);
}
/*Now that we've got our signal handlers installed, we can start our
long computation secure in the knowledge that the user can cleanly
interrupt it (usually ^C on commonly used interactive platforms) or
terminate it (in a system-dependent manner), or at least has been
warned that attempting to do so won't work.
*/
if(do_long_computation())
{
/*Nonzero return indicates early interruption, so indicate that with
exit status
*/
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
--------
dave
[1] This is actually a slight stretch, since an implementation that isn't
attempting to provide nonzero QOI can legally implement the signal
handling code as:
--------
void SIG_DFL(int i) {}
void SIG_IGN(int i) {}
void SIG_ERR(int i) {}
void (*signal(int signum,void (*handler)(int)))(int)
{
/*SIG_IGN is the only signal handler we can install without an error*/
if(handler!=SIG_IGN)
return SIG_ERR;
else
/*We know the previous handler was SIG_IGN*/
return SIG_IGN;
}
int raise(int signum)
{
/*We know the handler is SIG_IGN, so the as-if rule says we can
get away with doing nothing here.
Return value of 0 means we successfully did nothing.
*/
return 0;
}
--------
But you're not likely to encounter implementations that attempt to be
useful and don't support signals, unless the underlying system doesn't
provide a way to implement them or makes it pathologically difficult.
--
Dave Vandervies
dj******@csclub.uwaterloo.ca
I'm not sure how I can describe it so as to be free from the possibility of
perverse interpretation; ultimately, communication can only occur between
people who want to communicate. --Anton van Straaten in comp.lang.scheme