"Real time" signals on Linux

The GNU libc manual documents the original POSIX specification for signal handling functions. However subsequent POSIX standards expanded this functionality and the enhancements are often referred to as "realtime signals".

Introduction

The realtime signals are similar to the original signals, in that they have signal numbers and can be dealt with using the original signal functions (such as kill()). The realtime signals are those whose number is between the two constants SIGRTMIN and SIGRTMAX, defined in signal.h. With the GNU C library, SIGRTMIN is actually a function call*, as the threading library reserves some realtime signals for its own use.

The signals between SIGRTMIN and SIGRTMAX have no standard purpose and may be used by a program in any way that it sees fit. POSIX mandates at least 8 real time signals be available. On linux signal numbers between 32 and 63 are available, though as previously stated some (presently 2, historically 3, in the range 32 through 33 or 34) may be used by the GNU C library's threads support.

* Not sure if this is still true in 2023. The necessary signals might just be "always reserved".

** SIGRTMIN returns 34 on my current system. Not sure if three signals were ever really used; that may have been an error on my part.

Delivery priority

Lower number signals are given priority over higher number signals. In Linux this is true across the range of all signals, which incidentally gives the regular signals higher priority than all real-time signals (POSIX says that priority of real-time versus ordinary signals is unspecified, and only the realtime signals have a specified priority relative to each other).

Signal queues

One of the primary differences between normal signals and real-time signals on Linux is that multiple instances of individual real-time signals can be queued (and in fact, are always queued if possible) instead of being merged with an already-pending instance. Normal (non-realtime) signals on the other hand will be merged if sent to a process which already has that signal pending, as explained in the GNU libc manual; note that this is not true on all operating systems however (FreeBSD in particular queues at least some regular signals).

On Linux there is a system-wide limit to the number of queued signals. This is a potential source of denial-of-service attacks. However, there is a per-user resource limit to control the total number of pending signals that can be allocated to a single user.

Additional data

Another difference between regular and real-time signals is that real-time signals can carry additional signal data. The exact nature of this data is dependent on the type and source of the signal.

Signal reception

To receive the additional data that is sent with a real-time signal, a different signal handler type is required. For this purpose, a new field is present in the struct sigaction structure. It is:

void (*sa_sigaction)(int, siginfo_t *, void *);

A new flag, SA_SIGINFO, is used (in the sa_flags field) to specify that the sa_sigaction rather than the sa_handler field specifies the signal handler.

The siginfo_t structure has at least the following members:

    int si_signo;  // the signal number
    int si_errno;  // if non-zero, an errno value assosciated with this
                   // signal, as defined in 
    int si_code;   // signal code

The si_code field contains information on the source or cause of the signal. For signals sent with kill(), si_code will be set to SI_USER. A range of other values for si_code are not documented here, see the sigaction man page for relevant details.

The third argument to the new-style signal handler is a void pointer referred to as context. It is actually a pointer to a ucontext_t structure, as documented in the GNU libc manual, representing the execution context that was interrupted by delivery of the signal; it is not typically needed or used.

Queueing a signal

The sigqueue() function provides a means to queue a signal, and provide additional signal data.

  int sigqueue(pid_t pid, int signo, const union sigval value);

The union sigval has the following members:

    int sival_int;  // a simple integer value
    void *sival_ptr;  // a pointer

This union therefore allows either an integer or pointer value to be sent to the designated process, via the specified signal. As with kill(), a signal number of 0 does not result in a signal being delivered but still performs error checking, so that it can be used to verify if a process with a given pid actually exists (and determine whether it is owned by the same user).

According to POSIX, the function fails immediately (returning -1) if queueing the signal is not possible. On Linux (at least with GNU libc), only one instance of each of the non-realtime signals can be queued, but an error is not returned in case an already-pending non-realtime signal is sent; instead, the sent signal is silently discarded and no error is indicated. This does not appear to be POSIX-conformant behaviour.

Return is 0 if successful, -1 otherwise (but as noted above, Linux always returns success for non-realtime signals). The following error conditions (stored in the errno variable) apply:

EAGAIN - the signal buffer is full, so the signal could not be queued
EACCESS - the sending process does not have permission to send a signal to the specified process
EINVAL - the signal number is not valid
ESRCH - the specified process does not exist

On reception of a real-time signal queued using sigqueue(), an extended signal handler will see the si_code field of the siginfo_t structure is set to SI_QUEUE, and the value will be available from a field called si_value in the same structure. This is the only condition under which si_code can contain SI_QUEUE.

The differences between kill() and sigqueue() are:

Portability notes:

New signal wait functions

The sigsuspend() function provides no means to retrieve assosciated extra data sent with a queued signal. The new sigwaitinfo() function does exactly that. A third function, sigtimedwait(), also allows specifying a timeout value for a signal-wait operation.

   #include <signal.h>
   int sigwaitinfo(const sigset_t *set, const siginfo_t *info);
   int sigtimedwait(const sigset_t *se, const siginfo_t *info, const struct timespec *timeout);

These functions behave as you would expect if you are familiar with sigsuspend(). The specified signals should generally be blocked before the call. Both functions return the signal number received, or -1 if an error or timout occurs. For sigwaitinfo() the only possibly errno value is EINTR, which indicates that a signal not in the specified set was received.

The sigtimedwait() function has two additional errno values, EINVAL which specifies that the supplied timeout was invalid and EAGAIN which specifies that the timeout was reached without a signal reception occurring.