Every Linux sysadmin has faced this frustration: you kill a parent process but its children keep running like rebellious offspring. While SIGTERM (signal 15) should propagate through the process tree, certain scenarios break this chain:
- Processes that install custom signal handlers
- Zombie processes stuck in D state
- Containerized environments with PID namespaces
- Processes running with different UIDs
Method 1: pkill with Process Group ID
# Find PGID (process group ID) ps -ejf | grep [process_name] # Kill entire process group kill -- -$PGID
Method 2: The Nuclear Option (SIGKILL cascade)
# One-liner to kill process tree kill -9 $(pstree -p [parent_pid] | grep -o '([0-9]\+)' | grep -o '[0-9]\+')
When dealing with Docker containers or systemd services, you'll need specialized approaches:
# For Docker containers docker kill --signal=SIGKILL $(docker inspect --format='{{.State.Pid}}' [container_id]) # For systemd services systemctl kill --kill-who=all [service_name]
For maximum control, here's a Bash script that handles edge cases:
#!/bin/bash killtree() { local _pid=$1 local _sig=${2:-TERM} kill -stop ${_pid} # Prevent new children for _child in $(pgrep -P ${_pid}); do killtree ${_child} ${_sig} done kill -${_sig} ${_pid} } # Usage: killtree [PID] [SIGNAL] killtree 1234 KILL
The Linux kernel maintains process relationships through these mechanisms:
Mechanism | Effect on Process Killing |
---|---|
PR_SET_CHILD_SUBREAPER | Makes process immune to accidental tree kills |
PID namespaces | Can obscure parent-child relationships |
cgroups | May prevent signal delivery to contained processes |
When managing processes in Linux, terminating an entire process tree (parent + children) presents unique challenges. While some applications properly propagate termination signals, others require manual intervention. The naive approach of manually collecting PIDs is both inefficient and error-prone.
The most elegant solution utilizes process groups. When properly configured, killing a process group terminates all members simultaneously:
# Find the PGID (Process Group ID)
ps -o pgid= [PID]
# Kill entire process group (negative sign indicates PGID)
kill -- -[PGID]
For processes sharing a common name pattern:
# Terminate all matching processes
killall -TERM process_name
# Force kill if necessary
killall -9 process_name
For maximum control, implement a recursive killing function in bash:
function killtree() {
local _pid=$1
local _sig=${2:-TERM}
# Kill children
for _child in $(pgrep -P ${_pid}); do
killtree ${_child} ${_sig}
done
# Kill parent
kill -${_sig} ${_pid}
}
For systemd-managed services:
[Service]
KillMode=process # Only kills main process
KillMode=mixed # Default (main process + SIGTERM to children)
KillMode=control-group # Entire cgroup (recommended)
When child processes survive termination attempts:
# Find orphaned processes
ps -ef --forest | grep -v "grep" | grep "[PID]"
# Cleanup via /proc
ls -l /proc/[PID]/task/*/children
Understanding signal propagation is crucial:
# List all signals
kill -l
# Recommended signal sequence
kill -TERM [PID] # Graceful termination
sleep 2
kill -KILL [PID] # Force kill if needed