kill Command: Tutorial & Examples
The most misnamed command in Unix — it sends signals, and most signals don't kill anything.
What It Is
kill is the command that proves Unix has a sense of humor. Despite its blunt, scary name, it isn't a killer — it's a messenger. All it does is hand a small, numbered note to a running process on your machine. The note is called a signal, and the process gets to read it and decide what to do. Most of the time the default behavior is to exit, which is why everyone calls the command "kill." But of the 30-odd signals in the catalog, only a handful actually end programs. Others make daemons reload their config, pause execution, resume it, or trigger app-specific debugging dumps. The famous one-liner kill -HUP $(cat /var/run/nginx.pid) doesn't kill nginx at all — it tells it to re-read its config file.
If you've never touched a server before, kill is the command you'll reach for the first time a runaway program eats your CPU and won't quit. It's also the command you'll misuse the first time — almost everyone reaches straight for kill -9, which is exactly the sledgehammer you should reach for last. We'll walk the whole thing from scratch: what a signal actually is, every signal worth knowing, why -9 is destructive, and the small handful of patterns you'll use for the rest of your career.
Your First Look
The simplest possible invocation. Find a PID with ps, pgrep, or top, then:
kill 2323
That's it. No output on success, exit code 0, and the process with PID 2323 receives SIGTERM (signal 15) — Unix's polite "please wrap up and exit." The process gets a moment to flush buffers, close open files, release database locks, tell its children goodbye, and exit cleanly. Most well-written programs handle SIGTERM gracefully; that's why it's the default and why systemd uses it to stop services.
If the process ignores you and keeps running ten seconds later, then you escalate. But not before. The full vocabulary:
kill -15 2323 # SIGTERM, polite — the default
kill -TERM 2323 # same thing, named
kill -HUP 2323 # SIGHUP — daemons reload config
kill -9 2323 # SIGKILL — the nuclear option
kill -STOP 2323 # pause the process
kill -CONT 2323 # resume it
kill -l # list every signal your kernel knows
The numbers and names are interchangeable, and yes — kill is also a shell builtin (so bash intercepts it before /bin/kill runs), which is why it also accepts shell job specs like kill %1 to signal the first backgrounded job from jobs.
The Big Idea: kill Doesn't Kill
This is the insight that makes the rest of the page click. The command is misnamed. It comes from the underlying kernel system call kill(2), which itself is a misnomer kept for historical compatibility going back to the very first edition of Unix in 1971. What it really does is deliver a small integer to a process — a signal — which is one of about 30 predefined messages the kernel knows how to hand-deliver asynchronously.
Each signal has a default disposition baked into the kernel: terminate (the process dies if it doesn't catch it), ignore (nothing happens), stop (the process freezes), or continue (a stopped process resumes). And — this is the lovely part — most signals can be caught by the process and given a custom meaning. Web servers catch SIGHUP and use it to reload their config without dropping connections. Databases catch SIGTERM to flush their write buffers before exit. Long-running scientific code catches SIGUSR1 to print a progress report on demand. The signal is the universal inter-process message of Unix, and kill is just the command that delivers them. Once you see it this way, you stop being scared of kill and start using it like a Swiss Army knife — kill -HUP to reload nginx, kill -STOP to freeze a runaway long enough to inspect it with strace, kill -USR1 to ask a process to dump stats. That's the command's real shape.
The Signals, Explained
The ones you'll actually meet on a real server. Default disposition in parentheses; "catchable" means the process can install a handler that overrides the default. For the full numbered reference — every signal, its number, default action, and whether it can be caught — see the table on the signal page.
- SIGHUP (1, terminate, catchable) — "hangup." Originally sent when a serial line dropped; today's daemons catch it and treat it as "reload your config file" — that's the famous
kill -HUP $(cat /var/run/nginx.pid)idiom for nginx, apache, and almost every other server daemon ever written. - SIGINT (2, terminate, catchable) — "interrupt." What your shell sends when you press Ctrl-C in a terminal. A polite, conventional "I want this to stop now."
- SIGQUIT (3, terminate + core dump, catchable) — what Ctrl-\ sends. Like SIGINT but it also writes a core file for postmortem debugging with
gdb. - SIGKILL (9, terminate, NOT catchable) — the sledgehammer. The kernel yanks the process out of existence with no notification, no cleanup, no chance to flush anything. The process cannot block it, catch it, or ignore it. See the Danger callout below — this is not the friendly first move beginners think it is.
- SIGTERM (15, terminate, catchable) — "please terminate." The default for
kill, the default for systemd shutting down a service, the default for everything that should give a program a chance to exit cleanly. Always try SIGTERM first. - SIGUSR1 / SIGUSR2 (10/12, terminate, catchable) — reserved for the application to define however it wants. nginx uses USR1 to re-open log files (for
logrotate) and USR2 for binary upgrades; many long-running programs use them as "dump status" buttons. App-specific — check themanpage for whatever you're signaling. - SIGSTOP (19, stop, NOT catchable) — freezes the process mid-execution. Like SIGKILL it can't be intercepted. Pair it with SIGCONT.
- SIGTSTP (20, stop, catchable) — what Ctrl-Z sends from a terminal. The catchable cousin of SIGSTOP. This is how
bgandfgbackgrounding works. - SIGCONT (18, continue) — resumes a stopped process. Ignored if the process is already running.
- SIGPIPE (13, terminate, catchable) — sent when a process writes to a pipe whose reader has closed. The reason
yes | headexits silently:yeswrites forever untilheadcloses its end and the next write delivers SIGPIPE. - SIGCHLD (17, ignore, catchable) — sent to a parent when a child process exits. Parents are supposed to catch this and call
wait()to reap the child. When they forget, the child becomes a zombie (STAT Zinps) — already dead but still in the process table. - SIGSEGV / SIGBUS / SIGFPE / SIGILL (11/7/8/4, terminate + core) — the crash signals. The kernel sends these when your code does something illegal (bad memory access, divide by zero, illegal instruction). You'll see them in
dmesgafter a segfault. - Signal 0 — no signal, but the permission check still runs.
kill -0 PIDis the idiom for "does this process still exist and am I allowed to signal it?" — pure existence test, no side effect. Used in shell scripts everywhere.
kill -l prints the whole table; kill -L formats it in a nice grid. Signals 34-64 are real-time signals (queued, ordered, carry a value) used by specialized libraries — you can ignore them for normal admin work.
Danger
kill -9skips every cleanup the program would have done. Open files don't flush, database write buffers are lost, lock files stay on disk so the program won't restart, child processes get orphaned, sockets are left half-closed and tie up ports until the kernel reaps them. SIGKILL is for after SIGTERM has been ignored for ten seconds — never your first move on a database, a web server, or anything else holding state.
Reading It by Example
The handful of patterns that cover almost everything you'll ever do with kill.
The escalation ladder — the move that took me years to learn. Always try the polite signal first; escalate only when ignored.
kill 2323 # 1. SIGTERM (polite, default)
# wait 5-10 seconds, check with: ps -p 2323
kill -TERM 2323 # 2. try again, sometimes one is missed
# still alive?
kill -9 2323 # 3. SIGKILL, last resort
Reload a daemon's config without a restart — the nginx idiom that an entire generation of sysadmins typed every day:
kill -HUP $(cat /var/run/nginx.pid)
nginx re-reads nginx.conf, gracefully drains old worker processes, and starts new ones with the new config — without dropping a single connection. Apache, postfix, bind, haproxy, sshd all honor SIGHUP the same way. On a modern box you'd usually let systemd do this for you (systemctl reload nginx) — but systemctl reload is itself just sending SIGHUP under the hood.
Pause a runaway long enough to inspect it:
kill -STOP 2323
strace -p 2323 # peek at what it's doing
kill -CONT 2323 # let it run again — or kill it
This is a beautiful trick. The process is frozen by the kernel — uses zero CPU — and you can attach strace, inspect /proc/2323, look at open files with lsof, and decide what to do. No state lost.
Kill every process matching a name — when you don't have or want the PID:
pkill nginx # SIGTERM by name (regex)
killall -TERM nginx # same idea, exact-match name
pgrep nginx | xargs kill # the manual version
pkill and killall are siblings of kill that match by name instead of PID. Behind the scenes they call the same kill(2) system call — they're just sugar.
Check whether a process is alive without killing it (signal 0 — the permission check runs, the process isn't actually signaled):
if kill -0 2323 2>/dev/null; then echo alive; else echo gone; fi
The shell-script idiom for "is the PID from my lock file still valid?"
Signal a whole process group with a negative PID. Useful for killing a shell and everything it spawned in one shot:
kill -TERM -2323 # signal the entire process group led by 2323
kill -9 -1 # signal EVERY process you can — instant logout
That last one is a fun footgun: kill -9 -1 literally means "send SIGKILL to every process I'm allowed to signal," which as a regular user kicks you out of your session and as root reboots the machine the hard way. The man page documents it as an example. Don't.
Cheat Sheet
The commands worth knowing by heart:
kill PID— polite SIGTERM, the default and the right first move.kill -9 PID— SIGKILL, when SIGTERM has been ignored. Last resort.kill -HUP PID— tell a daemon to reload its config.kill -STOP PID/kill -CONT PID— pause and resume.kill -0 PID— does this process exist? Pure check, no side effect.kill -l— list every signal the kernel knows.kill -l 11— translate number 11 to a name (SIGSEGV).kill %1— signal a shell job fromjobsby spec rather than PID.pkill name/killall name— signal by name; same signals work.kill -TERM -PGID— signal a whole process group.
How You'll Actually Use It
Day-to-day, kill shows up in three places. The interactive cleanup: a runaway process is eating a core, you find it in top or with pgrep, send SIGTERM, wait, escalate to SIGKILL if needed. The config reload: SIGHUP to a daemon after editing its config — though on modern systems systemctl reload is the friendlier wrapper that does it for you. Shell scripts: kill -0 to check liveness from a lock-file PID, or trapping signals with trap to clean up on exit.
If you find yourself running kill often on the same service, that's the signal (pun intended) you should be using systemd instead: systemctl restart, systemctl reload, systemctl stop all wrap the right signals with the right escalation timeouts and the right process-group handling — they're kill done correctly so you don't have to remember.
Gotchas
- You can't kill a
D-state process. Not even withkill -9. The process is asleep inside a kernel call (almost always a stuck disk or frozen NFS mount) and signals don't get delivered until it wakes up — which it won't, because whatever it's waiting on is broken. Seepsandtopfor the full "load average 100, CPU idle" story. - SIGKILL on PID 1 does nothing.
init/ systemd has special kernel protection: signals you didn't install a handler for are silently dropped. Killing PID 1 anyway would mean instant kernel panic, so the kernel refuses. - You can only signal processes you own. Mortals can't signal root's processes; root can signal anyone.
killreturns "Operation not permitted" and exits non-zero if you try. - PIDs are reused. A PID you saved an hour ago may now belong to a completely unrelated process. Verify with
kill -0or by re-checkingpsbefore sending anything destructive. killis a shell builtin and a binary.bashinterceptskillto support job specs (kill %1), sowhich killshows/bin/killbuttype killreveals the builtin runs first. If they ever disagree (rare), call/bin/killexplicitly.kill -9is not a debugging tool. If a process only dies under-9, you have a bug worth understanding — either it's ignoring SIGTERM (rude) or it's stuck in something SIGTERM can't interrupt. Find out which before reaching for the hammer reflexively.
History & Philosophy
kill and signals shipped in the very first edition of Unix in 1971 — older than C, older than pipes, almost as old as Unix itself. The name was a joke that stuck: the kernel interface is called kill() because the most common reason to send a signal in 1971 was, in fact, to terminate a runaway program on a PDP-11 with no other way to interrupt it. The mechanism was generalized — "asynchronous notification, of any kind, between processes" — but the system call kept its blunt original name forever after. Albert Cahalan rewrote the user-space command in 1999 for the procps-ng project that ships on every Linux distro today; before that, the BSD version was the standard one.
The deeper idea is that signals are Unix's answer to "how do you interrupt a running program from outside it?" — the operating system equivalent of tapping someone on the shoulder. Modern alternatives exist (dbus, systemd units, message queues), but nothing else is as universally available, as simple, or as embedded in fifty years of Unix muscle memory. Every daemon that's ever shipped knows what to do with SIGHUP. Every process that's ever run has had its PID ready to receive a signal. That's an extraordinary level of cross-program protocol agreement, achieved with a handful of integers and one system call. It is, like much of Unix, both an accident of history and quietly perfect.
See Also
- ps — find the PID to feed to
kill - top / htop — interactive, with a built-in
kkey - pgrep — find PIDs by name
- pkill —
killby name in one step - killall — the classic name-based killer
- jobs / fg / bg — shell-level signaling via Ctrl-Z and
kill %N - strace — see signals being received in real time
- trap — catch signals inside a shell script
- systemctl — the modern wrapper around
killfor services - signal / process / PID — the concepts behind the command
Sent SIGTERM and the service didn't come back?
CleverUptime watches every service on every server, every minute, and tells you the moment one stops responding — so you find out before your users do, not after a
kill -9that left half the state on disk.Want to see your own server's health right now? One command, no signup, no install.