Как надежно использовать ловушку, используя 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).
Рекомендации:
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 может обрабатывать ввод-вывод или какие-либо другие условия.