zur Übersicht: Linux: meine Software und Konfigurationstipps
kill) an einen ganzen Prozessbaum08.10.2006
Manchmal will man nicht nur einen einzelnen Prozess killen, sondern mehrere zusammenhängende. Mein auslösender Praxisfall war ein Script, das vom cron ausgerufen wurde und hängenblieb. Das waren dann für jeden Durchlauf vier oder fünf Kindprozesse. Die manuell zu killen, ist mit Arbeit verbunden. Außerdem kann man sich nicht darauf verlassen, dass durch die Beendigung des Elternprozesses auch die Kinder abgeräumt werden.
Deshalb habe ich ein Script geschrieben, das für jede übergebene PID alle "untergeordneten" Prozesse heraussucht und diese von oben (Argumente) nach unten (Kinder der letzten Ebene) an kill übergibt, um zu vermeiden, dass ein Prozess sein Kind neu startet, weil es durch Signal beendet wurde, bevor er selber beendet wird.
Zwei wesentliche Unterschiede bestehen zum Standard-kill:
Negative PIDs zur Auswahl von Prozessgruppen werden nicht unterstützt.
Es werden weniger Aufrufvarianten unterstützt.
killtree [--show|--showonly] [-
--show--showonlyInformationen über die verfügbaren Signale bekommt man mit /bin/kill -t.
#! /bin/bash
# This software is licensed under the Gnu GPL version 2, see http://www.fsf.org/
# Fuer dieses Script gibt es eine OpenPGP-Signatur (ID 0xECCB5814) auf der Downloadseite (killtree.sig)
version=1.0.2
# Dieses Script wird so aufgerufen wie kill, killt aber nicht nur die uebergebenen PIDs,
# sondern auch den ganzen Baum der Kindprozesse.
# CHANGELOG
# version 1.0.1, 2006-10-07, Hauke Laging, software@hauke-laging.de
usage () {
echo Usage: >&2
echo "'$0' [--show|--showonly] [-<signal>] pid ..." >&2
echo "'$0' --help" >&2
echo "'$0' --version" >&2
}
info () {
usage
cat <<EOT
This script calls kill for the argument PIDs and all child processes
thus sending the signal to a whole process tree.
This script accepts normal PIDs only (i.e. no negative ones for
addressing process groups).
EOT
}
if [ "$1" = --help ]
then
info
exit 0
fi
if [ "$1" = --version ]
then
echo "killtree version ${version}"
echo "This software is licensed under the Gnu GPL version 2, see http://www.fsf.org/"
exit 0
fi
type mktemp &>/dev/null || { echo "Program 'mktemp' is not available; aborting."; exit 1; }
ps_file=$(mktemp /tmp/ps.XXXXXX)
/bin/ps -eo pid,ppid | awk 'NR==1 {next;}; {print $1 " " $2}' > "$ps_file"
# PID-Arrays initialisieren
counter_wrpp=0
while read pid ppid
do
pids[$counter_wrpp]=$pid
ppids[$counter_wrpp]=$ppid
((counter_wrpp++))
done < "$ps_file"
rm "$ps_file"
show=no
showonly=no
if [ "$1" = --show -o "$1" = --showonly ]
then
show=yes
if [ "$1" = --showonly ]
then
showonly=yes
fi
shift
fi
if [ "$1" != "${1#-}" ]
then
signal="$1"
shift
else
signal=
fi
if [ $# -eq 0 ]
then
echo "Argument missing; aborting" >&2
usage
exit 2
fi
kill_pids=()
for((counter_mainargs=1;counter_mainargs<=$#;counter_mainargs++))
do
root_pid=${!counter_mainargs}
if [[ ! "$root_pid" =~ '^[1-9][0-9]+$' ]]
then
echo "Illegal argument '${root_pid}'; aborting" >&2
exit 1 # bei kill sollte man bei fehlerhaftem Aufruf lieber beenden als ueberspringen...
fi
kill_pids=(${kill_pids[@]} $root_pid)
search_pids=($root_pid)
pids_found=yes
while [ "$pids_found" = yes ]
do
ppid_matches=()
for search_pid in ${search_pids[@]}
do
for((counter_pidarray=0;counter_pidarray<counter_wrpp;counter_pidarray++))
do
if [ $search_pid -eq ${ppids[$counter_pidarray]} ]
then
tmp_i=${#kill_pids[@]}
kill_pids[$tmp_i]=${pids[$counter_pidarray]}
tmp_i=${#ppid_matches[@]}
ppid_matches[$tmp_i]=${pids[$counter_pidarray]}
fi
done
done
search_pids=(${ppid_matches[*]})
if [ ${#ppid_matches[*]} -gt 0 ]
then
pids_found=yes
else
pids_found=no
fi
done
done
count_kill=${#kill_pids[@]}
echo "Going to call 'kill ${signal}' for ${count_kill} PIDs."
if [ "$show" = yes ]
then
echo ${kill_pids[@]}
if [ "$showonly" = yes ]
then
echo "Option --showonly given; exiting without sending signals." >&2
exit 0
fi
fi
count_success=0
count_failure=0
# Den Baum von der Wurzel zu den Blaettern abarbeiten, um zu vermeiden, dass Prozesse ihre beendeten Kinder neu starten,
# bevor sie selber beendet werden.
for((counter_kill=0;counter_kill<count_kill;counter_kill++))
do
if [ -n "$signal" ]
then
kill "$signal" ${kill_pids[counter_kill]} &>/dev/null
else
kill ${kill_pids[counter_kill]} &>/dev/null
fi
if [ $? -eq 0 ]
then
((count_success++))
else
((count_failure++))
fi
done
echo "${count_success} processes received the signal."
echo "${count_failure} errors occurred."