Linux - signals
What signals are, how they work in Linux, common signal types, trap usage and practical examples.
topic: Linux
release date: 2026-03-12
introduction
A signal is a small, asynchronous notification delivered to a process by the kernel, by another process, or by a user. Signals are used to indicate events such as interrupts from the terminal, timer expirations, child process status changes, or critical faults like segmentation violations. They are a primitive form of interprocess communication and are meant to be lightweight and quick.
Conceptually signals are not function calls. When a signal arrives the kernel marks it for the target process and that process, when scheduled, notices the pending signal. If a handler is installed the handler runs at an interrupt point. If not, the signal's default action applies. Some signals are uncatchable or unblockable and always perform their default action.
types of signals
Below is a comprehensive table with signal numbers, names and short descriptions. Real-time signal numeric values can differ between kernels; prefer using names in scripts and programs. After the table there is a more detailed discussion of the most commonly used signals.
| NUMBER | NAME | DESCRIPTION |
|---|---|---|
| 1 | SIGHUP | Hangup - controlling terminal closed; often used to tell daemons to re-read config |
| 2 | SIGINT | Interactive interrupt (Ctrl+C) |
| 3 | SIGQUIT | Quit - produce core dump (Ctrl+\) |
| 4 | SIGILL | Illegal instruction - invalid CPU instruction executed |
| 5 | SIGTRAP | Trace trap - used by debuggers |
| 6 | SIGABRT | Abort from abort(3) - program requested abnormal termination |
| 7 | SIGBUS | Bus error - bad memory access alignment or hardware fault |
| 8 | SIGFPE | Floating point exception - division by zero, overflow, etc. |
| 9 | SIGKILL | Immediate uncatchable kill - terminate process without cleanup |
| 10 | SIGUSR1 | User-defined signal 1 - for application-defined purposes |
| 11 | SIGSEGV | Segmentation fault - invalid memory access; usually core dumps |
| 12 | SIGUSR2 | User-defined signal 2 - for application-defined purposes |
| 13 | SIGPIPE | Broken pipe - writing to a pipe with no reader |
| 14 | SIGALRM | Alarm clock - timer expiration from alarm() |
| 15 | SIGTERM | Termination request - polite shutdown signal |
| 16 | SIGSTKFLT | Stack fault on coprocessor (architecture dependent) |
| 17 | SIGCHLD | Child status changed - parent should reap children |
| 18 | SIGCONT | Continue - resume a stopped process |
| 19 | SIGSTOP | Stop - stop (pause) process; uncatchable |
| 20 | SIGTSTP | Terminal stop - interactive stop (Ctrl+Z) |
| 21 | SIGTTIN | Background read from TTY - stop |
| 22 | SIGTTOU | Background write to TTY - stop |
| 23 | SIGURG | Urgent condition on socket |
| 24 | SIGXCPU | CPU time limit exceeded |
| 25 | SIGXFSZ | File size limit exceeded |
| 26 | SIGVTALRM | Virtual timer expired |
| 27 | SIGPROF | Profiling timer expired |
| 28 | SIGWINCH | Window size change - terminal resized |
| 29 | SIGIO | I/O now possible - async I/O notification |
| 30 | SIGPWR | Power failure (system) - platform dependent |
| 31 | SIGSYS | Bad system call |
| 32 | SIGRTMIN | Real-time signal - start of realtime range |
| 33 | SIGRTMIN+1 | Real-time signal |
| 34 | SIGRTMIN+2 | Real-time signal |
| 35 | SIGRTMIN+3 | Real-time signal |
| 36 | SIGRTMIN+4 | Real-time signal |
| 37 | SIGRTMAX-4 | Real-time signal |
| 38 | SIGRTMAX-3 | Real-time signal |
| 39 | SIGRTMAX-2 | Real-time signal |
| 40 | SIGRTMAX-1 | Real-time signal |
| 41 | SIGRTMAX | Real-time signal - end of realtime range |
| 42 | SIGRTMIN+5 | Real-time signal |
| 43 | SIGRTMIN+6 | Real-time signal |
| 44 | SIGRTMIN+7 | Real-time signal |
| 45 | SIGRTMIN+8 | Real-time signal |
| 46 | SIGRTMIN+9 | Real-time signal |
| 47 | SIGRTMIN+10 | Real-time signal |
| 48 | SIGRTMIN+11 | Real-time signal |
| 49 | SIGRTMIN+12 | Real-time signal |
| 50 | SIGRTMIN+13 | Real-time signal |
| 51 | SIGRTMIN+14 | Real-time signal |
| 52 | SIGRTMAX-5 | Real-time signal |
| 53 | SIGRTMAX-6 | Real-time signal |
| 54 | SIGRTMAX-7 | Real-time signal |
| 55 | SIGRTMAX-8 | Real-time signal |
| 56 | SIGRTMAX-9 | Real-time signal |
| 57 | SIGRTMAX-10 | Real-time signal |
| 58 | SIGRTMAX-11 | Real-time signal |
| 59 | SIGRTMAX-12 | Real-time signal |
| 60 | SIGRTMAX-13 | Real-time signal |
| 61 | SIGRTMAX-14 | Real-time signal |
| 62 | SIGRTMAX-15 | Real-time signal |
| 63 | SIGRTMAX-16 | Real-time signal |
| 64 | SIGRTMAX-17 | Real-time signal |
detailed discussion - common signals
Below are more complete verbal descriptions and common use cases for the signals you will meet most often. Read these paragraphs before trying examples. Understanding intent matters more than memorizing numbers.
SIGHUP
Originally SIGHUP indicated that a user's modem line hung up. Today it commonly signals to daemons that the controlling terminal went away or that they should reinitialize. Many servers use SIGHUP to reload configuration without stopping the service. The handler for SIGHUP typically triggers a config re-read and a graceful restart of worker processes.
SIGINT
SIGINT is emitted by the terminal driver when the user presses Ctrl+C. It is a polite interactive interrupt. Programs frequently catch SIGINT to stop cleanly - for example, by closing files, flushing buffers, or saving progress - rather than terminating immediately.
SIGTERM
SIGTERM is the conventional signal for asking a process to terminate. It gives the process a chance to tidy up. Services, supervisors and shutdown scripts use SIGTERM so programs can shut down gracefully. If the process ignores SIGTERM or does not terminate in reasonable time, SIGKILL is used next.
SIGKILL
SIGKILL cannot be caught, blocked or ignored. The kernel kills the process immediately and reclaims resources. Because it prevents cleanup, prefer SIGTERM first and only use SIGKILL if the process is stuck.
SIGSTOP / SIGCONT
SIGSTOP pauses a process and is not catchable. SIGCONT resumes it. These signals are used by job control, debuggers and wrappers that want to suspend work temporarily.
SIGCHLD
When a child process stops or exits, SIGCHLD is sent to the parent. The parent should call wait or waitpid to reap the child and avoid zombie processes. Many server frameworks rely on SIGCHLD handlers to monitor children.
SIGALRM, SIGVTALRM, SIGPROF
These signals come from timers: SIGALRM from alarm(), SIGVTALRM from virtual CPU timers, SIGPROF from profiling timers. They are useful for implementing timeouts or sampling profilers.
SIGPIPE
Senders writing to a pipe whose reader closed receive SIGPIPE. Default behavior is termination. Many networked programs prefer to ignore SIGPIPE and handle EPIPE errors explicitly to implement retries or logging.
SIGSEGV, SIGILL, SIGFPE
These are serious faults: segmentation violation, illegal instruction, floating point exception. They usually indicate programming bugs or corrupted memory and often produce a core dump for debugging.
SIGUSR1 / SIGUSR2
User signals are free for application-specific meanings. They are small, simple control channels for toggling modes, requesting status dumps, rotating logs or reloading lightweight parts of state. They are very handy because they do not require sockets or complex IPC.
SIGWINCH
This signal indicates that the terminal window size changed. Terminal applications re-query the terminal size and redraw UI when they receive SIGWINCH.
kill command
The kill command sends signals to processes. Despite its name, it can be used to send any signal, not only termination signals. The most portable and readable way to use it is by signal name.
how it works
When you run kill -SIGNAL PID, the kernel arranges for the named signal to be delivered to the process with the given PID. If you do not have permission to signal the process, the kernel returns an error. Root can signal any process.
common syntaxes
- kill -SIGTERM <pid> - signal by name
- kill -15 <pid> - numeric signal
- kill <pid> - default is SIGTERM on many systems
- kill -l - list signals and numbers
- pkill <pattern> - signal by process name or pattern
- kill -TERM -<pgid> - send to process group (note leading minus)
- kill -9 <pid> - force kill (SIGKILL)
examples
# polite shutdown
kill -SIGTERM 1234
# same using numeric form
kill -15 1234
# force kill if polite attempt fails
kill -9 1234
# send SIGUSR1 to process 4321
kill -USR1 4321
# send SIGTERM to all processes in process group 1000
kill -TERM -1000
# list signals on this system
kill -l
When scripting prefer names for readability and portability. Use negative PIDs to target process groups. pkill and pgrep work well when you need name-based matching, but be careful with ambiguous names.
Some administrative tools and service managers will re-spawn or react to kill signals. For example sending SIGTERM to a supervised process may cause the supervisor to restart it.
trap command
trap sets shell handlers for signals. When the shell receives a trapped signal it evaluates the trap code. Traps are the shell equivalent of signal handlers in C and are essential for cleanup and graceful exits.
basic forms
- trap 'handler' SIGINT - run handler on SIGINT
- trap '' SIGPIPE - ignore SIGPIPE
- trap - SIGINT - restore default action for SIGINT
- trap 'cmds' 0 - run cmds on shell exit (EXIT)
Trap handlers run in the context of the shell. Keep them safe and avoid complex non-reentrant operations that may fail if they interrupt other operations.
examples
# simple trap on Ctrl+C
trap 'echo "interrupted"; exit 130' SIGINT
# ignore SIGPIPE
trap '' SIGPIPE
# restore default for SIGINT
trap - SIGINT
trap EXIT
The trap for signal number 0 or name EXIT runs when the shell exits for any reason - normal exit, unhandled error or after an explicit exit. It is ideal for final cleanup: removing temporary files, restoring terminal state, killing background jobs, or restoring cursor visibility.
# cleanup on any exit
tmpfile="/tmp/my.$$"
touch "$tmpfile"
trap 'rm -f "$tmpfile"; echo "cleaned";' EXIT
# rest of script...
sleep 2
exit 0
trap in scripts
In real scripts traps are used to ensure system state is consistent when the script ends or is interrupted. Below are practical, slightly more elaborate cases showing typical uses.
restore cursor visibility on exit
If a script hides the terminal cursor for a nicer UI, make sure to restore it even if the user presses Ctrl+C. Use a trap EXIT for that.
#!/usr/bin/env bash
# hide cursor and restore on exit
hide_cursor() {
printf '\033[?25l'
}
show_cursor() {
printf '\033[?25h'
}
hide_cursor
trap 'show_cursor; echo "cursor restored";' EXIT
# simulate work
for i in {1..10}; do
printf '.'
sleep 1
done
Alternative using tput if available: tput civis to hide and tput cnorm to show.
trap SIGUSR1 / SIGUSR2 for control
Use SIGUSR signals to add runtime control without sockets. Below a worker toggles verbose logging on SIGUSR1 and prints status on SIGUSR2.
#!/usr/bin/env bash
VERBOSE=0
status() {
echo "pid $$ - verbose $VERBOSE"
}
toggle_verbose() {
VERBOSE=$((1 - VERBOSE))
echo "verbose -> $VERBOSE"
}
trap 'toggle_verbose' SIGUSR1
trap 'status' SIGUSR2
trap 'echo "exiting"; exit' SIGINT SIGTERM
while true; do
[ "$VERBOSE" -eq 1 ] && echo "working..."
sleep 2
done
reaping children and avoiding zombies
When a script spawns background jobs it should reap them. A SIGCHLD trap that calls wait -n or a loop with wait prevents accumulating zombie processes.
# example: reap children
trap 'while wait -n 2>/dev/null; do :; done' SIGCHLD
longtask() {
sleep 5
echo "done $$"
}
longtask & # background
longtask &
# wait for all children
wait
outro
Signals are compact and efficient for notifying processes about events. The right approach is to prefer graceful handling - use SIGTERM and traps to clean up, use SIGUSR1/SIGUSR2 for simple runtime control, and reserve SIGKILL only for stuck processes. For robust programs use sigaction() on the C side and keep shell traps careful and minimal.
If this article helps, consider a cup of coffee via >link<.