Think of a signal like a phone buzz. You are working on something and suddenly your phone vibrates โ that is an interruption. A signal does the same thing to a Linux process. It is a small notification sent to a running process to tell it something has happened.
Signals are often called software interrupts because โ just like a hardware interrupt stops the CPU โ a signal stops a process’s normal flow and forces it to react.
You cannot always predict exactly when a signal will arrive. That is what makes them asynchronous โ they show up at random moments, not at a fixed step in your program.
Three sources can send a signal to a process:
| Source | Example |
|---|---|
| ๐ฅ๏ธ The Kernel | Dividing by zero โ kernel sends SIGFPE |
| ๐จโ๐ป Another Process | Shell sends SIGTERM to stop a program |
| ๐ The Process Itself | A program calls raise() to signal itself |
The kernel generates a signal for a process when:
| Event Type | What Happens | Signal Sent |
|---|---|---|
| Hardware Exception | Program divides by zero | SIGFPE |
| Hardware Exception | Program accesses bad memory address | SIGSEGV |
| Terminal Character | User presses Ctrl+C | SIGINT |
| Terminal Character | User presses Ctrl+Z | SIGTSTP |
| Software Event | Timer expired | SIGALRM |
| Software Event | Child process terminated | SIGCHLD |
Every signal is assigned a unique integer number starting from 1. But in your C programs you never use these numbers directly โ you always use symbolic names like SIGINT, SIGTERM, etc., defined in <signal.h>.
This is important because the actual number for a given signal can differ between CPU architectures (x86, ARM, MIPS etc.). The symbolic name always works correctly.
kill -l in your terminal to see all signal names and their numbers on your system.A signal goes through three stages:
| 1. Generated | 2. Pending | 3. Delivered |
|---|---|---|
| An event occurs (e.g. Ctrl+C pressed) |
Signal is waiting to be handed to the process (may be blocked) |
Process receives the signal and takes action |
| ๐ด Event fires | โณ Queued in kernel | โ Process reacts |
Between generation and delivery, the signal is called a pending signal. The process handles it as soon as it is scheduled to run (or immediately if already running).
This program runs in an infinite loop. When you press Ctrl+C, the kernel sends SIGINT to this process and it terminates. This is the simplest demonstration that signals exist.
/* Example 1: Observe SIGINT from Ctrl+C
Compile: gcc -o signal_demo signal_demo.c
Run: ./signal_demo then press Ctrl+C */
#include <stdio.h>
#include <unistd.h>
int main(void)
{
int count = 0;
printf("Program started. PID = %d\n", getpid());
printf("Press Ctrl+C to send SIGINT and kill me.\n");
while (1) {
printf("Running... count = %d\n", count++);
sleep(1); /* sleep 1 second each loop */
}
return 0; /* never reached */
}
This example prints the PID of the running process. Open a second terminal and use kill -SIGTERM <PID> to manually send a signal to it. This shows that another process (the shell) can send signals.
/* Example 2: Print PID so you can send signal from another terminal
Compile: gcc -o show_pid show_pid.c
Run: ./show_pid
Then in another terminal: kill -SIGTERM <PID shown> */
#include <stdio.h>
#include <unistd.h>
int main(void)
{
printf("My PID is: %d\n", getpid());
printf("Send me a signal from another terminal:\n");
printf(" kill -SIGTERM %d\n", getpid());
printf(" kill -SIGKILL %d\n", getpid());
printf(" kill -SIGINT %d\n", getpid());
/* Wait forever doing nothing */
while (1) {
sleep(2);
printf("Still alive...\n");
}
return 0;
}
Signal Generation & Delivery โ How exactly does the kernel deliver a signal to a process?
