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
--showonly
Informationen ü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."