- Overview
-
Table of Contents
- Special Member Functions: Constructors, Destructors, and the Assignment Operator
- Operator Overloading
- Memory Management
- Templates
- Namespaces
- Time and Date Library
- Streams
- Object-Oriented Programming and Design Principles
- The Standard Template Library (STL) and Generic Programming
- Exception Handling
- Runtime Type Information (RTTI)
- Signal Processing
- Creating Persistent Objects
- Bit Fields
- New Cast Operators
- Environment Variables
- Variadic Functions
- Pointers to Functions
- Function Objects
- Pointers to Members
- Lock Files
- Design Patterns
- Dynamic Linking
- Tips and Techniques
- Five Things You Need to Know About C++11 Unions
- A Tour of C99
- A Tour of C1X
- C++0X: The New Face of Standard C++
- C++0x Concurrency
- The Reflecting Circle
- We Have Mail
- The Soapbox
- Numeric Types and Arithmetic
- Careers
- Locales and Internationalization
Signal Processing
Last updated Jan 1, 2003.
Signal Processing
Signals are similar to hardware interrupts. They cause a process to branch from the current execution flow, perform a specific action, and resume execution from the point of interruption. In the following sections, I will dissect the ANSI C/C++ <csignal> (<signal.h> in C) library and demonstrate how to use its interfaces. I will then discuss the POSIX signal API.
Setting a handler
A signal is an integer value that the kernel delivers to a process. When a process receives a signal, it can react in one of the following manners:
Ignore the signal
Let the kernel perform the default operation associated with that signal
Catch the signal, i.e., have the kernel transfer control to a signal handler and resume the program from the place of interruption once the handler returns.
A signal handler is a function that the kernel automatically invokes when a signal occurs. The signal() function registers a handler for a given signal:
typedef void (*handler)(void); void * signal(int signum, handler);
The first argument is the signal's code. The second argument is an address of a user-defined function to be called when the signal signum occurs.
Instead of a function's address, the second argument may take two special values: SIG_IGN and SIG_DFL. SIG_IGN indicates that the signal should be ignored (note however that the SIGKILL and SIGSTOP signals cannot be blocked, caught, or ignored); SIG_DFL instructs the kernel to perform the default action when the signal is raised.
Signaling a Process
There are three ways to signal a process:
A process explicitly sends a signal to itself by calling raise()
A signal is sent from another process, say by using the kill() system call, a Perl script etc.
A signal is sent from the kernel. For instance, when the process has attempted to access memory it doesn't own or during system shutdown.
The following program registers a handler for a SIGTERM signal. It then raises such a signal and thus causes it to run:
#include <csignal>
#include <iostream>
using namespace std;
void term(int sig)
{
//..necessary cleanup operations before terminating
cout << "handling signal no." << sig << endl;
}
int main()
{
signal(SIGTERM, term); // register a SIGTERM handler
raise(SIGTERM); // will cause term() to run
}
ANSI <csignal> Limitations
What happens when a process that is already running a handler for a SIGx signal receives another SIGx signal? One solution is to let the kernel interrupt the process and run the handler once more. To allow this, the handler must be re-entrant. However, designing re-entrant handlers is too complicated (for further details on re-entrancy, check out the Online Resources below). The <csignal> solution to the recurring signal problem is to reset the handler to SIG_DFL before executing the user-defined handler. This is problematic, though: When two signals occur quickly, the kernel will run the handler for the first signal and perform the default action for the second one, which might terminate the process. Several alternative signal frameworks have evolved in the past three decades, each of which offers a different solution to the recurring signal problem. The <a href= "http://www.burningvoid.com/iaq/posix-signals.html">POSIX signal API</a> is the most mature and portable among them.
Introducing POSIX Signals
The POSIX signal functions operate on sets of signals packed in a sigset_t datatype. Here are their prototypes:
//Clears all the signals in pset. int sigemptyset(sigset_t * pset); //Fills pset with all available signals. int sigfillset(sigset_t * pset); //Adds signum to pset. int sigaddset(sigset_t * pset, int signum); //Removes signum from pset. int sigdelset(sigset_t * pset, int signum); //Returns a nonzero value if signum is included in pset, 0 otherwise. int sigismember(const sigset_t * pset, int signum);
sigaction() registers a handler for a specific signal:
int sigaction(int signum, struct sigaction * act, struct sigaction *prev);
The sigaction struct describes the kernel's handling of signum:
struct sigaction
{
sighanlder_t sa_hanlder;
sigset_t sa_mask; // list of signals to block
unsigned long sa_flags; // blocking mode
void (*sa_restorer)(void); // never used
};
sa_hanlder holds an address of a function that takes int and returns no value. It can also take one of two special values: SIG_DFL and SIG_IGN.
The POSIX API offers various services not present in the ANSI <csignal> library. These include the ability to block incoming signals and retrieve currently pending signals. Let's look at them.
Blocking Signals
The sigprocmask() blocks and unblocks signals:
int sigprocmask(int mode, const sigset_t* newmask,
sigset_t * oldmask);
mode takes one of the following values:
SIG_BLOCK -- add the signals in newmask to the current signal mask.
SIG_UNBLOCK -- remove the signals in newmask from the current signal mask.
SIG_SETMASK -- block only the signals in newmask
Retrieving Pending Signals
Blocked signals wait until the process is ready to receive them. Such signals are said to be pending and can be retrieved by calling sigpending():
int sigpending(sigset_t * pset);
Summary
The standard <csignal> library provides rudimentary signal handling features that enable a process to install a handler for a certain signal, raise that signal and have the kernel run the specific handler for it. Although this implementation is sufficient for basic signal handling, it suffers from serious limitations such as its inability to block incoming signals or retrieve currently pending signals. For this reason, alternative signal libraries such as POSIX's are currently more widely-used in production code.


