Убийство родительского процесса не завершает дочерний.

Вопрос или проблема

У меня есть вопрос.
Изучая управление процессами, я заметил странное поведение на CentOS 7.
Я знаю, что при завершении родительского процесса также завершаются дочерние процессы. Но не в следующем случае. Я выполнил команду dd, просто для примера:

[root@server2 ~]# dd if=/dev/zero of=/dev/null &
[1] 1756

[root@server2 ~]# ps fax | grep -B2 dd
1737 pts/2    S      0:00         \_ su -
1741 pts/2    S      0:00             \_ -bash
1756 pts/2    R      1:18                 \_ dd if=/dev/zero of=/dev/null

После этого я попытался завершить (сигналом SIGKILL) родительский процесс, то есть bash, но это действие не завершает процесс dd:

[root@server2 ~]# kill -9 1741
Killed
[user@server2 ~]#

Оболочка завершается, но, как вы можете видеть в выводе команды top, процесс dd все еще работает:

PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
1756 root      20   0  107948    612    512 R 99.9  0.1  10:06.98 dd

У вас есть идеи по этому поводу?

По умолчанию завершение родительского процесса не завершает дочерние процессы.

Я предлагаю поискать другие вопросы о том, как завершить и родительский, и дочерний процессы, используя группу процессов (отрицательный PID).

Хороший ответ о том, как это сделать подробно, можно найти на Process descendants

Я был в подобной ситуации и нашел ответ.

Краткое содержание;

попробуйте: kill -2 <parent_pid>

Вы узнаете, сработает ли этот метод для вас, если во время выполнения вашего скрипта нажатие Ctrl+C завершит родительский и все созданные процессы.

Причина этого в различных сигналах, которые kill может отправлять. По умолчанию kill <pid> отправляет сигнал 15 <SIGTERM>. Но обычный сигнал при нажатии Ctrl+C не является 15 <SIGTERM>, это на самом деле 2 <SIGINT>.

Таким образом, если вы kill -2 <pid>, это завершит ваш родительский процесс, как если бы его прервали с помощью Ctrl+C

источник

Пожалуйста, настройте в соответствии с вашими нуждами, особенно команду kill:

function kill_recurse() {
    cpids=`pgrep -P $1|xargs`
    for cpid in $cpids;
    do
        kill_recurse $cpid
    done
    echo "завершение $1"
    kill -9 $1
}

Пример использования: kill_recurse <my_parent_pid>

Для более простых случаев, когда вам не нужно рекурсивно находить внуков и праправнуков, это завершит родительский и дочерние процессы:

pkill -P <parent PID>

.

Ответ или решение

Управление процессами в Unix-подобных системах, таких как CentOS, может показаться простым на первый взгляд, но на практике это сложная область, требующая глубокого понимания сигналов, идентификации процессов и их взаимодействия. Рассмотрим ситуацию, где убийство родительского процесса не ведет за собой автоматическое завершение дочерних процессов, как можно было бы ожидать.

Теория

В Unix-подобных операционных системах каждый процесс имеет свой идентификатор (PID) и принадлежит кессии и/или группе процессов. Родительский процесс создает дочерние процессы, но он не отвечает за их завершение. Если родительский процесс завершился, то дочерние процессы остаются в системе и становятся так называемыми "осиротевшими" процессами, которым присваивается новый родитель — обычно это процесс init (с PID=1).

Когда вы отправляете сигнал для завершения процесса, этот сигнал передается только целевому процессу. Сигнал SIGKILL (-9) мгновенно завершает процесс, не предоставляя возможности для очистки, что также обозначает, что у процесса нет возможности корректно управлять своими дочерними процессами.

Пример

Вы столкнулись с ситуацией, в которой завершение оболочки Bash (родительского процесса) через SIGKILL не привело к завершению дочернего процесса dd. Это естественное поведение, так как SIGKILL просто завершает указанный процесс, не посылая сигнал его дочерним процессам. В вашей ситуации процесс dd стал осиротевшим и возможно продолжает работу под процессом init.

[root@server2 ~]# dd if=/dev/zero of=/dev/null &
[1] 1756
[root@server2 ~]# kill -9 1741

В данном случае процесс dd продолжает работать после завершения Bash.

Применение

Исходя из вышеизложенной теории, вы можете использовать различные подходы для обеспечения завершения как родительских, так и дочерних процессов:

  1. Использование сигналов:

    • Вместо использования SIGKILL, можно использовать SIGINT (kill -2 ), что эквивалентно комбинации Ctrl+C, используемой для прерывания процессов внутри интерактивных сессий. Это полезно, если процессы правильно обработают этот сигнал.
  2. Управление группами процессов:

    • Процессы могут принадлежать группе процессов. Используя отрицательный PID для отправки сигнала целой группе процессов можно завершить и родительские, и дочерние процессы: kill -TERM -<pgid>. Эта команда завершает все процессы в группе.
  3. Скрипт для рекурсивного завершения:

    • Возможно, вам потребуется более сложное решение для управления иерархией процессов. Например, вы можете использовать bash-функцию для рекурсивного завершения всех дочерних процессов:
      function kill_recurse() {
       cpids=`pgrep -P $1|xargs`
       for cpid in $cpids;
       do
           kill_recurse $cpid
       done
       echo "killing $1"
       kill -9 $1
      }

      Запуск kill_recurse <pid> обеспечит завершение всех связанных процессов в иерархии.

  4. Команда pkill:

    • В простых случаях pkill -P <parent PID> также может быть использована для завершения дочерних процессов вместе с родительским.

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

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

Оцените материал
Добавить комментарий

Капча загружается...