Mastering Unix Signals: Types, Handling, and Practical Code Examples
This article explains what Unix signals are, categorizes reliable and unreliable signals, describes how they are generated by hardware and software, and provides detailed examples of registration, blocking, pending states, and custom handling using C functions such as kill, sigprocmask, sigaction, and signal.
1. What is a signal?
Signal is essentially a software interrupt.
Example:
Enter a command and start a foreground process in the shell.
User presses Ctrl‑C, generating a hardware interrupt.
If the CPU is executing the process, the user‑space code pauses, and the CPU switches from user mode to kernel mode to handle the interrupt.
The terminal driver translates Ctrl‑C into a SIGINT signal and records it in the process's PCB.
Before returning to user space, the kernel checks the PCB, finds a pending SIGINT whose default action is to terminate the process, and the process is killed.
In this example, the hardware interrupt generated by Ctrl‑C is a signal. Only the foreground process receives such signals; appending '&' runs the command in the background. The shell can run one foreground process and many background processes, but only the foreground process can receive Ctrl‑C.
2. Types of signals
View them with the command: kill -l Unreliable signals: numbers 1‑31 (may be lost). Reliable signals: numbers 34‑64 (cannot be lost).
Common signals:
SIGHUP (1): Hangup detected on controlling terminal or death of controlling process – action: terminate.
SIGINT (2): Interrupt from keyboard (Ctrl‑C) – action: terminate.
SIGQUIT (3): Quit from keyboard (Ctrl‑\) – action: core dump.
SIGABRT (6): Abort signal – action: core dump.
SIGKILL (9): Kill signal – action: terminate; cannot be blocked, ignored, or caught.
SIGSEGV (11): Invalid memory reference – action: core dump.
SIGPIPE (13): Broken pipe – action: terminate.
SIGCHLD (17): Child stopped or terminated – default action is ignored.
SIGSTOP (19): Stop process – action: stop.
SIGTSTP (20): Stop typed at terminal (Ctrl‑Z) – action: stop.
For detailed actions, see man 7 signal.
3. Signal generation
3.1 Hardware‑generated signals
Generated by terminal key presses:
Ctrl‑C → SIGINT (2) sent to the foreground process; appending '&' runs it in background, fg brings it back to foreground.
Ctrl‑Z → SIGTSTP (20) – rarely used.
Ctrl‑\ → SIGQUIT (3) – produces a core dump file.
Conditions for generating a core dump:
ulimit -a # ensure core dump size is not limited
# sufficient disk space
# scenarios that produce core dumps:
# 1. Dereferencing a null pointer → SIGSEGV (11)
# 2. Out‑of‑bounds memory access → SIGSEGV (11)
# 3. Double free → SIGABRT (6)
# 4. free(NULL) does not crash3.2 Software‑generated signals
Generated by calling system functions:
kill() function
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
/* pid: process ID, sig: signal number */kill command: kill -[signal] pid abort(): void abort(void); – raises SIGABRT (6)
alarm(): unsigned int alarm(unsigned int seconds); – raises SIGALRM after the specified seconds
4. Signal registration
Signals can be registered as reliable or unreliable. Registration uses a bitmap and a sigqueue queue.
4.1 Unreliable signal registration
When an unreliable signal arrives:
Set the corresponding bit in the bitmap to 1.
Add a sigqueue node to the queue; if a node for that signal already exists, do not add another.
4.2 Reliable signal registration
When a reliable signal arrives, the bitmap bit is set to 1 and a sigqueue node is always added, regardless of existing nodes.
5. Signal deregistration
5.1 Unreliable signal deregistration
Clear the bitmap bit to 0 and remove the corresponding sigqueue node from the queue.
5.2 Reliable signal deregistration
Remove the sigqueue node from the queue; if the queue still contains another node for the same signal, keep the bitmap bit set to 1, otherwise clear it to 0.
6. Signal blocking
6.1 How signals are blocked
Blocking a signal sets the corresponding bit in the block bitmap to 1. The signal can still be registered but will not be delivered until it is unblocked.
6.2 sigprocmask
Prototype:
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); how:
SIG_BLOCK – set signals to blocked
SIG_UNBLOCK – unblock signals
SIG_SETMASK – replace the block mask
set: pointer to the mask to apply
oldset: receives the previous maskExample: block all signals and then kill the process with kill -9:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void signcallback(int signumber) {
printf("change the signal %d
", signumber);
}
int main() {
sigset_t set, oldset;
sigfillset(&set); // set all bits to 1 → block all signals
sigprocmask(SIG_BLOCK, &set, &oldset);
while (1) { sleep(1); }
return 0;
}7. Pending signals
7.1 Concept of pending
Delivery is the actual execution of a signal’s action. The interval between generation and delivery is the pending state. A blocked signal remains pending until it is unblocked.
7.2 sigpending
Prototype: int sigpending(sigset_t *set); – retrieves the set of pending signals.
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void signalcallback(int signumber) {
printf("change signumber %d
", signumber);
}
void printsigset(sigset_t *set) {
for (int i = 0; i < 32; i++) {
putchar(sigismember(set, i) ? '1' : '0');
}
putchar('
');
}
int main() {
signal(2, signalcallback);
signal(10, signalcallback);
sigset_t set, oldset, pending;
sigfillset(&set); // block all signals
sigprocmask(SIG_BLOCK, &set, &oldset);
while (1) {
sigpending(&pending);
printsigset(&pending);
sleep(1);
}
return 0;
}8. Signal handling mechanisms
Each signal has two flag bits (blocked and pending) and a function pointer for its action.
SIGHUP is unblocked and never generated; default action is taken on delivery.
SIGINT has been generated but is blocked, so it cannot be delivered until unblocked.
SIGQUIT has not been generated; if generated it will be blocked and handled by a user‑defined handler.
8.1 signal()
Changes a signal’s handling action.
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
/* signum: signal number, handler: new action */8.2 sigaction()
Reads or modifies the action associated with a specific signal.
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);Key fields of struct sigaction:
void (*sa_handler)(int); // simple handler
void (*sa_sigaction)(int, siginfo_t *, void *); // detailed handler
sigset_t sa_mask; // signals blocked during handler
int sa_flags; // e.g., SA_SIGINFO
void (*sa_restorer)(void); // obsolete8.3 Example using sigaction
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void signcallback(int signumber) {
printf("change signumber %d
", signumber);
}
int main() {
struct sigaction act, oldact;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
act.sa_handler = signcallback;
sigaction(3, &act, &oldact);
while (1) { sleep(1); }
return 0;
}9. Signal catching
9.1 When a signal is caught
If the signal’s action is a user‑defined function, that function is invoked when the signal is delivered – this is called signal catching.
9.2 Signal catching flow
The kernel returns to user space via do_signal. If no pending signals, sys_return is called; otherwise the signal handler runs, followed by a special sigreturn system call before resuming the original user code.
10. Common signal set manipulation functions
int sigemptyset(sigset_t *set); // clear all bits
int sigfillset(sigset_t *set); // set all bits
int sigaddset(sigset_t *set, int signum); // set bit for signum
int sigdelset(sigset_t *set, int signum); // clear bit for signum
int sigismember(const sigset_t *set, int signum); // test membership11. SIGCHLD
Sent to a parent when a child terminates. By default it is ignored; if ignored, terminated children become zombies. Custom handling can reap children.
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <sys/wait.h>
#include <stdlib.h>
void signcallback(int signumber) {
printf("change signal %d
", signumber);
wait(NULL);
}
int main() {
signal(17, signcallback); // SIGCHLD
pid_t pid = fork();
if (pid < 0) {
perror("fork");
return -1;
} else if (pid == 0) {
printf("I am child
");
sleep(1);
exit(12);
} else {
while (1) { sleep(1); }
}
return 0;
}Check background processes with ps aux | grep ./fork.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
MaGe Linux Operations
Founded in 2009, MaGe Education is a top Chinese high‑end IT training brand. Its graduates earn 12K+ RMB salaries, and the school has trained tens of thousands of students. It offers high‑pay courses in Linux cloud operations, Python full‑stack, automation, data analysis, AI, and Go high‑concurrency architecture. Thanks to quality courses and a solid reputation, it has talent partnerships with numerous internet firms.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
