Как мне отслеживать и уничтожать все процессы, порожденные запуском сценария, не зная имен подпроцессов?

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

Например, мой скрипт-оболочка под названием pipeline_runner.sh делает следующее:

#!/bin/bash

# Some set up of the script ...

./monitor_job.sh ... arguments TBD ... &

script_path = "path/to/bash/script"

chmod u+x "$script_path"
"$script_path"

# ...

Каждый запуск pipeline_runner.sh запускает экземпляр monitor_job.sh в фоновом режиме для отслеживания конкретного запуска path/to/bash/script, запущенного этим запуском pipeline_runner.sh. Когда какое-то произвольное условие, определенное в monitor_job.sh, выполняется, оно должно иметь возможность уничтожить этот конкретный запуск path/to/bash/script вместе со всеми процессами, прямо или косвенно запущенными им.

Множество других процессов, запускаемых запуском path/to/bash/script, многочисленны и различаются, поэтому я пытаюсь выяснить, как захватить каждый скрипт, который создается в результате запуска, в какую-то группу или список и иметь возможность уничтожить их все, когда это необходимо. Уничтожить только начальный процесс $script_path недостаточно, поскольку все подпроцессы этого сценария сохранятся.

Важными второстепенными целями являются:

  • сделать эту динамику такой, чтобы она не зависела от того, какой сценарий обозначен $script_path. Это означает, что я не могу просто жестко закодировать конкретные имена команд для поиска.

  • осуществлять мониторинг в отдельном скрипте (monitor_job.sh), как описано, а не напрямую в pipeline_runner.sh.

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

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

Barmar 29.08.2024 18:08
monitor_job.sh также должен запустить $script_path или, по крайней мере, pipeline_runner.sh должен запуститься сначала script_path, а затем передать идентификатор процесса (доступный из $PID) в качестве аргумента monitor_job.sh. Идентификатор процесса не так надежен, поскольку всегда существует вероятность того, что процесс может завершиться и новый процесс получит идентификатор переработанного процесса, а вы не заметите. Однако родительский процесс всегда получает уведомление непосредственно при выходе одного из его дочерних процессов.
chepner 29.08.2024 18:16

Раз уж вы отметили Linux, я склоняюсь к Linux cgroups. SystemD использует эту возможность практически для того, что вы описываете. Однако я недостаточно авторитетен в CGroups, чтобы рекомендовать детали.

John Bollinger 29.08.2024 18:19

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

John Bollinger 29.08.2024 22:45

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

Raven 30.08.2024 11:25
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
5
58
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

kill -- -$$ завершит всю группу процессов.

Например, в следующем скрипте мы запускаем 2 подпроцесса, sleep 15 и sleep 30, тогда у нас могут быть другие задачи для запуска (в данном случае sleep 5), и поскольку мы удовлетворяем нашим критериям выхода, мы можем убить всю группу процессов.

#!/bin/sh
echo "Parent pid $$"
sleep 15 &
echo "child 1 pid $!"

sleep 30 &
echo "child 2 pid $!"

sleep 5
echo "criteria met"
kill -- -$$

Если мы запустим это с bash test.sh ; ps -ef | grep sleep, мы получим:

$ bash test.sh ;  ps -ef | grep sleep
Parent pid 87546
child 1 pid 87547
child 2 pid 87548
criteria met
Terminated: 15
  501 87595   789   0  2:08PM ttys007    0:00.00 grep sleep

Таким образом, мы видим, что подпроцессы также были убиты.

Проблема с этим подходом в том, что если бы сочетание клавиш Ctrl+C было введено сразу после моего выполнения, мы бы получили:

$ bash test.sh ;  ps -ef | grep sleep
Parent pid 88352
child 1 pid 88353
child 2 pid 88354
^C
  501 88353     1   0  2:10PM ttys007    0:00.00 sleep 15
  501 88354     1   0  2:10PM ttys007    0:00.00 sleep 30
  501 88391   789   0  2:10PM ttys007    0:00.00 grep sleep

Это означает, что различные подпроцессы будут продолжать работать как «сироты» (принятые процессом инициализации).

Чтобы решить эту проблему, мы могли бы использовать trap и изменить наш скрипт на:

#!/bin/sh

trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT

echo "Parent pid $$"

sleep 15 &
echo "child 1 pid $!"

sleep 30 &
echo "child 2 pid $!"

sleep 5
echo "criteria met"
exit 0

Обычное выполнение останется прежним, потому что exit 0 будет пойман ловушкой и, в свою очередь, kill -- -$$ будет выполнен, как и раньше.

Теперь, если мы запустим наш скрипт и введем ctrl+c сразу после выполнения, на этот раз мы получим:

Parent pid 91578
child 1 pid 91579
child 2 pid 91580
^CTerminated: 15
  501 91590   789   0  2:18PM ttys007    0:00.00 grep sleep

где мы видим, что подпроцесс также был убит.

Фантастика! Большое спасибо за мастер-класс! Это именно то, что я ищу.

Chephen 02.09.2024 22:27

Другие вопросы по теме