HOME | Software | Lotus Cars | DWARF | Kindle | Solar |
Every once in a while a programmer asks "Why does my application crash?" or "Why does my application hang?" or "Why does my application data get corrupted?" and it turns out that their code is doing serious work in a signal handler.
That's nearly always a bad idea. This note attempts to explain why it's a bad idea and and where to look further and suggests when you still might want to use signal handlers.
Corrections and suggests for improvement of this page are always welcome. Created March 8, 2004. Last Updated December 03, 2014(minor clarifications of the text).
The applicable language and system standards restrict what you can do in a signal handler very severely. Code that is legal and correct outside a signal handler is often invalid and incorrect inside a signal handler. The worst feature of code-in-a-signal-handler is that a problem in the handler may lead to an application crashes or misbehavior that is extremely difficult to reproduce or understand.
Symptoms of violating the rules can include such things as:
In other words, the symptoms are pretty general.
Normally POSIX signals are not queued. That means if 3 SIGHUPs come in close together it is unspecified whether any SIGHUP handler routine gets called once, twice, or 3 times. This means that an application with a handler that 'counts' signals or expects to get called on receipt of *every* signal of that type is not going to work reliably.
On the other hand, some signal-generating functions *do* support queueing signals (see current POSIX or Single Unix Specification for more information). The functions allowing signals to queue take a 'struct sigevent' structure argument (sigqueue() for example). The other issues with signals apply to queued signals. And not all signals can be queued.
Beginning with the REALTIME signal stuff, if you use SA_SIGINFO you can get information on what caused the signal. (See the POSIX documents). However, that does not make general POSIX calls from a signal handler any safer or invalidate any of the points made here.
C says all you can do is set a global variable declared a specific way. (volatile sig_atomic_t)
POSIX says that if you call an unsafe function and the signal interrupts an unsafe function, the behavior is undefined. You don't want undefined behavior. A specific list of functions is defined in POSIX as safe (all others in POSIX and C/C++ are 'unsafe', though your own written functions _may_ be safe if they are simple enough). You have no sure or portable way to know if the function interrupted is safe or not. You cannot call unsafe functions (malloc and printf are examples) in a signal handler and expect anything but chaos.
The POSIX cautions remain in effect, and the C rules apply too, because C++ pulls in C standard by reference.
You must use extern-C declarations for signal functions. Clause 1.9 says: "The value of any object not of type 'volatile sig_atomic_t' is unspecified and if you alter any object not of that type, the altered object becomes undefined." So you cannot rely on normal C++ class objects to be well formed. (Consider what it would mean for the signal to happen while the code was in the middle of a nest of constructors or destructors.) There is no portable way to use C++ exceptions in a signal handler (See Clause 18.7 for the specific wording). So if code in a signal handler throws an exception you are in trouble.
Using signals correctly is not often covered in C/C++ books. One book that has very useful information is
UNIX Network Programming: The Sockets Networking API Volume 1, Third Edition W. Richard Stevens, Bill Fenner, Andrew M. Rudoff Addison-Wesley, 2004
A short history of the POSIX standards is in this book (pp 26-28). You will find examples of proper use and cautions on improper use.
Chapter 10 in
Multithreaded Programming with Pthreads Bil Lewis, Daniel J Berg Prentice Hall, 1998
has proper cautions on using signals.
The C FAQ web page http://www.eskimo.com/~scs/C-faq/top.html is worth looking at though it has not been maintained much since the 2005 timeframe (AFAICT). Note that this is originally based on information from comp.lang.c, a newsgroup that has now vanished along with the rest of the Usenet newsgroups. The groups were really important until the world-wide-web came into common use and the groups have now disappeared, pretty much.
An official source of the C standard as of December 2009 is http://webstore.ansi.org/RecordDetail.aspx?sku=ISO/IEC+9899:1999 (priced at an outrageous US$349).
Originally (1988-89) the C standard was a paper photocopy of a document printed by a company known as Global Documents. After community outrage at the $270 (I think) price, the price declined to $18 for a pdf version and there was a $20 book with the complete standard in it (but with a number of errors in the book!). As of December 2009 the price from ANSI is outrageous again (I do not know when it changed). Ansi.org makes it hard to find the standard on its web pages and insists that one register to even search for any document they have for sale on their pages -- that seems insane to me. Instead of buying from ANSI, find a book that contains the standard text and use that text (and take the author's comments with a large grain of salt, just read the standard itself).
As of 2014 the situation has changed a lot and changed for the better. For example, in the USA I bought the C11 standard as a pdf download, INCITS+ISO+IEC+9899-2011[2012].pdf, for USD60 or so (which I consider reasonable). A paper version is quite expensive. The ISO version (from ISO) is quite expensive, even as a pdf as best I can tell. Searching for C/C++ language standards on the ansi.org webstore is difficult unless you already know a crucial search term. For C, 9899 is a useful term. For C++, 14882 is a useful term. Other useful terms are 'language c' and 'language c++'.
A C++ FAQ site is http://www.parashift.com/c++-faq-lite/. Marshall Cline is keeping this site updated as of December 2009, I recommend you take a look. At one time the standard was available for $18! See above comments about ansi.org...
ISO/IEC 9945-1:1996 (IEEE Std 1003.1, 1996 Edition) was a current POSIX ISO standard (contains the RealTime extensions and pthreads). Added to in IEEE Std 1003.1g (Protocol independent interfaces).
A slightly earlier version, IEEE Std 1003.1b-1993, has the REALTIME signal information but not the pthreads extensions.
Information on the POSIX projects as of 2003 is at http://www.pasc.org/standing/sd11.html and that site has links to sites that sell the POSIX standards, though the site seems rather dated now.
The Single Unix Specification is available for purchase at http://www.theopengroup.org/ click on Publications then Single Unix Specification. There notice the book entitled "The Single Unix Specification, The Authorized Guide to version 3" (Item G906) which also contains the specification on CD-ROM, which with shipping and tax once cost under USD90. The CD-ROM and the book also has Versions 1 and 2 of the Single Unix Specification, the Open Motif 2.2 source reference and much more.
As this is written in 2014 the latest Single Unix Specification is T101, Verion 4, 2013 edition. Possibly superseded by C138 Base Specifications Issue 7, 2013. It is difficult to make sense of the opengroup.org pricing and the extensive use of the word FREE on their web site. I think FREE means free to opengroup members.
http://www.UNIX.org/version3 is another approach to the same information (but harder to find the book this way).
The Single Unix Specification can be viewed for free at http://www.UNIX-systems.org/online.html (you do have to register to see it.)
This is a non-pthreads example and I've not had time to create a pthreads example. Sorry. That is a grave omission, I know.
I'm ignoring the advanced threads-specific aspects and signal handling facilities such as the "struct sigevent" and the function sigqueue() and related functions.
An example of sensible signal use might be an app that catches SIGTERM to close() one or more files and exit().
Another example might be a long-lived app (with a main loop of some sort) that a) in its SIGHUP handler sets a global (volatile sig_atomic_t) flag, and in its main loop tests that flag and then calls a function from the main loop to reread some (known to the app) configuration file and reconfigure itself (presumably a human changed the configuration file before sending SIGHUP).
The basic concept for safe signal use is simple:
That's the whole idea. Now all the messy actions needed are done in the main body of the application. Only a simple safe setting-of-a-global happens in the signal handler.
To make this concrete, a simple example which does no more than copy standard-input to standard-output, and does it slowly at that, follows.
/* To make this work, compile like cc signaltest.c Then run ./a.out In a different window ps -eaf |grep a.out find the process id (let's say it is 12345 for example), and do kill -HUP 12345 to see the a.out respond. Try typing a few characters to the a.out before sending the HUP; stdin is line-buffered, so no input will be echoed till <return> gets pressed to the a.out. You could change the test case to use non-buffered stdin: that would be interesting to experiment with... */ #include <signal.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <stdlib.h> /* POSIX example demonstrating basic signal use. David Anderson. March 2004, updated December 2009. Permission granted to copy/modify this C source example for any use without restriction. Calls to write() with a length > 1 byte are actually not coded quite right here as write() can write fewer bytes than were requested. See the Stevens et.al. book to see code doing that call portably and correctly (by wrapping it in a function with a loop). For a test case the following is adequate as written. */ #define STDIN 0 #define STDOUT 1 volatile sig_atomic_t got_hup; void catchup(); int main() { struct sigaction newact; char buf[1]; int r; memset(&newact, 0, sizeof(newact)); sigemptyset(&newact.sa_mask); sigaddset(&newact.sa_mask, SIGHUP); newact.sa_handler = catchup; r = sigaction(SIGHUP, &newact, 0); if (r == -1) { int myerr = errno; printf("Sigaction failed, errno %d (%s)\n", myerr, strerror(myerr)); } for (;;) { ssize_t count = 0; /* buf is size 1 */ if (got_hup) { char *m = "Safely undertake operations recognizing HUP\n"; got_hup = 0; /* We got a SIGHUP, so now, safely, do whatever we need to do. All normal operations allowed.*/ write(STDOUT, m, strlen(m)); } count = read(STDIN, buf, 1); if (count == 1) { /* Read succeeded, copy to output. */ ssize_t wcount = write(STDOUT, buf, 1); if (wcount != 1) { /* Give up here. */ exit(EXIT_FAILURE); } continue; /* Get next byte. */ } if (count == 0) { /* Read succeeded, end of file. */ char *m = "EOF read.\n"; write(STDOUT, m, strlen(m)); exit(EXIT_SUCCESS); } /* Failed. */ { int myerr = errno; if (myerr == EINTR) { char *m = "Interrupted read. Retry. \n"; write(STDOUT, m, strlen(m)); /* We should check for 'write' status, but we are going to quit anyway... */ continue; } /* Unexpected failure. */ exit(EXIT_FAILURE); } } } void catchup() { /* Safe handler action. */ got_hup = 1; }
https://www.prevanders.net/signals.html
This work is licensed under a
Creative Commons Attribution 4.0 International License.