Как лучше всего отправить сигнал всем членам группы процессов?

414

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

  • 2
    Зомби должны уходить, когда работает системный жнец. Я признаю, что видел системы, в которых зомби задерживаются, но это нетипично.
  • 5
    Иногда эти затяжные зомби несут ответственность за некоторые страшные действия.
Показать ещё 3 комментария
Теги:
signals
process

31 ответ

286
Лучший ответ

Вы не говорите, что дерево, которое вы хотите убить, - это отдельная группа процессов. (Это часто бывает, если дерево является результатом разворачивания с начала сервера или командной строки оболочки.) Вы можете обнаружить группы процессов с помощью GNU ps следующим образом:

 ps x -o  "%p %r %y %x %c "

Если это группа процессов, которую вы хотите убить, просто используйте команду kill(1), но вместо того, чтобы указывать номер процесса, дайте ей отрицание номера группы. Например, чтобы убить каждый процесс в группе 5112, используйте kill -TERM -- -5112.

  • 3
    kill -74313 -bash: kill: 74313: неверная спецификация сигнала. Если я добавлю kill -15 -GPID, он сработает отлично.
  • 45
    Как обычно почти для любой команды, если вы хотите, чтобы обычный аргумент, начинающийся с -, не интерпретировался как переключатель, предшествуйте ему: -: kill - -GPID
Показать ещё 7 комментариев
175

Убейте все процессы, принадлежащие одному и тому же дереву процессов, с помощью Идентификатора группы процессов (PGID)

  • kill -- -$PGID   Использовать сигнал по умолчанию (TERM= 15)
  • kill -9 -$PGID   Используйте сигнал KILL (9)

Вы можете получить PGID из любого Идентификатора процесса (PID) того же дерева процессов

  • kill -- -$(ps -o pgid= $PID | grep -o '[0-9]*') (сигнал TERM)
  • kill -9 -$(ps -o pgid= $PID | grep -o '[0-9]*') (сигнал KILL)

Особая благодарность tanager и Speakus за вклад в $PID оставшиеся пространства и OSX совместимость.

Описание

  • kill -9 -"$PGID" = > Отправить сигнал 9 (KILL) всем дочерним и внукам...
  • PGID=$(ps opgid= "$PID") = > Получить идентификатор группы процессов из любого идентификатора процесса дерева, а не только идентификатор процесса-родителя. Вариант ps opgid= $PID ps -o pgid --no-headers $PID, где PGID можно заменить на pgrp.
    Но:
    • ps вставляет ведущие пробелы, когда PID составляет менее пяти цифр и выравнивается по правому краю, как отмечено tanager. Вы можете использовать:
      PGID=$(ps opgid= "$PID" | tr -d ' ')
    • ps из OSX всегда печатает заголовок, поэтому Speakus предлагает:
      PGID="$( ps -o pgid "$PID" | grep [0-9] | tr -d ' ' )"
  • grep -o [0-9]* печатает только последовательные цифры (не печатает пробелы или алфавитные заголовки).

Дополнительные командные строки

PGID=$(ps -o pgid= $PID | grep -o [0-9]*)
kill -TERM -"$PGID"  # kill -15
kill -INT  -"$PGID"  # correspond to [CRTL+C] from keyboard
kill -QUIT -"$PGID"  # correspond to [CRTL+\] from keyboard
kill -CONT -"$PGID"  # restart a stopped process (above signals do not kill it)
sleep 2              # wait terminate process (more time if required)
kill -KILL -"$PGID"  # kill -9 if it does not intercept signals (or buggy)

Ограничение

  • Как заметил davide и Hubert Kario, когда KILL вызывается процессом, принадлежащим к тому же дереву, KILL рискует убить себя, прежде чем прекратить уничтожение всего дерева.
  • Поэтому обязательно запустите команду, используя процесс с другим идентификатором группы процессов.

Длинная история

> cat run-many-processes.sh
#!/bin/sh
echo "ProcessID=$$ begins ($0)"
./child.sh background &
./child.sh foreground
echo "ProcessID=$$ ends ($0)"

> cat child.sh
#!/bin/sh
echo "ProcessID=$$ begins ($0)"
./grandchild.sh background &
./grandchild.sh foreground
echo "ProcessID=$$ ends ($0)"

> cat grandchild.sh
#!/bin/sh
echo "ProcessID=$$ begins ($0)"
sleep 9999
echo "ProcessID=$$ ends ($0)"

Запустите дерево процессов в фоновом режиме, используя '&'

> ./run-many-processes.sh &    
ProcessID=28957 begins (./run-many-processes.sh)
ProcessID=28959 begins (./child.sh)
ProcessID=28958 begins (./child.sh)
ProcessID=28960 begins (./grandchild.sh)
ProcessID=28961 begins (./grandchild.sh)
ProcessID=28962 begins (./grandchild.sh)
ProcessID=28963 begins (./grandchild.sh)

> PID=$!                    # get the Parent Process ID
> PGID=$(ps opgid= "$PID")  # get the Process Group ID

> ps fj
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
28348 28349 28349 28349 pts/3    28969 Ss   33021   0:00 -bash
28349 28957 28957 28349 pts/3    28969 S    33021   0:00  \_ /bin/sh ./run-many-processes.sh
28957 28958 28957 28349 pts/3    28969 S    33021   0:00  |   \_ /bin/sh ./child.sh background
28958 28961 28957 28349 pts/3    28969 S    33021   0:00  |   |   \_ /bin/sh ./grandchild.sh background
28961 28965 28957 28349 pts/3    28969 S    33021   0:00  |   |   |   \_ sleep 9999
28958 28963 28957 28349 pts/3    28969 S    33021   0:00  |   |   \_ /bin/sh ./grandchild.sh foreground
28963 28967 28957 28349 pts/3    28969 S    33021   0:00  |   |       \_ sleep 9999
28957 28959 28957 28349 pts/3    28969 S    33021   0:00  |   \_ /bin/sh ./child.sh foreground
28959 28960 28957 28349 pts/3    28969 S    33021   0:00  |       \_ /bin/sh ./grandchild.sh background
28960 28964 28957 28349 pts/3    28969 S    33021   0:00  |       |   \_ sleep 9999
28959 28962 28957 28349 pts/3    28969 S    33021   0:00  |       \_ /bin/sh ./grandchild.sh foreground
28962 28966 28957 28349 pts/3    28969 S    33021   0:00  |           \_ sleep 9999
28349 28969 28969 28349 pts/3    28969 R+   33021   0:00  \_ ps fj

Команда pkill -P $PID не убивает внука:

> pkill -P "$PID"
./run-many-processes.sh: line 4: 28958 Terminated              ./child.sh background
./run-many-processes.sh: line 4: 28959 Terminated              ./child.sh foreground
ProcessID=28957 ends (./run-many-processes.sh)
[1]+  Done                    ./run-many-processes.sh

> ps fj
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
28348 28349 28349 28349 pts/3    28987 Ss   33021   0:00 -bash
28349 28987 28987 28349 pts/3    28987 R+   33021   0:00  \_ ps fj
    1 28963 28957 28349 pts/3    28987 S    33021   0:00 /bin/sh ./grandchild.sh foreground
28963 28967 28957 28349 pts/3    28987 S    33021   0:00  \_ sleep 9999
    1 28962 28957 28349 pts/3    28987 S    33021   0:00 /bin/sh ./grandchild.sh foreground
28962 28966 28957 28349 pts/3    28987 S    33021   0:00  \_ sleep 9999
    1 28961 28957 28349 pts/3    28987 S    33021   0:00 /bin/sh ./grandchild.sh background
28961 28965 28957 28349 pts/3    28987 S    33021   0:00  \_ sleep 9999
    1 28960 28957 28349 pts/3    28987 S    33021   0:00 /bin/sh ./grandchild.sh background
28960 28964 28957 28349 pts/3    28987 S    33021   0:00  \_ sleep 9999

Команда kill -- -$PGID убивает все процессы, включая внука.

> kill --    -"$PGID"  # default signal is TERM (kill -15)
> kill -CONT -"$PGID"  # awake stopped processes
> kill -KILL -"$PGID"  # kill -9 to be sure

> ps fj
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
28348 28349 28349 28349 pts/3    29039 Ss   33021   0:00 -bash
28349 29039 29039 28349 pts/3    29039 R+   33021   0:00  \_ ps fj

Заключение

Я заметил, что в этом примере PID и PGID равны (28957).
Вот почему я изначально думал, что kill -- -$PID было достаточно. Но в случае, когда процесс появляется в Makefile, идентификатор процесса отличается от ID группы.

Я думаю, что kill -- -$(ps -o pgid= $PID | grep -o [0-9]*) - лучший простой трюк для уничтожения всего дерева процессов при вызове из другого идентификатора группы (другое дерево процессов).

  • 0
    Если kill вызывается процессом , принадлежащего к тому же дереву, делает kill риск убить себя перед завершением всего дерева?
  • 1
    Привет @ Давид. Хороший вопрос. Я думаю, что kill всегда должен посылать сигнал всему дереву, прежде чем получит собственный сигнал. Но в некоторых конкретных обстоятельствах / реализациях kill может отправлять себе сигнал, прерываться и затем принимать собственный сигнал. Однако риск должен быть достаточно минимальным, и в большинстве случаев его можно игнорировать, поскольку до этого должны возникать другие ошибки. Может ли этот риск быть проигнорирован в вашем случае? Более того, в других ответах есть эта распространенная ошибка ( kill части дерева процессов, которое уничтожается). Надеюсь, что это поможет .. Ура!)
Показать ещё 8 комментариев
165
pkill -TERM -P 27888

Это приведет к уничтожению всех процессов, у которых есть идентификатор родительского процесса 27888.

Или более надежный:

CPIDS=$(pgrep -P 27888); (sleep 33 && kill -KILL $CPIDS &); kill -TERM $CPIDS

который планирует убить 33 секунды позже и вежливо просить процессы прекратить.

Смотрите этот ответ для завершения всех потомков.

  • 19
    В моем быстром тесте pgrep сообщал только о непосредственных дочерних элементах, поэтому это может не уничтожить всю иерархию.
  • 10
    Я согласен с @haridsv: pkill -P отправляет сигнал только ребенку => внук не получает сигнал => Поэтому я написал другой ответ, чтобы объяснить это. Ура ;-)
Показать ещё 11 комментариев
99

Чтобы убить дерево процессов рекурсивно, используйте killtree():

#!/bin/bash

killtree() {
    local _pid=$1
    local _sig=${2:--TERM}
    kill -stop ${_pid} # needed to stop quickly forking parent from producing children between child killing and parent killing
    for _child in $(ps -o pid --no-headers --ppid ${_pid}); do
        killtree ${_child} ${_sig}
    done
    kill -${_sig} ${_pid}
}

if [ $# -eq 0 -o $# -gt 2 ]; then
    echo "Usage: $(basename $0) <pid> [signal]"
    exit 1
fi

killtree $@
  • 3
    В -- аргументы ps не работают на OS X. Для того, чтобы заставить его работать там заменить ps команду по: ps ax -o "pid= ppid=" | grep -E "${_regex}" | sed -E "s/${_regex}/\1/g где _regex определяется перед циклом for : local _regex="[ ]*([0-9]+)[ ]+${_pid}"
  • 5
    Остановленные процессы не уничтожаются с помощью SIGTERM. Смотри мой ответ
Показать ещё 8 комментариев
14

rkill команда из pslist package посылает данный сигнал (или SIGTERM по умолчанию) указанному процессу и всем его потомкам:

rkill [-SIG] pid/name...
11

Ответ на брэд - это то, что я тоже рекомендую, за исключением того, что вы можете полностью отказаться от awk, если вы используете параметр --ppid для ps.

for child in $(ps -o pid -ax --ppid $PPID) do ....... done
  • 0
    Это не работает для меня, если я по какой-то причине не достану -ax (Centos5). В противном случае это здорово!
9

Измененная версия ответа zhigang:

#!/usr/bin/env bash
set -eu

killtree() {
    local pid
    for pid; do
        kill -stop $pid
        local cpid
        for cpid in $(pgrep -P $pid); do
            killtree $cpid
        done
        kill $pid
        kill -cont $pid
        wait $pid 2>/dev/null || true
   done
}

cpids() {
    local pid=$1 options=${2:-} space=${3:-}
    local cpid
    for cpid in $(pgrep -P $pid); do
        echo "$space$cpid"
        if [[ "${options/a/}" != "$options" ]]; then
            cpids $cpid "$options" "$space  "
        fi
    done
}

while true; do sleep 1; done &
cpid=$!
for i in $(seq 1 2); do
    cpids $$ a
    sleep 1
done
killtree $cpid
echo ---
cpids $$ a
  • 0
    вы можете wait $pid только на запущенных вами процессах, а не на всех процессах, так что это не общее решение
  • 0
    @Hubert Kario В этом случае wait просто выйдет с ненулевым статусом и продолжит выполнение скрипта. Я ошибся? Но wait будет подавлять Terminated сообщение, если это ребенок.
9

Я использую немного модифицированную версию описанного здесь метода: https://stackoverflow.com/questions/5213973/walking-a-process-tree

Итак, это выглядит так:

kill `pstree -p 24901 | sed 's/(/\n(/g' | grep '(' | sed 's/(\(.*\)).*/\1/' | tr "\n" " "`

где 24901 является родительским PID.

Это выглядит довольно уродливо, но отлично работает.

  • 1
    Упрощение с помощью grep вместо sed ... pstree -p 24901 | grep -oP '(?<=\()[0-9]+(?=\))'
  • 2
    вы должны добавить -l к pstree , чтобы длинные строки не усекались; это также может быть kill `pstree -l -p 24901 |grep "([[:digit:]]*)" -o |tr -d '()'` для чтения с помощью kill `pstree -l -p 24901 |grep "([[:digit:]]*)" -o |tr -d '()'` (нет необходимости преобразовывать \n в космос как то нормально будет работать), спасибо!
9

если вы знаете, передать pid родительского процесса, вот оболочка script, которая должна работать:

for child in $(ps -o pid,ppid -ax | \
   awk "{ if ( \$2 == $pid ) { print \$1 }}")
do
  echo "Killing child process $child because ppid = $pid"
  kill $child
done
  • 0
    Некоторые версии ps выдают предупреждение, если вы используете «-ax» вместо «ax». Таким образом: для потомка в $ (ps -o pid, ppid ax | \ awk "{if (\ $ 2 == $ pid) {print \ $ 1}}")
8

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

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

kill -- -$( ps opgid= $PID | tr -d ' ' )
  • 0
    Спасибо, танагер. Я исправлю свой ответ;) ура
8

Чтобы добавить к ответу Нормана Рэмси, может быть стоит посмотреть на setsid, если вы хотите создать группу процессов.
http://pubs.opengroup.org/onlinepubs/009695399/functions/setsid.html

Функция setsid() должна создать новый сеанс, если вызывающий процесс а не лидера группы процессов. на вернуть вызывающий процесс руководитель сессии этого нового сессии, должна быть группа процессов лидер новой группы процессов и не должен иметь управляющего терминала. Идентификатор группы процессов вызывающего процесс должен быть установлен равным идентификатор процесса вызывающего процесса. вызывающий процесс должен быть единственным процесса в новой группе процессов и единственный процесс в новой сессии.

Что я подразумеваю, что вы можете создать группу из начального процесса. Я использовал это в php, чтобы иметь возможность убить все дерево процессов после его запуска.

Это может быть плохая идея. Меня бы интересовали комментарии.

  • 0
    На самом деле это отличная идея и работает очень хорошо. Я использую его в тех случаях, когда я могу поместить процессы в одну группу процессов (или они уже находятся в одной группе).
6

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

kill -- -PGID

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

  • 0
    К сожалению, я только что понял, что дал тот же ответ, что и вы => +1. Но более того, я объясняю, как просто получить PGID из PID . Как вы думаете? ура
5

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

def killChildren(pid):
    parent = psutil.Process(pid)
    for child in parent.get_children(True):
        if child.is_running():
            child.terminate()
5

Следующая функция оболочки похожа на многие другие ответы, но она работает как на 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
}
  • 0
    Я думаю, что у вас есть дополнительное слово "ребенок" в конце 2-й строки.
  • 0
    @mato - Это не лишнее. Он ограничивает область действия $child этой функцией, чтобы не мешать другим (нелокальным) переменным с тем же именем и гарантировать, что значение локальной переменной будет очищено после завершения функции.
Показать ещё 2 комментария
4

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

killall -9 -g someprocessname

или

pgrep someprocessname | xargs pkill -9 -g
4

На основе ответа на zhigang это позволяет избежать самоубийства:

init_killtree() {
    local pid=$1 child

    for child in $(pgrep -P $pid); do
        init_killtree $child
    done
    [ $pid -ne $$ ] && kill -kill $pid
}
3

Вот вариант ответа @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. В ситуациях, когда вы не можете полагаться на возможность управлять группами процессов - например, при написании сценариев для тестирования части программного обеспечения, которое должно быть построено в нескольких средах, эта техника древовидной ходьбы определенно полезна.

3

Это моя версия убийства всех дочерних процессов с помощью bash script. Он не использует рекурсию и зависит от команды 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//,/ }
1

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

killtree() { 
    for p in $(pstree -p $1 | grep -o "([[:digit:]]*)" |grep -o "[[:digit:]]*" | tac);do
        echo Terminating: $p 
        kill $p
    done
}
1

Старый вопрос, я знаю, но все ответы, похоже, продолжают вызывать 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) родительских: дочерние записи, затем обходим вокруг массива, находящего детей для наших совпадающих родителей, добавляя их в список наших родителей (p), когда мы идем.

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

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

перед тем, как строка system() удалит ее из набора kill.

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

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

1

Я разработал решение zhigang, xyuri и solidsneck далее:

 #!/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

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

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

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

1

Следующее тестирование было протестировано на 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 в любой процесс child/grandchild в оболочке script, и если 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

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

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

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

Тем не менее, в основном идея zhigangs


#!/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
    обратите внимание, что сценарий @zhighang SIGSTOP фиксирует родительский процесс и доставляет сигнал остановленному процессу, поэтому это (AFAIK) не должно вызывать состязание между процессом, создающим дочерние элементы, и доставкой сигнала. Ваша версия, тем не менее, борется между получением списка детей и доставкой сигнала родителю.
0

Этот script также работает:

#/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

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

Много раз нам нужно убить дочерний процесс, который по какой-то причине повешен или заблокирован. например. Ошибка подключения к 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;

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

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

Над script создадут 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;

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

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

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

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

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

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

0

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

И не убивайте, если вам не нужно!

for SIGNAL in TERM KILL; do
  for CHILD in $(jobs -s|sort -r); do
    kill -s $SIGNAL $CHILD
    sleep $MOMENT
  done
done
0

Если вы знаете pid того, что хотите убить, вы обычно можете перейти от идентификатора сеанса и всего в том же сеансе. Я бы дважды проверял, но я использовал это для скриптов, запускающих rsyncs в циклах, которые я хочу умереть, и не запускать другой (из-за цикла), как если бы я просто убил 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

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

perl -e 'kill 9, (`pstree -p PID` =~ m/\((\d+)\)/sg)'
0
ps -o pid= --ppid $PPID | xargs kill -9 
  • 11
    Не kill -9 , правда.
  • 2
    Иногда kill -15 не поможет.
0

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

# kill my group subprocesses:          killGroup
# kill also myself:                      killGroup -x
# kill another group 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
}

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

-3

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

  • 0
    это не убивает дочерний процесс

Ещё вопросы

Сообщество Overcoder
Наверх
Меню