it-swarm.com.ru

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

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

398
Adam Peck

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

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

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

279
Norman Ramsey

Убить все процессы, принадлежащие одному и тому же дерево процессов, используя 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]* печатает только последовательные цифры (не печатает пробелы или алфавитные заголовки).

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

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 и Хьюберт Карио , когда kill вызывается процессом, принадлежащим к тому же дереву, kill рискует убить себя перед завершением уничтожения всего дерева.
  • Поэтому обязательно запустите команду, используя процесс с другим Process-Group-ID.

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

> 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, Идентификатор процесса отличается от Идентификатор группы

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

181
olibre
pkill -TERM -P 27888

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

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

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

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

Смотрите этот ответ для уничтожения всех потомков.

162
Onlyjob

Чтобы рекурсивно убить дерево процессов, используйте 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 [email protected]
99
zhigang

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

rkill [-SIG] pid/name...
15
Onlyjob

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

for child in $(ps -o pid -ax --ppid $PPID) do ....... done
11
Kim Stebel

Модифицированная версия ответа Жиганга:

#!/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
9
x-yuri

Я использую немного модифицированную версию метода, описанного здесь: https://stackoverflow.com/a/5311362/563175

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

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

где 24901 - PID родителя.

Это выглядит довольно уродливо, но отлично справляется со своей задачей.

9
Tomasz Werszko

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

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
9
brad.lane

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

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

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

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

8
hajamie

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

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

kill -- -$( ps opgid= $PID | tr -d ' ' )
8
tanager

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

kill -- -PGID

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

7
Steven Penny

Следующая функция Shell похожа на многие другие ответы, но она работает как в 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
David Röthlisberger

Это очень легко сделать с помощью 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
genericdave

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

killall -9 -g someprocessname

или же

pgrep someprocessname | xargs pkill -9 -g
4
Mika Vatanen

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

init_killtree() {
    local pid=$1 child

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

Вот вариант ответа @ 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
solidsnack

Это моя версия уничтожения всех дочерних процессов с помощью 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
marekdef

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

Это решение на основе 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
Whinger

Я разрабатываю решение 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
Peter Steier

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

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

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

Тем не менее, это в основном идея жигангов


#!/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
1
Peter Steier

Следующее было протестировано на 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 любому дочернему процессу/процессу внука в сценарии Shell, и если 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
Orsiris de Jong

В sh команда 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
jens

Если вы знаете 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
Morgan

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

# 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
Donald Duck

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

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

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

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

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

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

Убить группу, используя только имя процесса, принадлежащего группе:

kill -- -$(ps -ae o pid,pgrp,cmd | grep "[e]xample.py" | awk '{print $2}' | tail -1)

Это модификация ответа olibre, но вам не нужно знать PID, просто имя члена группы. 

Объяснение:

Чтобы получить идентификатор группы, вы делаете команду ps, используя аргументы, как показано, grep для вашей команды, но форматируя example.py с кавычками и используя скобку для первой буквы (это отфильтровывает саму команду grep), затем фильтруйте ее через awk, чтобы получить второе поле, которое является идентификатором группы. Хвост -1 избавляется от дубликатов групповых идентификаторов. Вы помещаете все это в переменную, используя синтаксис $ () и вуаля - вы получаете идентификатор группы. Таким образом, вы замените этот $ (беспорядок) на этот -группа выше. 

0
CoderBlue
ps -o pid= --ppid $PPID | xargs kill -9 
0
Harpreet

Убить дочерний процесс в сценарии Shell:

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

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

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

Убить дочерний процесс в сценарии Shell

0
Hemant Thorat