Как лучше всего отправить сигнал всем членам группы процессов? - PullRequest
403 голосов
/ 24 декабря 2008

Я хочу убить целое дерево процессов. Каков наилучший способ сделать это с использованием любых распространенных языков сценариев? Я ищу простое решение.

Ответы [ 32 ]

1 голос
/ 14 сентября 2015

Следующее было протестировано на FreeBSD, Linux и MacOS X и зависит только от pgrep и kill (версии ps -o не работают под BSD). Первый аргумент - родительский pid, дочерние элементы которого должны быть прекращены. Второй аргумент - логическое значение, определяющее, должен ли родительский pid также быть завершен.

KillChilds() {
        local pid="${1}"
        local self="${2:-false}"

        if children="$(pgrep -P "$pid")"; then
                for child in $children; do
                        KillChilds "$child" true
                done
        fi

        if [ "$self" == true ]; then
                kill -s SIGTERM "$pid" || (sleep 10 && kill -9 "$pid" &)
        fi
}

KillChilds $$ > /dev/null 2>&1

Это отправит SIGTERM любому дочернему процессу / процессу внука в скрипте оболочки, и если SIGTERM не удастся, он будет ждать 10 секунд, а затем отправит kill.


Предыдущий ответ:

Следующее также работает, но уничтожит саму оболочку на BSD.

KillSubTree() {
    local parent="${1}"
    for child in $(ps -o pid=$parent); do
            if [ $$ -ne $child ]; then (kill -s SIGTERM $child || (sleep 10 && kill -9 $child & )) > /dev/null 2>&1 ; fi
    done
}
# Example lanch from within script
KillSubTree $$ > /dev/null 2>&1
1 голос
/ 05 февраля 2011

Вероятно, лучше убить родителя раньше детей; в противном случае родитель может снова породить новых детей, прежде чем его убьют. Они переживут убийство.

Моя версия PS отличается от приведенной выше; может быть, слишком стар, поэтому странный плач ...

Использование сценария оболочки вместо функции оболочки имеет много преимуществ ...

Однако, это в основном идея чжигангов


#!/bin/bash
if test $# -lt 1 ; then
    echo >&2 "usage: kiltree pid (sig)"
fi ;

_pid=$1
_sig=${2:-TERM}
_children=$(ps j | grep "^[ ]*${_pid} " | cut -c 7-11) ;
echo >&2 kill -${_sig} ${_pid}
kill -${_sig} ${_pid}
for _child in ${_children}; do
    killtree ${_child} ${_sig}
done
0 голосов
/ 09 января 2009

Спасибо за вашу мудрость, ребята. Мой скрипт оставлял некоторые дочерние процессы при выходе, а подсказка negation упростила ситуацию. Я написал эту функцию для использования в других сценариях при необходимости:

# kill my group's subprocesses:          killGroup
# kill also myself:                      killGroup -x
# kill another group's subprocesses:     killGroup N  
# kill that group all:                   killGroup -x N
# N: PID of the main process (= process group ID).

function killGroup () {
    local prid mainpid
    case $1 in
        -x) [ -n "$2" ] && kill -9 -$2 || kill -9 -$$ ;;
        "") mainpid=$$ ;;
         *) mainpid=$1 ;;
    esac
    prid=$(ps ax -o pid,pgid | grep $mainpid)
    prid=${prid//$mainpid/}
    kill -9 $prid 2>/dev/null
    return
}

Приветствие.

0 голосов
/ 05 октября 2015

Этот скрипт также работает:

#/bin/sh while true do echo "Enter parent process id [type quit for exit]" read ppid if [ $ppid -eq "quit" -o $ppid -eq "QUIT" ];then exit 0 fi for i in `ps -ef| awk '$3 == '$ppid' { print $2 }'` do echo killing $i kill $i done done

0 голосов
/ 04 сентября 2015

Уничтожение дочернего процесса в сценарии оболочки:

Много раз нам нужно убивать дочерние процессы, которые по какой-то причине зависают или блокируются. например. Проблема с FTP-соединением.

Есть два подхода,

1) Создать отдельного нового родителя для каждого дочернего элемента, который будет отслеживать и уничтожать дочерний процесс после истечения времени ожидания.

Создайте test.sh следующим образом,

#!/bin/bash

declare -a CMDs=("AAA" "BBB" "CCC" "DDD")
for CMD in ${CMDs[*]}; do
    (sleep 10 & PID=$!; echo "Started $CMD => $PID"; sleep 5; echo "Killing $CMD => $PID"; kill $PID; echo "$CMD Completed.") &
done
exit;

и наблюдайте за процессами, которые имеют имя «test» в другом терминале, используя следующую команду.

watch -n1 'ps x -o "%p %r %c" | grep "test" '

Вышеописанный скрипт создаст 4 новых дочерних процесса и их родителей. Каждый дочерний процесс будет работать в течение 10 секунд. Но по истечении 5 секунд соответствующие родительские процессы убьют этих детей. Таким образом, ребенок не сможет завершить выполнение (10 секунд). Поиграйте в эти моменты (переключатели 10 и 5), чтобы увидеть другое поведение. В этом случае дочерний процесс завершит выполнение за 5 секунд до истечения времени ожидания 10 секунд.

2) Пусть текущий родительский монитор отслеживает и уничтожает дочерний процесс по истечении времени ожидания. Это не создаст отдельного родителя для мониторинга каждого ребенка. Также вы можете правильно управлять всеми дочерними процессами в пределах одного и того же родителя.

Создайте test.sh следующим образом,

#!/bin/bash

declare -A CPIDs;
declare -a CMDs=("AAA" "BBB" "CCC" "DDD")

CMD_TIME=15;
for CMD in ${CMDs[*]}; do
    (echo "Started..$CMD"; sleep $CMD_TIME; echo "$CMD Done";) &
    CPIDs[$!]="$RN";
    sleep 1;
done

GPID=$(ps -o pgid= $$);
CNT_TIME_OUT=10;
CNT=0;
while (true); do
    declare -A TMP_CPIDs;

    for PID in "${!CPIDs[@]}"; do
        echo "Checking "${CPIDs[$PID]}"=>"$PID;

        if ps -p $PID > /dev/null ; then
          echo "-->"${CPIDs[$PID]}"=>"$PID" is running..";
          TMP_CPIDs[$PID]=${CPIDs[$PID]};
        else
          echo "-->"${CPIDs[$PID]}"=>"$PID" is completed.";
        fi
    done

    if [ ${#TMP_CPIDs[@]} == 0 ]; then
        echo "All commands completed.";
        break;
    else
        unset CPIDs;
        declare -A CPIDs;
        for PID in "${!TMP_CPIDs[@]}"; do
            CPIDs[$PID]=${TMP_CPIDs[$PID]};
        done
        unset TMP_CPIDs;

        if [ $CNT -gt $CNT_TIME_OUT ]; then
            echo ${CPIDs[@]}"PIDs not reponding. Timeout reached $CNT sec. killing all childern with GPID $GPID..";
            kill -- -$GPID;
        fi
    fi

    CNT=$((CNT+1));
    echo "waiting since $b secs..";
    sleep 1;
done

exit;

и наблюдайте за процессами, которые имеют имя «test» в другом терминале, с помощью следующей команды.

watch -n1 'ps x -o "%p %r %c" | grep "test" '

Вышеуказанный скрипт создаст 4 новых дочерних процесса. Мы храним pids всех дочерних процессов и зацикливаем их, чтобы проверить, закончили ли они свое выполнение или все еще работают. Дочерний процесс будет выполняться до времени CMD_TIME. Но если тайм-аут CNT_TIME_OUT достигнут, все дочерние процессы будут убиты родительским процессом. Вы можете переключать время и играть со скриптом, чтобы увидеть поведение. Недостатком этого подхода является использование идентификатора группы для уничтожения всего дочернего дерева. Но сам родительский процесс принадлежит к той же группе, поэтому он также будет уничтожен.

Вам может потребоваться назначить другой идентификатор группы родительскому процессу, если вы не хотите, чтобы родительский процесс был убит.

Более подробную информацию можно найти здесь,

Уничтожение дочернего процесса в сценарии оболочки

0 голосов
/ 17 февраля 2015

введите ps -ef проверьте идентификатор процесса. Завершите процесс, набрав kill -9 <pid>

0 голосов
/ 29 марта 2011

Если вы знаете pid того, что хотите убить, вы обычно можете перейти от идентификатора сеанса и всего в одном сеансе. Я бы дважды проверил, но я использовал это для сценариев, запускающих rsyncs в циклах, которые я хочу умереть, а не для запуска другого (из-за цикла), как если бы я просто killall'd rsync.

kill $(ps -o pid= -s $(ps -o sess --no-heading --pid 21709))

Если вы не знаете pid, вы все равно можете вкладывать больше

kill $(ps -o pid= -s $(ps -o sess --no-heading --pid $(pgrep rsync )))
0 голосов
/ 01 декабря 2017

Я знаю, что это старо, но это лучшее решение, которое я нашел:

killtree() { 
    for p in $(pstree -p $1 | grep -o "([[:digit:]]*)" |grep -o "[[:digit:]]*" | tac);do
        echo Terminating: $p 
        kill $p
    done
}
0 голосов
/ 02 марта 2009
ps -o pid= --ppid $PPID | xargs kill -9 
0 голосов
/ 27 июля 2010

если у вас есть pstree и perl в вашей системе, вы можете попробовать это:

perl -e 'kill 9, (`pstree -p PID` =~ m/\((\d+)\)/sg)'
...