Mastering Unix Signals: From Basics to Custom Handlers
This article explains what Unix signals are, categorizes reliable and unreliable signals, describes how they are generated by hardware and software, and details registration, blocking, pending states, handling functions, and practical code examples for custom signal processing.
1. What is a signal?
A signal is essentially a software interrupt.
Example:
Enter a command to start a foreground process in the shell.
Press Ctrl‑C, generating a hardware interrupt.
If the CPU is executing the process, it 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 sees the pending SIGINT, whose default action is to terminate the process, so the process ends without returning to user code.
In this example, the hardware interrupt caused by Ctrl‑C is a signal that can only be sent to the foreground process; appending '&' runs a command in the background. The shell can run one foreground process and many background processes, but only the foreground process receives signals like Ctrl‑C.
2. Types of signals
Use the command kill -l to list signals.
Unreliable signals: numbers 1‑31 (may be lost). Reliable signals: numbers 34‑64 (cannot be lost).
SIGHUP (1): Hangup detected on controlling terminal or death of controlling process.
SIGINT (2): Interrupt from keyboard (Ctrl‑C).
SIGQUIT (3): Quit from keyboard (Ctrl‑\), generates a core dump.
SIGABRT (6): Abort signal from abort(3), generates a core dump.
SIGKILL (9): Kill signal, cannot be blocked, ignored, or caught.
SIGSEGV (11): Invalid memory reference, generates a core dump.
SIGPIPE (13): Broken pipe, terminates the process.
SIGCHLD (17): Child stopped or terminated (default ignored).
SIGSTOP (19): Stop process.
SIGTSTP (20): Stop typed at terminal (Ctrl‑Z).
For detailed actions, see man 7 signal.
3. Signal generation
3.1 Hardware-generated signals
Hardware generates signals via terminal key presses, e.g.:
Ctrl‑C → SIGINT (2)
Ctrl‑Z → SIGTSTP (20)
Ctrl‑\ → SIGQUIT (3)
3.2 Software-generated signals
Software generates signals 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, returns 0 on success, -1 on error */kill command: kill -[signal] pid abort: void abort(void); – sends signal 6 to the calling process.
alarm: unsigned int alarm(unsigned int seconds); – schedules SIGALRM after the given seconds.
4. Signal registration
Signal registration differs for reliable and unreliable signals and involves a bitmap and a sigqueue.
4.1 Unreliable signal registration
When a process receives an unreliable signal:
Set the corresponding bit in the signal bitmap to 1.
Add a sigqueue node to the sigqueue; if a node for that signal already exists, do not add another.
4.2 Reliable signal registration
When a process receives a reliable signal:
Set the corresponding bit in the bitmap to 1 and always add a sigqueue node, regardless of existing nodes.
5. Signal deregistration
5.1 Unreliable signal deregistration
Clear the signal’s bit in the bitmap and remove its node from the sigqueue.
5.2 Reliable signal deregistration
Remove the node from the sigqueue; if no other node for that signal remains, clear the bitmap bit, otherwise leave the bit set.
6. Signal blocking
6.1 How signals are blocked
Blocking a signal sets its bit in the block bitmap to 1. The signal can still be registered but will not be delivered until unblocked.
6.2 sigprocmask
Function prototype:
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); how: operation type
SIG_BLOCK: set signals to blocked
SIG_UNBLOCK: unblock signals
SIG_SETMASK: replace the block bitmap
set: bitmap to apply
oldset: previous bitmapExample: 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); // block all signals
sigprocmask(SIG_BLOCK, &set, &oldset);
while(1){ sleep(1); }
return 0;
}Result: signals have no effect; the process must be killed with kill -9.
7. Pending signals
7.1 Concept of pending
After a signal is generated, it remains pending until delivered. A blocked signal stays pending until 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');
}
int main(){
signal(2, signalcallback);
signal(10, signalcallback);
sigset_t set, oldset, pending;
sigfillset(&set);
sigprocmask(SIG_BLOCK, &set, &oldset);
while(1){
sigpending(&pending);
printsigset(&pending);
sleep(1);
}
return 0;
}8. Signal handling methods
Each signal has two flags (blocked and pending) and a function pointer for its action.
8.1 signal function
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 */Internally, signal calls sigaction.
8.2 sigaction function
Gets or sets the action for a specific signal.
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);Structure:
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);8.3 Custom signal handling flow
The task_struct contains a struct sighand_struct.
The sighand_struct holds an array of struct k_sigaction actions.
The _sighandler_t sa_handler in the array stores the handling method; changing it customizes signal handling.
9. Signal catching
9.1 Conditions for catching
If a signal’s action is a user-defined function, the kernel calls it when the signal is delivered—this is signal catching.
9.2 Catching process
When returning from kernel to user mode, do_signal is invoked. If a signal is pending, the kernel executes the handler instead of the original user code, then returns via sigreturn.
10. Common signal set operations
int sigemptyset(sigset_t *set); // clear all bits
int sigfillset(sigset_t *set); // set all bits
int sigaddset(sigset_t *set, int signum); // add a signal
int sigdelset(sigset_t *set, int signum); // remove a signal
int sigismember(const sigset_t *set, int signum); // test membership11. SIGCHLD signal
Sent to a parent when a child terminates; default action is ignore. Ignoring it can cause zombie processes. You can customize its handling:
#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;
}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.
