Как надежно использовать ловушку, используя Bash, выполняющий дочерние процессы переднего плана

У меня есть сценарий Bash, который запускает длительный процесс на переднем плане. Когда он получает сигнал SIGQUIT, он должен выполнять различные операции очистки, такие как уничтожение самого себя и всех своих дочерних процессов (посредством уничтожения группы процессов и т. Д.). Минимальный скрипт, который должен поймать сигнал, показан ниже (называется test_trap.sh):

#!/bin/bash

trap 'echo "TRAP CAUGHT"; exit 1' QUIT  # other required signals are omitted for brevity

echo starting sleep
sleep 11666
echo ending sleep

echo done

Я хотел бы послать сигнал SIGHUP процессу test_trap.sh скрипт. Тем не менее, отправка SIGHUP на test_trap.sh не вызывает выражение ловушки, но только когда я посылаю сигнал ребенку sleep 11666 процесс делает ловушку огнем. Ниже сессия bash, демонстрирующая это:

bash-4.1$ test_trap.sh &
[1] 19633
bash-4.1$ starting sleep

bash-4.1$ kill -s SIGQUIT 19633
bash-4.1$ jobs
[1]+  Running                 test_trap.sh &
bash-4.1$ ps -ef --forest --cols=10000 | grep '11666\|test_trap.sh' | grep -v grep
theuser   19633 12227  0 07:40 pts/4    00:00:00              \_ /bin/bash ./test_trap.sh
theuser   19634 19633  0 07:40 pts/4    00:00:00              |   \_ sleep 11666
bash-4.1$ kill -s SIGQUIT 19634
bash-4.1$ Quit (core dumped)
TRAP CAUGHT

[1]+  Exit 1                  test_trap.sh
bash-4.1$ ps -ef --forest --cols=10000 | grep '11666\|test_trap.sh' | grep -v grep
bash-4.1$ 

Обратите внимание, что "сон 11666" является просто представительным процессом. Этот процесс на самом деле может быть интерактивной подоболочкой (например, bash -i).

Почему не родитель test_trap.sh процесс поймать сигнал SIGHUP? Почему ловушка сработает только тогда, когда процесс для sleep 11666 было сигнализировано?

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

Этот сценарий предназначен для запуска в любой довольно свежей версии любого дистрибутива Linux, содержащего Bash (например, не Cygwin).

Рекомендации:

  1. уничтожение родительского процесса вместе с дочерним процессом с помощью SIGKILL
  2. Убить баш и дочерний процесс

3 ответа

Решение

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

trap 'kill $sleep_pid; echo "TRAP CAUGHT"; exit 1' QUIT

echo starting sleep
sleep 11666 &
sleep_pid=$!
wait
echo ending sleep

echo done

Запись sleep_pid и использовать его, чтобы убить sleep из обработчика необязательны.

На самом деле, bash получает сигнал, но находится в непрерывном состоянии, ожидая sleep Команда до конца. Когда это закончится, bash отреагирует на сигнал и выполнит ловушку.

Вы можете заменить длинные sleep команда с короткой петлей sleep команды:

while true
do
    sleep 1
done

При этом, если вы отправите сигнал процессу bash, он отреагирует, как только исполняющийся в данный момент sleep команда заканчивается, то есть не более 1 секунды после ее отправки.

Попробуй с сигналом SIGINT (то же самое, что отправляется нажатием Ctrl + C) вместо SIGKILL, Другие сигналы работают только тогда, когда bash может обрабатывать ввод-вывод или какие-либо другие условия.

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