<signal.h> lets your program react to signals—asynchronous events like Ctrl+C (SIGINT), illegal memory access (SIGSEGV), or floating-point errors (SIGFPE). You register a handler with signal and can deliver signals yourself with raise.
01
signal
Set handler.
02
raise
Send signal.
03
SIGINT
Ctrl+C.
04
SIG_DFL
Default.
05
sig_atomic
Safe flag.
06
Safe
Minimal handler.
Fundamentals
Definition and Usage
A signal interrupts normal execution. The OS (or your own code via raise) delivers an integer signal number. If you installed a handler with signal, that function runs; otherwise the default action applies (often terminate the program).
Handlers run asynchronously—they can fire while your program is inside printf, malloc, or a loop. That is why handlers must be tiny and use only async-signal-safe operations. The standard pattern is to set a volatile sig_atomic_t flag and do real work in main.
💡
Beginner Tip
The old reference called printf inside a signal handler—that works in simple demos but is not safe in production. Examples below use the flag pattern; printing happens in normal code.
Foundation
📝 Syntax
Include the header:
C
#include <signal.h>
Standard C functions
C
void (*signal(int sig, void (*handler)(int)))(int);
int raise(int sig);
signal(sig, handler) — install handler for signal sig; returns previous handler.
raise(sig) — send signal sig to the current program; returns 0 on success.
Common signal macros
SIGINT — interrupt (Ctrl+C in terminal).
SIGTERM — termination request (often from kill).
SIGFPE — floating-point exception (e.g. division by zero).
SIGILL — illegal instruction.
SIGSEGV — invalid memory access.
SIGABRT — from abort().
Special handler values
SIG_DFL — default handling (restore with signal(sig, SIG_DFL)).
SIG_IGN — ignore the signal.
SIG_ERR — error return from signal (not a handler).
POSIX extensions (not standard C)
On Linux/macOS you may also see sigaction, sigprocmask, kill, and signal-set functions. They are not required by ISO C signal.h—the old reference mixed them together; this page separates standard C from POSIX.
Headers and linking
#include <signal.h> — no special link flag on most platforms.
Compile: gcc program.c -std=c11 -o program
Cheat Sheet
⚡ Quick Reference
Call
Purpose
Example
signal(SIGINT, fn)
Catch Ctrl+C
Custom handler
signal(SIGINT, SIG_IGN)
Ignore interrupt
Cannot Ctrl+C out
raise(SIGINT)
Self-delivery
Test handler
volatile sig_atomic_t
Handler → main flag
Safe communication
SIG_DFL
Reset default
Restore normal exit
Handler
void h(int s) {
g_stop = 1;
}
Set flag only
Install
signal(SIGINT, h)
Register
Test
raise(SIGINT)
Fire signal
Check
if (g_stop) break;
In main loop
Hands-On
Examples Gallery
Compile with gcc file.c -std=c11 -o out. Examples use raise() so output is reproducible without pressing Ctrl+C. On a real terminal, SIGINT also arrives from the keyboard.
📚 Getting Started
Safe handler design and testing with raise.
Example 1 — SIGINT with sig_atomic_t Flag (Safe Pattern)
Improved version of the reference: handler sets a flag; main prints the message.
C
#include <signal.h>
#include <stdio.h>
static volatile sig_atomic_t got_sigint = 0;
void handle_sigint(int sig) {
(void)sig;
got_sigint = 1;
}
int main(void) {
if (signal(SIGINT, handle_sigint) == SIG_ERR) {
printf("Could not install SIGINT handler\n");
return 1;
}
printf("Simulating Ctrl+C with raise(SIGINT)...\n");
raise(SIGINT);
if (got_sigint) {
printf("Caught SIGINT (signal %d) — handler ran safely\n", SIGINT);
}
return 0;
}
📤 Output:
Simulating Ctrl+C with raise(SIGINT)...
Caught SIGINT (signal 2) — handler ran safely
How It Works
raise(SIGINT) delivers the same signal as Ctrl+C. The handler only sets got_sigint—no printf inside the handler. sig_atomic_t is guaranteed writable from a signal handler.
Example 2 — raise() to Test a Custom Handler
Count how many times a handler runs when signals are raised.
With handler: caught = 1
With SIG_IGN: caught = 0 (still 0)
How It Works
SIG_IGN tells the OS to discard the signal. Useful when you temporarily want to block Ctrl+C during a critical section (POSIX sigprocmask is more precise on Unix).
Example 4 — Restore Default with SIG_DFL
After one custom catch, reset to default behavior.
C
#include <signal.h>
#include <stdio.h>
static volatile sig_atomic_t once = 0;
void catch_once(int sig) {
(void)sig;
once = 1;
}
int main(void) {
signal(SIGINT, catch_once);
raise(SIGINT);
printf("First SIGINT: custom handler (once=%d)\n", (int)once);
signal(SIGINT, SIG_DFL);
printf("Restored SIG_DFL — next SIGINT would use default action\n");
printf("(not calling raise again — default often terminates)\n");
return 0;
}
📤 Output:
First SIGINT: custom handler (once=1)
Restored SIG_DFL — next SIGINT would use default action
(not calling raise again — default often terminates)
How It Works
SIG_DFL restores normal process behavior. A second SIGINT without a handler typically ends the program—so we do not call raise again in this demo.
Example 5 — Graceful Shutdown Loop
Pattern for long-running programs: work until the flag is set.
C
#include <signal.h>
#include <stdio.h>
static volatile sig_atomic_t keep_running = 1;
void request_stop(int sig) {
(void)sig;
keep_running = 0;
}
int main(void) {
int tick = 0;
signal(SIGINT, request_stop);
printf("Working... (simulate Ctrl+C after 3 ticks)\n");
while (keep_running && tick < 10) {
tick++;
printf(" tick %d\n", tick);
if (tick == 3) {
raise(SIGINT); /* stand in for user pressing Ctrl+C */
}
}
printf("Clean shutdown after %d ticks\n", tick);
return 0;
}
📤 Output:
Working... (simulate Ctrl+C after 3 ticks)
tick 1
tick 2
tick 3
Clean shutdown after 3 ticks
How It Works
On tick 3, raise(SIGINT) clears keep_running. The loop exits and you can flush files and free memory in normal code—not inside the handler.
Applications
🚀 Common Use Cases
CLI tools — catch SIGINT for graceful Ctrl+C shutdown.
Servers — handle SIGTERM from process managers (Docker, systemd).
Crash signals — log and exit on SIGSEGV (advanced; often use backtrace libraries).
Timeouts — combine with alarms or timers (often POSIX alarm).
Testing — raise to verify handler wiring in unit tests.
Child processes — POSIX kill sends signals to other PIDs (not standard C).
Normal code sees the flag and shuts down or logs safely.
Important
📝 Notes
Only async-signal-safe functions belong in handlers (_exit, signal, raise, etc.)—not printf or malloc.
Use volatile sig_atomic_t for flags shared with handlers.
signal() behavior on repeated signals varies by platform; POSIX sigaction is more reliable.
Handlers may interrupt system calls, returning EINTR—retry if needed.
Default action for many signals is process termination.
Windows C runtime supports a subset of signals; behavior differs from Unix terminals.
Performance
⚡ Optimization
Signal handling overhead is negligible for occasional events like Ctrl+C. Avoid installing handlers you do not need. For high-frequency timing, prefer polling or threads rather than flooding the process with signals.
Wrap Up
Conclusion
<signal.h> connects your program to OS-level events. Use signal to register handlers, raise to test them, and SIG_DFL / SIG_IGN for default or ignored behavior. Keep handlers minimal; do real work in main.
For deeper Unix programming, learn POSIX sigaction and sigprocmask. Pair with <setjmp.h> only when jumping from signal contexts with sigsetjmp (POSIX).
Flush and close resources after seeing the stop flag
Prefer sigaction on POSIX for production servers
❌ Don’t
Call printf, malloc, or exit in handlers
Assume kill / sigaction are standard C
Run long or complex logic inside signal handlers
Ignore EINTR from interrupted syscalls without a plan
Rely on undefined behavior after SIGSEGV in the handler
Summary
Key Takeaways
Knowledge Unlocked
Five things to remember about signal.h
Signal handling in C, explained simply.
5
Core concepts
💬01
signal
Install.
Register
📚02
raise
Send.
Test
📈03
SIGINT
Ctrl+C.
Common
📄04
sig_atomic
Safe flag.
Pattern
🌐05
Minimal
Handler.
Safety
❓ Frequently Asked Questions
signal.h declares the standard C signal interface: signal() to install a handler for a signal number, raise() to send a signal to the current process, and macros like SIGINT, SIGFPE, SIG_DFL, and SIG_IGN. Signals are asynchronous notifications (e.g. Ctrl+C, divide-by-zero).
signal(sig, handler) registers what happens when signal number sig arrives. raise(sig) delivers signal sig to the calling program right now—useful for testing handlers or reporting errors as signals.
Avoid it. printf is not guaranteed async-signal-safe and can deadlock if interrupted while holding a lock. The safe pattern: set a volatile sig_atomic_t flag in the handler, then check the flag in your main loop and print there.
Special handler values passed to signal(). SIG_DFL restores default behavior (often terminate the process). SIG_IGN ignores the signal. Use them instead of a custom function when you want default or no action.
sigaction, sigprocmask, and kill are POSIX extensions—not part of standard C signal.h. They offer more control on Linux/macOS. Standard C programs use signal() and raise(); production Unix code often prefers sigaction over signal().
SIGINT is the interrupt signal, usually sent when the user presses Ctrl+C in a terminal. Default action is to terminate the process. You can catch it with signal(SIGINT, handler) for graceful shutdown.
Did you know?
Before threads were common, signals were how Unix daemons learned to reload config or shut down. Today servers still catch SIGTERM, but most application logic belongs in worker threads—with signal handlers only flipping a flag so the main thread can coordinate an orderly exit.