When the kernel generates a signal for a process but the process has that signal currently blocked (in its signal mask), the signal cannot be delivered yet. It stays in a waiting area called the pending signal set.
Think of it like a letter in a mailbox that you haven’t opened yet because you’re busy. The letter (signal) arrived (generated), but you haven’t read it (delivered) yet.
The pending state ends as soon as the process unblocks the signal β at that point the kernel delivers it immediately.
| Step | What Happens | Signal State |
|---|---|---|
| 1 | Event occurs (e.g. timer expires) | Generated |
| 2 | Signal is in the process’s signal mask (blocked) | β³ Pending |
| 3 | Process removes signal from mask (unblocks it) | Unblocked |
| 4 | Kernel delivers signal β handler runs or default action | β Delivered |
The kernel keeps per-process signal information in /proc/PID/status. The relevant fields are hex bitmasks where each bit represents a signal number:
| Field in /proc/PID/status | Meaning |
|---|---|
| SigPnd | Per-thread pending signals |
| ShdPnd | Process-wide pending signals (since kernel 2.6) |
| SigBlk | Blocked (masked) signals |
| SigIgn | Ignored signals |
| SigCgt | Caught signals (have a handler installed) |
cat /proc/<PID>/status | grep Sig to see these bitmasks.This program blocks SIGINT, then loops for 5 seconds while you can press Ctrl+C. During that time SIGINT becomes pending. We use sigpending() to check and print the pending set.
/* Example: Block SIGINT, check pending signals with sigpending()
Compile: gcc -o pending_demo pending_demo.c
Run: ./pending_demo then press Ctrl+C quickly */
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
int main(void)
{
sigset_t block_set, pending_set;
/* Step 1: block SIGINT */
sigemptyset(&block_set);
sigaddset(&block_set, SIGINT);
sigprocmask(SIG_BLOCK, &block_set, NULL);
printf("SIGINT is now blocked for 5 seconds.\n");
printf("Press Ctrl+C β it will become PENDING.\n");
sleep(5); /* sleep while SIGINT may arrive */
/* Step 2: check what is pending */
sigpending(&pending_set);
if (sigismember(&pending_set, SIGINT))
printf("YES β SIGINT is currently PENDING!\n");
else
printf("No pending SIGINT detected.\n");
/* Step 3: unblock β signal is delivered NOW */
printf("Unblocking SIGINT β it will be delivered now...\n");
sigprocmask(SIG_UNBLOCK, &block_set, NULL);
/* If SIGINT was pending, process terminates here */
printf("This line only prints if no SIGINT was pending.\n");
return 0;
}
This program blocks SIGINT, then reads its own /proc/self/status to show the SigPnd bitmask before and after pressing Ctrl+C.
/* Example: Read SigPnd from /proc/self/status
Compile: gcc -o proc_sig proc_sig.c
Run: ./proc_sig then press Ctrl+C when prompted */
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
/* Helper: print SigPnd line from /proc/self/status */
void print_sig_status(void)
{
FILE *f = fopen("/proc/self/status", "r");
if (!f) { perror("fopen"); return; }
char line[256];
while (fgets(line, sizeof(line), f)) {
/* Print only lines starting with Sig */
if (strncmp(line, "Sig", 3) == 0)
printf(" %s", line);
}
fclose(f);
}
int main(void)
{
sigset_t block_set;
/* Block SIGINT */
sigemptyset(&block_set);
sigaddset(&block_set, SIGINT);
sigprocmask(SIG_BLOCK, &block_set, NULL);
printf("=== Signal status BEFORE pressing Ctrl+C ===\n");
print_sig_status();
printf("\nNow press Ctrl+C within 5 seconds...\n");
sleep(5);
printf("\n=== Signal status AFTER pressing Ctrl+C ===\n");
print_sig_status();
/* Unblock to deliver */
sigprocmask(SIG_UNBLOCK, &block_set, NULL);
return 0;
}
Signal Mask & Blocking β How does a process block signals from being delivered?
