Как лучше всего отправить сигнал всем членам группы процессов?
422
Я хочу убить целое дерево процессов. Каков наилучший способ сделать это с использованием любых распространенных языков сценариев? Я ищу простое решение.
@ MichaelLeBarbierGrünewald Не могли бы вы дать ссылку на эти программы?
пенополистирол летать
Ответы:
305
Вы не говорите, является ли дерево, которое вы хотите убить, одной группой процессов. (Это часто имеет место, если дерево является результатом разветвления от запуска сервера или командной строки оболочки.) Вы можете обнаружить группы процессов, используя GNU ps следующим образом:
ps x -o "%p %r %y %x %c "
Если вы хотите уничтожить группу процессов, просто используйте kill(1)команду, но вместо того, чтобы давать ей номер процесса, присвойте ему отрицание номера группы. Например, чтобы убить каждый процесс в группе 5112, используйте kill -TERM -- -5112.
kill -74313 -bash: kill: 74313: неверная спецификация сигнала. Если я добавлю kill -15 -GPID, он сработает отлично.
Адам Пек
51
Как обычно почти для любой команды, если вы хотите, чтобы обычный аргумент, начинающийся с -, не интерпретировался как переключатель, предшествуйте ему: -: kill - -GPID
ysth
9
pgrepможет предложить более простой способ найти идентификатор группы процессов. Например, чтобы убить группу процессов my-script.sh, запустите kill -TERM -$(pgrep -o my-script.sh).
Джош Келли
9
Лучше взгляните на stackoverflow.com/questions/392022/… это, безусловно, более элегантное решение, и если вам нужно перечислить pids детей, то используйте:ps -o pid --no-headers --ppid $PARENT_PID
Szymon Jeż
4
И если вы немного измените формат и отсортируете, вы увидите, что все процессы красиво сгруппированы и начинаются с (потенциально) родительского ps x -o "%r %p %y %x %c" | sort -nk1,2
элемента
199
Убить все процессы, принадлежащие одному и тому же дереву процессов, используя ID группы процессов ( PGID)
kill -- -$PGID Использовать сигнал по умолчанию ( TERM= 15)
kill -9 -$PGID Используйте сигнал KILL(9)
Вы можете извлечь PGIDиз любого Process-ID ( PID) того же дерева процессов
Отдельное спасибо 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 ' ')
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)
ограничение
Как замечают Дэвид и Хьюберт Карио , когда killвызывается процессом, принадлежащим к одному и тому же дереву, killрискует убить себя, прежде чем прекратить уничтожение всего дерева.
Поэтому обязательно запустите команду, используя процесс с другим идентификатором группы процессов .
Запустите дерево процессов в фоновом режиме, используя '&'
>./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
28348283492834928349 pts/328969Ss330210:00-bash
28349289572895728349 pts/328969 S 330210:00 \_ /bin/sh ./run-many-processes.sh
28957289582895728349 pts/328969 S 330210:00| \_ /bin/sh ./child.sh background
28958289612895728349 pts/328969 S 330210:00|| \_ /bin/sh ./grandchild.sh background
28961289652895728349 pts/328969 S 330210:00||| \_ sleep 999928958289632895728349 pts/328969 S 330210:00|| \_ /bin/sh ./grandchild.sh foreground
28963289672895728349 pts/328969 S 330210:00|| \_ sleep 999928957289592895728349 pts/328969 S 330210:00| \_ /bin/sh ./child.sh foreground
28959289602895728349 pts/328969 S 330210:00| \_ /bin/sh ./grandchild.sh background
28960289642895728349 pts/328969 S 330210:00|| \_ sleep 999928959289622895728349 pts/328969 S 330210:00| \_ /bin/sh ./grandchild.sh foreground
28962289662895728349 pts/328969 S 330210:00| \_ sleep 999928349289692896928349 pts/328969 R+330210:00 \_ ps fj
Команда pkill -P $PIDне убивает внука:
> pkill -P "$PID"./run-many-processes.sh: line 4:28958Terminated./child.sh background
./run-many-processes.sh: line 4:28959Terminated./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
28348283492834928349 pts/328987Ss330210:00-bash
28349289872898728349 pts/328987 R+330210:00 \_ ps fj
1289632895728349 pts/328987 S 330210:00/bin/sh ./grandchild.sh foreground
28963289672895728349 pts/328987 S 330210:00 \_ sleep 99991289622895728349 pts/328987 S 330210:00/bin/sh ./grandchild.sh foreground
28962289662895728349 pts/328987 S 330210:00 \_ sleep 99991289612895728349 pts/328987 S 330210:00/bin/sh ./grandchild.sh background
28961289652895728349 pts/328987 S 330210:00 \_ sleep 99991289602895728349 pts/328987 S 330210:00/bin/sh ./grandchild.sh background
28960289642895728349 pts/328987 S 330210: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
28348283492834928349 pts/329039Ss330210:00-bash
28349290392903928349 pts/329039 R+330210:00 \_ ps fj
Вывод
Я заметил в этом примере PIDи PGIDравны ( 28957).
Вот почему я изначально думал, что kill -- -$PIDбыло достаточно. Но в случае , когда процесс икра в пределах идентификатор процесса отличается от ID группы .Makefile
Я думаю, что kill -- -$(ps -o pgid= $PID | grep -o [0-9]*)это лучший простой способ убить целое дерево процессов при вызове из другого идентификатора группы (другого дерева процессов).
Привет @ Давид. Хороший вопрос. Я думаю, что killвсегда следует отправлять сигнал всему дереву, прежде чем получить собственный сигнал. Но в некоторых конкретных обстоятельствах / реализациях killможет посылать себе сигнал, прерываться и затем принимать собственный сигнал. Однако риск должен быть достаточно минимальным, и в большинстве случаев его можно игнорировать, поскольку до этого должны возникать другие ошибки. Может ли этот риск быть проигнорирован в вашем случае? Кроме того, другие ответы имеют эту общую ошибку ( killчасть уничтожаемого дерева процессов). Надеюсь, что это поможет .. Cheers;)
olibre
3
Это работает, только если сами подкоманды не становятся лидерами группы. Даже такие простые инструменты, как manэто сделать. С другой стороны, если вы хотите убить процесс gandchild из дочернего процесса, kill -- -$pidон не будет работать. Так что это не общее решение.
Юбер Карио
1
Примером является «ребенок», пытающийся убить своих детей (поэтому внуки команды, инициированной пользователем). IOW, попробуйте убить иерархию фонового процесса в child.sh.
Хьюберт Карио
1
> kill -QUIT - "$ PGID" # тот же сигнал, что и [CRTL + C] с клавиатуры ---- QUIT следует заменить на INT, чтобы быть правдой
Максим Холявкин
1
для OSX опция --no-headers не поддерживается, поэтому код должен быть обновлен до: PGID = "$ (ps -o pgid" $ PID "| grep [0-9] | tr -d '')"
Максим Холявкин
166
pkill -TERM -P 27888
Это уничтожит все процессы, которые имеют родительский процесс с идентификатором 27888.
В моем быстром тесте pgrep сообщал только о непосредственных дочерних элементах, поэтому это может не уничтожить всю иерархию.
haridsv
10
Я согласен с @haridsv: pkill -Pпосылает сигнал только ребенку => внук не получает сигнал => Поэтому я написал другой ответ, чтобы объяснить это. Приветствия ;-)
olibre
1
Из сценария bash, чтобы убить своих собственных детей, используйте pkill -TERM -P ${$}.
Франсуа Босолей
3
@Onlyjob, разве не опасно спать и убивать? Тем временем идентификаторы процессов могли быть повторно использованы ОС: вы можете убивать процессы, которые больше не являются вашими детьми. Я подозреваю, что вызов pkill должен был бы быть сделан снова, чтобы гарантировать это.
Франсуа Босолей
2
@Onlyjob К вашему сведению, я могу порождать 32768 процессов, однопоточных, с легким доступом к $ time for i in {1..32768}; do ( echo $BASHPID >> pids ); done real 0m18.860s
вводу / выводу
102
Чтобы рекурсивно убить дерево процессов, используйте 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 killingfor _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 1fi
killtree $@
В --аргументы 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. Смотрите мой ответ
x-yuri
1
-1 использует #! / Bin / bash вместо #! / Usr / bin / env bash (или, что еще лучше, POSIX конструирует и / bin / sh)
Good Person
Достаточно ли отправить SIGKILL вместо SIGTERM, чтобы гарантировать killtree()надежную работу?
Давид
3
если psне поддерживает --ppid, можно использовать pgrep -P ${_pid}вместо этого
Hubert Kario
16
Команда rkill изпакета pslist отправляет данный сигнал (илиSIGTERMпо умолчанию) указанному процессу и всем его потомкам:
Некоторые версии ps выдают предупреждение, если вы используете «-ax» вместо «ax». Таким образом: для ребенка в $ (ps -o pid, ppid ax | \ awk "{if (\ $ 2 == $ pid) {print \ $ 1}}")
Упрощение с помощью grep вместо sed ...pstree -p 24901 | grep -oP '(?<=\()[0-9]+(?=\))'
anishsane
2
вы должны добавить -lк pstree, так длинные строки не получают усечены; это также может быть упрощено для чтения kill `pstree -l -p 24901 |grep "([[:digit:]]*)" -o |tr -d '()'`(нет необходимости конвертировать \nв пространство, так как оно будет работать нормально), спасибо!
Водолей Сила
9
Модифицированная версия ответа Жиганга:
#!/usr/bin/env bashset-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 "fidone}while true;do sleep 1;done&
cpid=$!for i in $(seq 12);do
cpids $$ a
sleep 1done
killtree $cpid
echo ---
cpids $$ a
Вы можете использовать wait $pidтолько процессы, которые вы запустили, а не все процессы, так что это не общее решение
Hubert Kario
@Hubert Kario В этом случае wait просто выйдет с ненулевым статусом и продолжит выполнение скрипта. Я ошибаюсь? Но waitбудет подавлять Terminatedсообщение, если это ребенок.
x-yuri
9
Я не могу комментировать (недостаточно репутации), поэтому я вынужден добавить новый ответ , хотя это не совсем ответ.
Есть небольшая проблема с очень хорошим и подробным ответом, который @olibre дал 28 февраля. Вывод ps opgid= $PIDбудет содержать начальные пробелы для PID короче пяти цифр, потому что psэто оправдывает столбец (выравнивание по номерам). Во всей командной строке это приводит к отрицательному знаку, за которым следуют пробелы, за которыми следует PID группы. Простое решение состоит psв том, trчтобы убрать пробелы:
Функция setsid () должна создать новый сеанс, если вызывающий процесс не является лидером группы процессов. По возвращении вызывающий процесс должен быть лидером сеанса этого нового сеанса, должен быть лидером группы процессов новой группы процессов и не должен иметь управляющего терминала. Идентификатор группы процессов вызывающего процесса должен быть установлен равным идентификатору процесса вызывающего процесса. Вызывающий процесс должен быть единственным процессом в новой группе процессов и единственным процессом в новом сеансе.
Под этим я понимаю, что вы можете создать группу из начального процесса. Я использовал это в php, чтобы иметь возможность убить целое дерево процессов после его запуска.
Это может быть плохой идеей. Я был бы заинтересован в комментариях.
На самом деле это отличная идея и работает очень хорошо. Я использую его в тех случаях, когда я могу поместить процессы в одну группу процессов (или они уже находятся в одной группе).
вместо того, чтобы дать ему номер процесса, дайте ему отрицание номера группы. Как обычно почти для любой команды, если вы хотите, чтобы обычный аргумент, начинающийся с a, -не интерпретировался как переключатель, добавьте перед ним--
К сожалению, я только что понял, что дал тот же ответ, что и вы => +1. Но более того, я объясняю, как просто получить PGIDот PID. Что вы думаете? Приветствия
olibre
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
}
Я думаю, у вас есть дополнительное слово «ребенок» в конце 2-й строки.
мато
@mato - Это не лишнее. Он ограничивает область действия $childэтой функции, чтобы не мешать другим (нелокальным) переменным с тем же именем и гарантировать, что значение локальной переменной будет очищено после завершения функции.
Адам Кац
О, теперь я понял, я думал, что это было частью задания. Я думаю, что я должен перечитать (медленнее), прежде чем писать комментарий. :) Спасибо.
мато
Кстати, не лучше ли создать список детей, а затем начать убивать сверху (родитель)? Таким образом, мы могли бы избежать ситуации, когда родительский объект воссоздает дочерний элемент или продолжает выполнение следующего кода, который потенциально может изменить предполагаемое поведение.
мато
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()
Кажется, он отлично работает как на Mac, так и на Linux. В ситуациях, когда вы не можете полагаться на способность управлять группами процессов - например, при написании сценариев для тестирования программного обеспечения, которое должно быть построено в нескольких средах - эта техника обхода дерева определенно полезна.
Спасибо за вашу мудрость, ребята. Мой сценарий оставлял некоторые дочерние процессы при выходе, а подсказка отрицания упростила ситуацию. Я написал эту функцию для использования в других сценариях, если это необходимо:
# 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}
Вероятно, лучше убить родителя раньше детей; в противном случае родитель может снова породить новых детей, прежде чем его убьют. Это переживет убийство.
Моя версия PS отличается от вышеупомянутой; может быть, слишком стар, поэтому странный жаргон ...
Использование сценария оболочки вместо функции оболочки имеет много преимуществ ...
обратите внимание, что сценарий @zhighang SIGSTOP фиксирует родительский процесс и доставляет сигнал остановленному процессу, поэтому это не должно (AFAIK) вызывать состояние гонки между процессом создания дочерних элементов и доставкой сигнала. В вашей версии есть расхождение между получением списка детей и доставкой сигнала родителю.
Хьюберт Карио
1
Следующее было протестировано на FreeBSD, Linux и MacOS X и зависит только от pgrep и kill (версии ps -o не работают под BSD). Первый аргумент - родительский pid, дочерние элементы которого должны быть прекращены. Второй аргумент - логическое значение, определяющее, должен ли родительский pid также быть завершен.
Это отправит SIGTERM любому дочернему процессу / процессу внука в сценарии оболочки, и если SIGTERM не удастся, он будет ждать 10 секунд, а затем отправит kill.
Предыдущий ответ:
Следующее также работает, но уничтожит саму оболочку на BSD.
KillSubTree(){local parent="${1}"for child in $(ps -o pid=$parent);doif[ $$ -ne $child ];then(kill -s SIGTERM $child ||(sleep 10&& kill -9 $child &))>/dev/null 2>&1;fidone}# Example lanch from within scriptKillSubTree $$ >/dev/null 2>&1
Я разрабатываю решение zhigang, xyuri и solidsneck дальше:
#!/bin/bashif 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.";return1;fi;# this avoids that children are spawned or disappear.
kill -SIGSTOP $2 ;
_children=$(ps -o pid --no-headers --ppid $2);
_success=0for _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
Эта версия позволит избежать уничтожения своих предков - что вызывает поток дочерних процессов в предыдущих решениях.
Процессы должным образом останавливаются до определения списка дочерних элементов, поэтому новые дочерние элементы не создаются и не исчезают.
После уничтожения остановленные задания должны продолжать исчезать из системы.
Старый вопрос, я знаю, но все ответы, кажется, продолжают вызывать пс, что мне не понравилось.
Это решение на основе 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, я полагаю.
Если вы знаете pid того, что хотите убить, вы обычно можете перейти от идентификатора сеанса и всего в одном сеансе. Я бы дважды проверил, но я использовал это для сценариев, запускающих rsyncs в циклах, которые я хочу умереть, а не для запуска другого (из-за цикла), как если бы я просто killall'd rsync.
В sh команда jobs перечислит фоновые процессы. В некоторых случаях может быть лучше сначала убить самый новый процесс, например, более старый создал общий сокет. В этих случаях сортируйте PID в обратном порядке. Иногда вам нужно подождать, пока задания запишут что-то на диск или что-то подобное, прежде чем они остановятся.
И не убивай, если не надо!
for SIGNAL in TERM KILL;dofor CHILD in $(jobs -s|sort -r);do
kill -s $SIGNAL $CHILD
sleep $MOMENT
donedone
Уничтожение дочернего процесса в сценарии оболочки:
Много раз нам нужно убивать дочерние процессы, которые по какой-то причине зависают или блокируются. например. Проблема с FTP-соединением.
Есть два подхода,
1) Создать отдельного нового родителя для каждого дочернего элемента, который будет отслеживать и уничтожать дочерний процесс после истечения времени ожидания.
и наблюдайте за процессами, которые имеют имя «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.";fidoneif[ ${#TMP_CPIDs[@]}==0];then
echo "All commands completed.";break;else
unset CPIDs;
declare -A CPIDs;for PID in"${!TMP_CPIDs[@]}";doCPIDs[$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;fifi
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 достигнут, все дочерние процессы будут убиты родительским процессом. Вы можете переключать время и играть со скриптом, чтобы увидеть поведение. Одним из недостатков этого подхода является использование идентификатора группы для уничтожения всего дочернего дерева. Но сам родительский процесс относится к той же группе, поэтому он также будет уничтожен.
Вам может потребоваться назначить другой идентификатор группы родительскому процессу, если вы не хотите, чтобы родительский процесс был убит.
#/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
chronos
илиherodes
.Ответы:
Вы не говорите, является ли дерево, которое вы хотите убить, одной группой процессов. (Это часто имеет место, если дерево является результатом разветвления от запуска сервера или командной строки оболочки.) Вы можете обнаружить группы процессов, используя GNU ps следующим образом:
Если вы хотите уничтожить группу процессов, просто используйте
kill(1)
команду, но вместо того, чтобы давать ей номер процесса, присвойте ему отрицание номера группы. Например, чтобы убить каждый процесс в группе 5112, используйтеkill -TERM -- -5112
.источник
pgrep
может предложить более простой способ найти идентификатор группы процессов. Например, чтобы убить группу процессов my-script.sh, запуститеkill -TERM -$(pgrep -o my-script.sh)
.ps -o pid --no-headers --ppid $PARENT_PID
ps x -o "%r %p %y %x %c" | sort -nk1,2
Убить все процессы, принадлежащие одному и тому же дереву процессов, используя ID группы процессов (
PGID
)kill -- -$PGID
Использовать сигнал по умолчанию (TERM
= 15)kill -9 -$PGID
Используйте сигналKILL
(9)Вы можете извлечь
PGID
из любого Process-ID (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]*
печатает только последовательные цифры (не печатает пробелы или алфавитные заголовки).Дальнейшие командные строки
ограничение
kill
вызывается процессом, принадлежащим к одному и тому же дереву,kill
рискует убить себя, прежде чем прекратить уничтожение всего дерева.Длинная история
Запустите дерево процессов в фоновом режиме, используя '&'
Команда
pkill -P $PID
не убивает внука:Команда
kill -- -$PGID
убивает все процессы, включая внука.Вывод
Я заметил в этом примере
PID
иPGID
равны (28957
).Вот почему я изначально думал, что
kill -- -$PID
было достаточно. Но в случае , когда процесс икра в пределах идентификатор процесса отличается от ID группы .Makefile
Я думаю, что
kill -- -$(ps -o pgid= $PID | grep -o [0-9]*)
это лучший простой способ убить целое дерево процессов при вызове из другого идентификатора группы (другого дерева процессов).источник
kill
всегда следует отправлять сигнал всему дереву, прежде чем получить собственный сигнал. Но в некоторых конкретных обстоятельствах / реализацияхkill
может посылать себе сигнал, прерываться и затем принимать собственный сигнал. Однако риск должен быть достаточно минимальным, и в большинстве случаев его можно игнорировать, поскольку до этого должны возникать другие ошибки. Может ли этот риск быть проигнорирован в вашем случае? Кроме того, другие ответы имеют эту общую ошибку (kill
часть уничтожаемого дерева процессов). Надеюсь, что это поможет .. Cheers;)man
это сделать. С другой стороны, если вы хотите убить процесс gandchild из дочернего процесса,kill -- -$pid
он не будет работать. Так что это не общее решение.child.sh
.Это уничтожит все процессы, которые имеют родительский процесс с идентификатором 27888.
Или более надежный:
который график убивает 33 секунды позже и вежливо попросить процессы прекратить.
Смотрите этот ответ для уничтожения всех потомков.
источник
pkill -P
посылает сигнал только ребенку => внук не получает сигнал => Поэтому я написал другой ответ, чтобы объяснить это. Приветствия ;-)pkill -TERM -P ${$}
.$ time for i in {1..32768}; do ( echo $BASHPID >> pids ); done real 0m18.860s
Чтобы рекурсивно убить дерево процессов, используйте killtree ():
источник
--
аргументы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}"
killtree()
надежную работу?ps
не поддерживает--ppid
, можно использоватьpgrep -P ${_pid}
вместо этогоКоманда rkill изпакета pslist отправляет данный сигнал (или
SIGTERM
по умолчанию) указанному процессу и всем его потомкам:источник
Ответ Брэда - это то, что я бы порекомендовал, за исключением того, что вы можете полностью отказаться от него,
awk
если используете--ppid
опциюps
.источник
если вы знаете, передайте pid родительского процесса, вот сценарий оболочки, который должен работать:
источник
Я использую немного модифицированную версию метода, описанного здесь: https://stackoverflow.com/a/5311362/563175
Так это выглядит так:
где 24901 - PID родителя.
Это выглядит довольно уродливо, но отлично справляется со своей задачей.
источник
pstree -p 24901 | grep -oP '(?<=\()[0-9]+(?=\))'
-l
кpstree
, так длинные строки не получают усечены; это также может быть упрощено для чтенияkill `pstree -l -p 24901 |grep "([[:digit:]]*)" -o |tr -d '()'`
(нет необходимости конвертировать\n
в пространство, так как оно будет работать нормально), спасибо!Модифицированная версия ответа Жиганга:
источник
wait $pid
только процессы, которые вы запустили, а не все процессы, так что это не общее решениеwait
будет подавлятьTerminated
сообщение, если это ребенок.Я не могу комментировать (недостаточно репутации), поэтому я вынужден добавить новый ответ , хотя это не совсем ответ.
Есть небольшая проблема с очень хорошим и подробным ответом, который @olibre дал 28 февраля. Вывод
ps opgid= $PID
будет содержать начальные пробелы для PID короче пяти цифр, потому чтоps
это оправдывает столбец (выравнивание по номерам). Во всей командной строке это приводит к отрицательному знаку, за которым следуют пробелы, за которыми следует PID группы. Простое решение состоитps
в том,tr
чтобы убрать пробелы:источник
Чтобы добавить ответ Нормана Рэмси, возможно, стоит взглянуть на setsid, если вы хотите создать группу процессов.
http://pubs.opengroup.org/onlinepubs/009695399/functions/setsid.html
Под этим я понимаю, что вы можете создать группу из начального процесса. Я использовал это в php, чтобы иметь возможность убить целое дерево процессов после его запуска.
Это может быть плохой идеей. Я был бы заинтересован в комментариях.
источник
Вдохновленный комментариями YSTH
источник
PGID
отPID
. Что вы думаете? ПриветствияСледующая функция оболочки похожа на многие другие ответы, но она работает как в Linux, так и в BSD (OS X и т. Д.) Без внешних зависимостей, таких как
pgrep
:источник
$child
этой функции, чтобы не мешать другим (нелокальным) переменным с тем же именем и гарантировать, что значение локальной переменной будет очищено после завершения функции.Это очень легко сделать с Python, используя psutil . Просто установите psutil с pip, и тогда у вас будет полный набор инструментов для управления процессами:
источник
Исходя из ответа Жиганга, это позволяет избежать самоубийств:
источник
Если вы хотите убить процесс по имени:
или
источник
Это моя версия уничтожения всех дочерних процессов с помощью bash-скрипта. Он не использует рекурсию и зависит от команды pgrep.
использование
Содержание killtrees.sh
источник
Вот вариант ответа @ zhigang, который обходится без AWK, полагаясь только на собственные возможности парсинга Bash:
Кажется, он отлично работает как на Mac, так и на Linux. В ситуациях, когда вы не можете полагаться на способность управлять группами процессов - например, при написании сценариев для тестирования программного обеспечения, которое должно быть построено в нескольких средах - эта техника обхода дерева определенно полезна.
источник
Спасибо за вашу мудрость, ребята. Мой сценарий оставлял некоторые дочерние процессы при выходе, а подсказка отрицания упростила ситуацию. Я написал эту функцию для использования в других сценариях, если это необходимо:
Приветствия.
источник
если у вас есть pstree и perl в вашей системе, вы можете попробовать это:
источник
Вероятно, лучше убить родителя раньше детей; в противном случае родитель может снова породить новых детей, прежде чем его убьют. Это переживет убийство.
Моя версия PS отличается от вышеупомянутой; может быть, слишком стар, поэтому странный жаргон ...
Использование сценария оболочки вместо функции оболочки имеет много преимуществ ...
Тем не менее, это в основном идея жигангов
источник
Следующее было протестировано на FreeBSD, Linux и MacOS X и зависит только от pgrep и kill (версии ps -o не работают под BSD). Первый аргумент - родительский pid, дочерние элементы которого должны быть прекращены. Второй аргумент - логическое значение, определяющее, должен ли родительский pid также быть завершен.
Это отправит SIGTERM любому дочернему процессу / процессу внука в сценарии оболочки, и если SIGTERM не удастся, он будет ждать 10 секунд, а затем отправит kill.
Предыдущий ответ:
Следующее также работает, но уничтожит саму оболочку на BSD.
источник
Я разрабатываю решение zhigang, xyuri и solidsneck дальше:
Эта версия позволит избежать уничтожения своих предков - что вызывает поток дочерних процессов в предыдущих решениях.
Процессы должным образом останавливаются до определения списка дочерних элементов, поэтому новые дочерние элементы не создаются и не исчезают.
После уничтожения остановленные задания должны продолжать исчезать из системы.
источник
Старый вопрос, я знаю, но все ответы, кажется, продолжают вызывать пс, что мне не понравилось.
Это решение на основе awk не требует рекурсии и вызывает ps только один раз.
Или на одной строке:
По сути, идея состоит в том, что мы создаем массив (a) записей parent: child, а затем зацикливаемся вокруг массива, находя дочерних элементов для наших подходящих родителей, добавляя их в список наших родителей (p) по мере продвижения.
Если вы не хотите убивать процесс верхнего уровня, тогда
незадолго до того, как строка system () удалит его из набора уничтожений.
Имейте в виду, что здесь есть условие гонки, но это верно (насколько я вижу) для всех решений. Он делает то, что мне нужно, потому что сценарий, для которого он мне нужен, не создает много недолговечных детей.
Упражнение для читателя состояло бы в том, чтобы сделать его двухпроходным циклом: после первого прохода отправьте SIGSTOP всем процессам в списке p, затем выполните цикл для повторного запуска ps, а после второго прохода отправьте SIGTERM, затем SIGCONT. Если вы не заботитесь о хороших концах, то второй проход может быть просто SIGKILL, я полагаю.
источник
Если вы знаете pid того, что хотите убить, вы обычно можете перейти от идентификатора сеанса и всего в одном сеансе. Я бы дважды проверил, но я использовал это для сценариев, запускающих rsyncs в циклах, которые я хочу умереть, а не для запуска другого (из-за цикла), как если бы я просто killall'd rsync.
Если вы не знаете, pid, вы все еще можете вкладывать больше
источник
источник
kill -9
правда.kill -15
не поможет.В sh команда jobs перечислит фоновые процессы. В некоторых случаях может быть лучше сначала убить самый новый процесс, например, более старый создал общий сокет. В этих случаях сортируйте PID в обратном порядке. Иногда вам нужно подождать, пока задания запишут что-то на диск или что-то подобное, прежде чем они остановятся.
И не убивай, если не надо!
источник
Уничтожение дочернего процесса в сценарии оболочки:
Много раз нам нужно убивать дочерние процессы, которые по какой-то причине зависают или блокируются. например. Проблема с FTP-соединением.
Есть два подхода,
1) Создать отдельного нового родителя для каждого дочернего элемента, который будет отслеживать и уничтожать дочерний процесс после истечения времени ожидания.
Создайте test.sh следующим образом:
и наблюдайте за процессами, которые имеют имя «test» в другом терминале, используя следующую команду.
Выше сценарий создаст 4 новых дочерних процесса и их родителей. Каждый дочерний процесс будет работать в течение 10 секунд. Но по истечении 5 секунд соответствующие родительские процессы убьют этих детей. Таким образом, ребенок не сможет завершить выполнение (10 секунд). Поиграйте в эти моменты (переключатели 10 и 5), чтобы увидеть другое поведение. В этом случае дочерний процесс завершит выполнение за 5 секунд до истечения времени ожидания 10 секунд.
2) Позвольте текущему родительскому монитору и уничтожите дочерний процесс, как только истечет время ожидания. Это не создаст отдельного родителя для мониторинга каждого ребенка. Также вы можете правильно управлять всеми дочерними процессами внутри одного и того же родителя.
Создайте test.sh следующим образом:
и наблюдайте за процессами, которые имеют имя «test» в другом терминале, используя следующую команду.
Выше скрипт создаст 4 новых дочерних процесса. Мы храним pids всех дочерних процессов и зацикливаем их, чтобы проверить, закончили ли они свое выполнение или все еще работают. Дочерний процесс будет выполняться до времени CMD_TIME. Но если тайм-аут CNT_TIME_OUT достигнут, все дочерние процессы будут убиты родительским процессом. Вы можете переключать время и играть со скриптом, чтобы увидеть поведение. Одним из недостатков этого подхода является использование идентификатора группы для уничтожения всего дочернего дерева. Но сам родительский процесс относится к той же группе, поэтому он также будет уничтожен.
Вам может потребоваться назначить другой идентификатор группы родительскому процессу, если вы не хотите, чтобы родительский процесс был убит.
Более подробную информацию можно найти здесь,
Уничтожение дочернего процесса в сценарии оболочки
источник
Этот скрипт также работает:
#/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
источник
Я знаю, что это старо, но это лучшее решение, которое я нашел:
источник