Linux

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

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

 

Ответ 1

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

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

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

 

Ответ 2

Для завершения всех процессов, принадлежащих одному дереву процессов, используется идентификатор группы процессов ( 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)

Объяснение

  • kill -9 -"$PGID"=> Отправляет сигнал 9 ( KILL) всем дочерним и вложенным процессам...

  • PGID=$(ps opgid= "$PID")=> Используется для получения идентификатора группы процессов из любого идентификатора процесса в дереве, а не только из идентификатора родительского процесса . Вариант ps opgid= $PIDis ps -o pgid --no-headers $PID where pgid может быть заменен на pgrp.
    Но:

    • Ps вставляет начальные пробелы, если PID меньше пяти цифр, и выравнивает по правому краю. Вы можете использовать:
      PGID=$(ps opgid= "$PID" | tr -d ' ');

    • Ps из OSX всегда печатает заголовок, поэтому:
      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"      # соответствует [CRTL+C] с клавиатуры

kill -QUIT -"$PGID"    # соответствует [CRTL+\] с клавиатуры

kill -CONT -"$PGID"  # перезапустить остановленный процесс (вышеуказанные сигналы не завершают его)

sleep 2                       # подождите завершения процесса (потребуется  больше времени)

kill -KILL -"$PGID"     # kill -9, если он не перехватывает сигналы (или глючит)

Ограничение:

  • когда 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 процесса отличается от ID группы.

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

 

Ответ 3

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

#!/bin/bash

killtree() {

    local _pid=$1

    local _sig=${2:--TERM}

    kill -stop ${_pid} # нужен для того, чтобы остановить быстрое форкирование родителя от запуске дочерних процессов между завершением порожденного дочернего и завершением родительского

    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 $@

 

Ответ 4

Модифицированная версия предыдущего ответа:

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

 

Схожие статьи

Linux

Как запускать приложения с графическим интерфейсом в контейнере Linux Docker

Linux

Существующие решения, позволяющие использовать контроль версий для файлов конфигурации сервера

Linux

Как предотвратить остановку фонового процесса после закрытия клиента SSH в Linux

Linux

Как узнать максимальное количество потоков на процесс в Linux?