What Is a Signal in Simple Terms
Signals are the way processes communicate without passing data. They are simple notifications: "you should shut down", "reload your config", "you have been paused."
When you press Ctrl+C in a terminal, you are sending SIGINT to the running process. When you run kill 1234, you are sending SIGTERM. When you run systemctl reload nginx, systemd sends SIGHUP to nginx, which reloads its configuration without restarting.
Understanding signals is essential for production work: graceful shutdowns, zero-downtime config reloads, and debugging hung processes all involve signals.
Signal delivery flow:
Process A Kernel Process B (PID 1234)kill -TERM 1234 | | | | | +-- sys_kill() -------> | | | | +-- mark SIGTERM -------> | pending for 1234 | | Next scheduler tick: | check pending signals | | | +-- has SIGTERM handler? | yes: run handler + clean up no: default action (terminate)How It Works
The kernel delivers signals to a process by setting a flag in the process descriptor. The next time the process is scheduled to run, the kernel checks this flag and calls the signal handler (or default action) before resuming normal execution.
Process A (your shell)kill -TERM 1234 | vKernel: mark signal SIGTERM pending for PID 1234 | vNext time PID 1234 runs, kernel checks pending signals | vPID 1234 has a SIGTERM handler -> handler runsHandler does: close connections, flush buffers, exit cleanlyThe most important signals:
SIGHUP (1) Hang up / reload Traditional meaning: terminal disconnected Modern use: reload configuration without restart Example: nginx reloads nginx.conf on SIGHUP SIGINT (2) Interrupt (Ctrl+C) Polite request to stop Default: terminate the process Can be caught and handled SIGKILL (9) Kill immediately Cannot be caught, blocked, or ignored Kernel terminates the process immediately No cleanup, no graceful shutdown Use as last resort only SIGTERM (15) Terminate Polite request to terminate Default: terminate the process Can be caught -- process can clean up first This is what `kill PID` sends by default SIGSTOP (19) Pause execution Cannot be caught or ignored Process is frozen until SIGCONT SIGCONT (18) Continue after stop Resume a SIGSTOP'd process SIGUSR1 (10) User-defined signal 1SIGUSR2 (12) User-defined signal 2 Application-specific meaning Many apps use USR1 for log rotation SIGCHLD (17) Child process changed state Sent to parent when child exits Parent must wait() to reap the childWhy SIGKILL should be a last resort:
SIGTERM allows the process to: - Finish processing the current request - Close database connections properly - Flush write buffers to disk - Remove lock files and PID files - Log the shutdown event SIGKILL gives none of these opportunities: - In-flight requests are dropped - Database connections abruptly closed (may leave locks) - Write buffers lost (potential data corruption) - Lock files remain (next start may fail) Production shutdown sequence: 1. Send SIGTERM 2. Wait 30 seconds for graceful shutdown 3. If still running, send SIGKILLPractical Commands
## Send SIGTERM (default) to a processkill 1234kill -15 1234 ## samekill -TERM 1234 ## same ## Send SIGKILLkill -9 1234kill -KILL 1234 ## same ## Reload config (SIGHUP)kill -HUP 1234kill -1 1234 ## same ## Kill by name (all processes with that name)killall nginxpkill nginx ## Kill by patternpkill -f 'python3 worker.py' ## Check if a process is alive (signal 0 = test only, no signal sent)kill -0 1234## exit code 0 = process exists## exit code 1 = process does not exist or no permission ## List all signal names and numberskill -l ## Send SIGUSR1 to trigger log rotation in many daemonskill -USR1 $(cat /var/run/nginx.pid) ## Trap signals in bash scriptstrap 'echo "Caught SIGTERM, cleaning up..."; rm -f /tmp/mylock; exit 0' TERMtrap 'echo "Caught SIGINT, cleaning up..."; rm -f /tmp/mylock; exit 1' INT ## Or trap EXIT to run cleanup no matter how the script endstmpfile=$(mktemp)trap 'rm -f "$tmpfile"' EXITTroubleshooting
| Symptom | Command | What to Look For |
|---|---|---|
| kill not working | kill -0 PID |
Process in D state -- cannot kill |
| Service not reloading on SIGHUP | Check app docs | App may not implement SIGHUP handler |
| Process ignoring SIGTERM | Send SIGKILL after timeout | Process catching and ignoring SIGTERM |
| Zombie processes | `ps aux | grep Z` |
PLACEMENT PRO TIP**Tip:** `kill -0 PID` is the safest way to check if a process is running. It sends no actual signal — it just checks if the process exists and you have permission to signal it. Exit code 0 means the process is alive. This is useful in scripts that need to check process health without affecting it.
REMEMBER THIS**Remember:** SIGKILL (9) cannot be caught, blocked, or ignored — it is implemented entirely in the kernel, not in the process. This is both its power and its danger. It always works, but it gives the process no opportunity to clean up. Always try SIGTERM first and wait a reasonable time before escalating to SIGKILL.
COMMON MISTAKE / WARNING**Security:** Any process running as the same user can send signals to any other process of that user. Root can signal any process. This is why running services as dedicated service accounts with minimal permissions matters — a compromised service cannot send SIGKILL to other services running as different users.