Previous post covers the basics of Linux signals. This post illustrates how to install a Linux signal handler with examples.
Linux provides two methods to install signal handler, signal and sigaction. sigaction is the preferred method over signal because signal behaves differently on different UNIX and UNIX-like OSes and therefore less compatible than sigaction. This post covers sigaction only.
Before going to sigaction system call, we go through several functions which we’ll need later. You can also go the end of the post to read the source code first and refer to the explaination later.
sigprocmask System Call
A signal can be blocked. In this case, it is not delivered to the process until it is unblocked. After the signal is generated and before it is delivered, the signal is in pending state. sigprocmask system call is used to query blocked signals. It has the following prototype,
how: specify how to change the block signal set. It can be one of the three values. SIG_BLOCK: the set of blocked signal is the union of current set and the input parameter set SIG_UNBLOCK: the signals specified in set is removed from the set of blocked signals SIG_SETMASK: the set of blocked signals is set to same as the input parameter set
set: if not null, the set value is used to according to the description for how. If null, the blocked signal set is unchanged and how has no meeting.
oldset: if not null, the previous value of the signal mask is returned through oldset.
Signal Sets System Calls
The sigprocmask function returns a data structure of sigset_t type, several functions are defined to manipulate the signal set.
One can tell what the functions do from their names.
pause and sigsuspend System Calls
Two system calls are defined to suspend the execution of a process to wait for a signal to be caught, pause() and sigsuspend(). Their prototypes are as below,
pause: Suspends execution until any signal is caught. It only returns when a signal was caught and the signal catching function returned. In this case, it returns -1 and errno is set to EINTR.
sigsuspend: Temporarily changes the signal mask and suspends execution until one of the unmasked signals is caught. The calling process’s signal maks is temporarily replaced by value given in mask input parameter until the sigsuspend returns or the process is terminated.
Note that sigsuspend always returns -1, normally with the error EINTR.
Now we explain the sigaction system call.
sigaction System Call
sigaction is a system call to query and change the actions taken when a process receives a particular signal. The sigaction system call has the following prototype,
signum: specify the signal
act: if not NULL, the action specified by act is installed.
oldact: if not NULL, the previous action is returned.
And the data structure sigaction is defined as below,
The parameters have the following meanings,
sa_handler: specifies the action taken to the signal, SIG_DFL (default action), SIG_IGN (ignore the signal) or a function pointer to a user defined signal handler. The defined signal handler should accepts the signal number as the only input argument.
sa_sigaction: if SA_SIGINFO flag is set in sa_flags, the handler pointed by sa_sigaction should be used.
The handler function should accept three arguments, the signal number, a pointer to an instance of siginfo_t structure (contains information about the signal) and a pointer to an object of type ucontext_t (contains the receiving process context which has been interrupted by the delivered signal. It is a void pointer can be casted to ucontext_t pointer).
The siginfo_t data structure has the following elements,
sa_mask: specify the signals should be blocked when the handler is in execution. In other words, the sa_mask adds signals to the signal mask of the process before signal handler is called. And when the signal handler function returns, the signal mask of the process is reset to its previous value. In this way, we can block certain signals when the signal handler is running. In addition, the signal that caused the signal handler to execute is also blocked, unless the SA_NODEFER flag is set.
sa_flags: specifies various options of handling the signal. The actions can be aggregated by the bitwise OR operation. The details of the flags can be referred from Linux man page.
sa_restorer: This is obsolete and should not be used.
Below is an example of installing a naive signal handler for SIGINT.
The code first installs the signal handler for SIGINT with sigaction system call. Inside the signal handler, it checks if the SIGINT signal is blocked. As described in sa_mask of sigaction system call, the SIGINT should be blocked inside the signal handler.
After installing the signal handler, the program calls pause() system call to wait for signal to occur. And it checks to see if the SIGINT is blocked again after the execution returns from the signal handler. This time, SIGINT should not be blocked.
Compile the code with command below,
gcc -o sigaction sigaction.c
And a sample execution is as below,
Figure 1. Sample Execution of Sigaction Program
SA_RESTART and Interrupted System Call
You may notice that the we set the sa_flags with SA_RESTART in the example above. This has something to do with system calls.
When a signal is caught while a system call is blocked (e.g. waiting for IO), then two results can happen after the signal handler:
- the call is restarted automatically after the signal handler returns
- the call fails with errno set to EINTR
With SA_RESTART flag set, some system calls are restarted automatically after the signal handler, including open, wait, waitpid etc. Those system calls return failure if SA_RESTART is not set.
There’re some system calls return failure regardless SA_RESTART is set or not. A special case is the sleep function, which is never restarted but the number of seconds remaining is returned instead of failure.
The detailed list of system calls that are restarted can found with “man 7 signal” command.
The Async-signal-safe Functions
When a signal is caught by a signal handler, the normal execution is temporarily interrupted by the signal handler. If the signal handler returns instead of terminating the process, the execution continues at where it is interrupted. Special attention needs to be paid for functions inside signal handler because we don’t want signal handler code to interfere with other code.
A bad example of this is the signal handler changes the global static variable which is used by the code after the handler.
POSIX standard defines a list of safe functions which can be called inside the signal handler, the detailed list can be obtained with “man 7 signal”.
Note that in the example above, we called printf() function inside the signal handler, which is actually not safe. If the signal is caught when we are calling printf inside the main function, the results may be unexpected.
1. The Linux Signals Handling Model: http://www.linuxjournal.com/article/3985
2. Linux Signals for the Application Programmer: http://www.linuxjournal.com/article/6483
3. Linux sigaction man page.
4. Linux signal man page (man 7 signal).
5. Advaned Programming in the UNIX environment