Fundamentals 14 min read

Why Some Linux Signals Are Unreliable and How Real‑Time Signals Fix the Problem

This article explains the difference between unreliable (1‑31) and reliable real‑time (32‑64) Linux signals, demonstrates the behavior with two C programs, and reveals the kernel data structures and limits that govern signal queuing and delivery.

ITPUB
ITPUB
ITPUB
Why Some Linux Signals Are Unreliable and How Real‑Time Signals Fix the Problem

Background

Traditional UNIX signal() cannot block arbitrary signals during handling, and non‑real‑time signals are not queued; if a pending instance already exists the kernel discards new ones. Modern glibc fixes some historic issues but the fundamental reliability problem remains.

Reliable vs Unreliable Signals

Signals whose numbers are in the range [1,31] are unreliable: the kernel keeps at most one pending instance and drops subsequent deliveries. Signals in the range [32,64] (real‑time signals) are reliable: each instance is queued, up to a system‑wide limit.

Demo Programs

Two small C programs illustrate the behavior.

signal_receiver.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <errno.h>

static int sig_cnt[NSIG];
static volatile sig_atomic_t get_SIGINT = 0;

void handler(int signo) {
    if (signo == SIGINT) {
        get_SIGINT = 1;
    } else {
        sig_cnt[signo]++;
    }
}

int main(int argc, char *argv[]) {
    int i = 0;
    sigset_t blockall_mask, pending_mask, empty_mask;
    printf("%s:PID is %ld
", argv[0], getpid());
    for (i = 1; i < NSIG; i++) {
        if (i == SIGKILL || i == SIGSTOP) continue;
        if (signal(i, &handler) == SIG_ERR) {
            fprintf(stderr, "signal for signo(%d) failed(%s)
", i, strerror(errno));
        }
    }
    if (argc > 1) {
        int sleep_time = atoi(argv[1]);
        sigfillset(&blockall_mask);
        if (sigprocmask(SIG_SETMASK, &blockall_mask, NULL) == -1) {
            fprintf(stderr, "setprocmask to block all signal failed(%s)
", strerror(errno));
            return -2;
        }
        printf("I will sleep %d second
", sleep_time);
        sleep(sleep_time);
        if (sigpending(&pending_mask) == -1) {
            fprintf(stderr, "sigpending failed(%s)
", strerror(errno));
            return -2;
        }
        for (i = 1; i < NSIG; i++) {
            if (sigismember(&pending_mask, i)) {
                printf("signo(%d) :%s
", i, strsignal(i));
            }
        }
        sigemptyset(&empty_mask);
        if (sigprocmask(SIG_SETMASK, &empty_mask, NULL) == -1) {
            fprintf(stderr, "setprocmask to release all signal failed(%s)
", strerror(errno));
            return -3;
        }
    }
    while (!get_SIGINT) continue; // wait for SIGINT
    for (i = 1; i < NSIG; i++) {
        if (sig_cnt[i] != 0) {
            printf("%s:signal %d caught %d time%s
", argv[0], i, sig_cnt[i], (sig_cnt[i] > 1) ? "s" : "");
        }
    }
    return 0;
}

signal_sender.c

#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <signal.h>
#include <string.h>
#include <errno.h>

void usage() {
    fprintf(stderr, "USAGE:
");
    fprintf(stderr, "--------------------------------
");
    fprintf(stderr, "signal_sender pid signo times
");
}

int main(int argc, char *argv[]) {
    pid_t pid = -1;
    int signo = -1, times = -1, i;
    if (argc < 4) { usage(); return -1; }
    pid = atol(argv[1]);
    signo = atoi(argv[2]);
    times = atoi(argv[3]);
    if (pid <= 0 || times < 0 || signo < 1 || signo >= 64 || signo == 32 || signo == 33) {
        usage(); return -1;
    }
    printf("pid = %ld,signo = %d,times = %d
", (long)pid, signo, times);
    for (i = 0; i < times; i++) {
        if (kill(pid, signo) == -1) {
            fprintf(stderr, "send signo(%d) to pid(%ld) failed,reason(%s)
", signo, (long)pid, strerror(errno));
            return -2;
        }
    }
    fprintf(stdout, "done
");
    return 0;
}

Running the Demo

Start signal_receiver (optionally with a sleep interval to block all signals). While it is sleeping, run signal_sender to send 10 000 instances of SIGUSR1 (signal 10, unreliable) and 10 000 instances of SIGRTMIN+2 (signal 36, reliable). The receiver reports only a few thousand catches for the unreliable signal but all 10 000 catches for the reliable one. When the pending‑signal limit (e.g., 15408, visible via ulimit -i) is reached, further reliable signals are dropped.

Kernel Implementation

The kernel tracks pending signals in two structures: struct task_struct contains a struct sigpending pending field. struct signal_struct contains a struct sigpending shared_pending field.

Both sigpending structures hold a list_head and a sigset_t signal bitmap.

Kernel signal data structures
Kernel signal data structures

When a non‑real‑time signal ( signo < 32) arrives, the kernel checks whether the same signal is already pending; if so, the new instance is discarded. For real‑time signals the kernel allocates a struct sigqueue and links it into the pending list, guaranteeing delivery until the global pending‑signal limit is reached.

Pending‑Signal Limit

The per‑process pending‑signal limit can be queried with ulimit -i (default 15408 on many Linux systems). Sending more reliable signals than this limit results in the excess being dropped.

Conclusion

Non‑real‑time signals can be lost because the kernel does not queue them, while real‑time signals are reliably queued up to a configurable limit. Understanding these differences and the underlying kernel data structures helps developers choose the appropriate inter‑process communication mechanism on Linux.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Real-TimeLinuxsignalsreliable
ITPUB
Written by

ITPUB

Official ITPUB account sharing technical insights, community news, and exciting events.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.