"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". This implies that these so-called realtime signals have bounded/prioritised delivery times but it is unclear whether this is the case, and in fact a broader range of new functionality is introduced which is independent of real-time qualities.

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 3, in the range 32 through 34) are used by the threading library if active.

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 is that the real-time signals are queued. That is, for every real-time signal successfully sent, the process signal handler will be called (assuming that a handler has been established). Normal signals on the other hand will be merged if sent to a process which already has that signal pending, as explained in the libc manual.

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 non-realtime signals, and signals sent with kill() or equivalent mechanisms, si_code will be set to SI_USER. (Hmm, except on kernel 2.6.22.6 with glibc 2.6.1 I get SI_TKILL when the signal was sent via raise(), which is consistent with POSIX which states that raise may be equivalent to pthread_kill).

Other values for si_code are not documented here, with one or two exceptions.

The third argument to the new-style signal handler is a void pointer referred to as context. However its value is not defined.

Queueing a signal

The sigqueue() function provides a means to queue a signal, and provide additional signal data. The function fails immediately if queueing is not possible.
  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).

Return is 0 if successful, -1 otherwise. 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.

Note that sending non-realtime signals does not cause multiple signals of the same number to be queued. If a non-realtime signal is sent using sigqueue(), and the same signal is already pending in the specified process, sigqueue() returns 0 but does not actually queue the signal. (Well, with kernel 2.6.22.6 at least, you *can* have two of the same signal pending: one queued via sigqueue (to the process) plus one via raise or pthread_kill (to the thread). The thread-specific signal gets delivered first).

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

Portability note: POSIX says that to guarentee queueing rather than merging of a signal, SA_SIGINFO must be specified in the signal flags when the signal handler is setup using sigaction(), and the sigqueue() function must be used to send the signal. This is not the case on Linux; realtime signals are always queued if possible.

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.