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

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

Ответы [ 32 ]

8 голосов
/ 26 сентября 2013

Я не могу комментировать (недостаточно репутации), поэтому я вынужден добавить новый ответ , хотя это не совсем ответ.

Существует небольшая проблема с очень хорошим и подробным ответом, который @olibre дал 28 февраля. Вывод ps opgid= $PID будет содержать начальные пробелы для PID короче пяти цифр, поскольку ps оправдывает столбец выровняйте числа). Во всей командной строке это приводит к отрицательному знаку, за которым следуют пробелы, за которыми следует PID группы. Простое решение состоит в том, чтобы трубу ps до tr удалить пробелы:

kill -- -$( ps opgid= $PID | tr -d ' ' )
7 голосов
/ 31 декабря 2012

Вдохновленный комментариями ysth

kill -- -PGID

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

5 голосов
/ 13 августа 2013

Следующая функция оболочки похожа на многие другие ответы, но она работает как в Linux, так и в BSD (OS X и т. Д.) Без внешних зависимостей, таких как pgrep:

killtree() {
    local parent=$1 child
    for child in $(ps -o ppid= -o pid= | awk "\$1==$parent {print \$2}"); do
        killtree $child
    done
    kill $parent
}
5 голосов
/ 19 августа 2013

Это очень легко сделать с помощью Python, используя psutil . Просто установите psutil с pip, и тогда у вас будет полный набор инструментов для управления процессами:

def killChildren(pid):
    parent = psutil.Process(pid)
    for child in parent.get_children(True):
        if child.is_running():
            child.terminate()
4 голосов
/ 30 октября 2013

Исходя из ответа чжигана, это позволяет избежать самоубийства

init_killtree() {
    local pid=$1 child

    for child in $(pgrep -P $pid); do
        init_killtree $child
    done
    [ $pid -ne $$ ] && kill -kill $pid
}
4 голосов
/ 03 октября 2014

Если вы хотите убить процесс по имени:

killall -9 -g someprocessname

или

pgrep someprocessname | xargs pkill -9 -g
3 голосов
/ 14 ноября 2011

Это моя версия уничтожения всех дочерних процессов с помощью bash-скрипта. Он не использует рекурсию и зависит от команды pgrep.

Использование

killtree.sh PID SIGNAL

Содержимое killtrees.sh

#!/bin/bash
PID=$1
if [ -z $PID ];
then
    echo "No pid specified"
fi

PPLIST=$PID
CHILD_LIST=`pgrep -P $PPLIST -d,`

while [ ! -z "$CHILD_LIST" ]
do
    PPLIST="$PPLIST,$CHILD_LIST"
    CHILD_LIST=`pgrep -P $CHILD_LIST -d,`
done

SIGNAL=$2

if [ -z $SIGNAL ]
then
    SIGNAL="TERM"
fi
#do substring from comma to space
kill -$SIGNAL ${PPLIST//,/ }
3 голосов
/ 27 февраля 2014

Вот вариант ответа @ zhigang, который обходится без AWK, полагаясь только на собственные возможности синтаксического анализа Bash:

function killtree {
  kill -STOP "$1"
  ps -e -o pid= -o ppid= | while read -r pid ppid
                           do
                             [[ $ppid = $1 ]] || continue
                             killtree "$pid"  || true # Skip over failures
                           done
  kill -CONT "$1"          
  kill -TERM "$1"
}

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

1 голос
/ 08 ноября 2017

Старый вопрос, я знаю, но все ответы, кажется, продолжают вызывать ps, что мне не понравилось.

Это решение на основе awk не требует рекурсии и вызывает ps только один раз.

awk 'BEGIN {
  p=1390
  while ("ps -o ppid,pid"|getline) a[$1]=a[$1]" "$2
  o=1
  while (o==1) {
    o=0
    split(p, q, " ")
    for (i in q) if (a[q[i]]!="") {
      p=p""a[q[i]]
      o=1
      a[q[i]]=""
    }
  }
  system("kill -TERM "p)
}'

или в одну строку:

awk 'BEGIN {p=1390;while ("ps -o ppid,pid"|getline) a[$1]=a[$1]" "$2;o=1;while (o==1) {o=0;split(p, q, " ");for (i in q) {if (a[q[i]]!="") {p=p""a[q[i]];o=1;a[q[i]]=""}}}system("kill -TERM "p)}'

По сути, идея состоит в том, что мы создаем массив (a) записей parent: child, затем зацикливаемся вокруг массива, находя дочерних элементов для наших подходящих родителей, добавляя их в наш список родителей (p) по мере продвижения.

Если вы не хотите убивать процесс верхнего уровня, тогда выполните

sub(/[0-9]*/, "", p)

непосредственно перед тем, как строка system () удалит его из набора уничтожений.

Имейте в виду, что здесь есть условие гонки, но это верно (насколько я вижу) для всех решений. Он делает то, что мне нужно, потому что сценарий, для которого он мне нужен, не создает много недолговечных детей.

Упражнение для читателя состояло бы в том, чтобы сделать его двухпроходным циклом: после первого прохода отправьте SIGSTOP всем процессам в списке p, затем выполните цикл для повторного запуска ps, а после второго прохода отправьте SIGTERM, затем SIGCONT , Если вы не заботитесь о хороших концах, то второй проход может быть просто SIGKILL, я полагаю.

1 голос
/ 09 февраля 2017

Дорабатываю решения Жиганга, Сюри и Солидснека дальше:

 #!/bin/bash

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

_pid=$1
_sig=${2:-TERM}

# echo >&2 "killtree($_pid) mypid = $$"
# ps axwwf | grep -6 "^[ ]*$_pid " >&2 ;

function _killtree () {
    local _children
    local _child
    local _success

    if test $1 -eq $2 ; then # this is killtree - don't commit suicide!
        echo >&2 "killtree can´t kill it´s own branch - some processes will survive." ; 
        return 1 ;
      fi ;
    # this avoids that children are spawned or disappear.
    kill -SIGSTOP $2 ;

    _children=$(ps -o pid --no-headers --ppid $2) ;        
    _success=0 
    for _child in ${_children}; do
        _killtree $1 ${_child} $3 ;
        _success=$(($_success+$?)) ;
      done ;

    if test $_success -eq 0 ; then
        kill -$3 $2
      fi ;
    # when a stopped process is killed, it will linger in the system until it is continued
    kill -SIGCONT $2
    test $_success -eq 0 ;
    return $?
    }

_killtree $$ $_pid $_sig

Эта версия позволит избежать уничтожения своих предков, что приведет к потоку дочерних процессов в предыдущих решениях.

Процессы должным образом останавливаются до определения списка дочерних элементов, поэтому новые дочерние элементы не создаются и не исчезают.

После уничтожения остановленные задания должны продолжать исчезать из системы.

...