POSIX Signals - SIGINT
SIGINT (Interrupt, signal number 2) is the standard POSIX signal sent by the operating system terminal driver when a user presses the interrupt key sequence—almost universally Ctrl+C.
SIGINT is designed to politely request that an interactive program stop what it is doing and terminate. Because it is catchable, blockable, and ignorable, interactive applications (such as command-line shells, text editors, and debuggers) frequently intercept SIGINT to abort an in-flight operation without closing the program itself.
How SIGINT Is Generated: The Terminal Line Discipline
When you type Ctrl+C into a terminal, the operating system kernel terminal driver intercepts the special ASCII character 0x03 (ETX / End of Text):
[ User presses Ctrl+C ]
│
▼
┌────────────────────────────────────────────────────────┐
│ Terminal Subsystem (Line Discipline: n_tty) │
│ Detects VINTR character (0x03) │
└──────────────────────────┬─────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────┐
│ Kernel queries Foreground Process Group (tcgetpgrp) │
└──────────────────────────┬─────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────┐
│ Kernel broadcasts SIGINT to ALL processes in the group │
│ e.g. In "find / | grep test", both find and grep get it│
└────────────────────────────────────────────────────────┘
[!NOTE]
SIGINTis targeted at the Foreground Process Group, not just a single process. In a pipeline likecat huge_file | grep target, pressingCtrl+CsendsSIGINTto bothcatandgrepsimultaneously so the entire pipeline shuts down.
Common Handling Patterns
Pattern 1: Clean Terminal Exit (Removing Scratch Files)
Standard CLI utilities catch SIGINT to delete temporary files from /tmp before terminating:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
volatile sig_atomic_t g_interrupted = 0;
void handle_sigint(int signum) {
g_interrupted = 1;
}
int main() {
struct sigaction sa = {
.sa_handler = handle_sigint,
.sa_flags = 0, // Do NOT set SA_RESTART: allow slow syscalls to return EINTR
};
sigemptyset(&sa.sa_mask);
sigaction(SIGINT, &sa, NULL);
printf("Working... Press Ctrl+C to abort gracefully.\n");
while (!g_interrupted) {
// Perform work...
sleep(1);
}
printf("\nInterrupt caught! Cleaning up temporary scratch files...\n");
unlink("/tmp/my_temp_lock.pid");
exit(130); // 128 + Signal 2 = Standard 130 exit code
}
Pattern 2: The "Double Ctrl+C" Exit Pattern
Long-running tools or interactive consoles (like Node.js REPL or GDB) use a timer to require two quick Ctrl+C presses before quitting:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
time_t last_press = 0;
void handle_double_sigint(int signum) {
time_t now = time(NULL);
if (now - last_press <= 1) {
printf("\nExiting immediately.\n");
exit(0);
}
last_press = now;
printf("\nPress Ctrl+C again within 1 second to exit.\n");
}
Pattern 3: Shells and Editors (Aborting Without Exiting)
Full-screen text editors (Vim, Nano) or shells (bash, zsh) catch SIGINT to cancel ongoing searches or clear the current input buffer, redrawing the prompt rather than terminating.
Exit Status Convention: 130
When a process terminates due to a signal, standard Unix shells report an exit code equal to 128 + Signal Number:
- For
SIGINT(signal 2), the standard shell exit code is130(128 + 2). - For
SIGTERM(signal 15), the exit code is143(128 + 15). - For
SIGKILL(signal 9), the exit code is137(128 + 9).
Frequently Asked Questions (FAQ)
What is the difference between SIGINT and SIGQUIT?
SIGINT (Signal 2, triggered by Ctrl+C) politely requests an interruption and produces no core dump, while SIGQUIT (Signal 3, triggered by Ctrl+\) terminates the process immediately and generates a core dump for debugging. Developers use Ctrl+\ when a process hangs and ignores Ctrl+C.
Why does Ctrl+C kill both commands in a piped command like cat | grep?
Because the terminal line discipline broadcasts SIGINT to the entire Foreground Process Group (PGID), which includes all processes connected in the shell pipeline. This design guarantees that multi-process pipelines terminate cleanly as a single coordinated job.
Why is the exit code 130 associated with SIGINT?
Unix shells follow the convention of setting the exit code of a process killed by a signal to 128 + signal_number. Because SIGINT is signal number 2, 128 + 2 = 130, signaling to parent scripts that the command was intentionally aborted by user keyboard interruption.
Related Topics
- POSIX Signals - SIGTERM: Graceful termination requests and container lifecycles.
- POSIX Signals - SIGKILL: Uncatchable process destruction.
- POSIX Process Groups & Sessions: Understanding foreground process groups and terminal signal distribution.
