Как правильно дождаться завершения дочернего процесса bash при перехвате сигналов - PullRequest
1 голос
/ 10 апреля 2019

У нас есть скрипт-обертка, который запускает рабочий DelayedJob в фоновом режиме. Этот сценарий ожидает, пока рабочий DelayedJob завершит работу, прежде чем выйти. Скрипт-обертка является основной точкой входа в контейнер Docker и устанавливает среду, необходимую для работы диджея.

Однако мы замечаем, что при выдаче Docker stop контейнер Docker должен ждать, пока работник DJ корректно завершит работу (или пока не истечет максимальное время ожидания), но этого не происходит. Контейнер выходит немедленно.

При выдаче Docker сообщения об остановке контейнера отправляет SIGTERM основному процессу - сценарию-оболочке. В скрипте-обёртке мы перехватываем SIGTERM и передаем сигнал в рабочий процесс DJ.

Это все еще не работает. Я создал контрольный пример с использованием простых сценариев Bash, который иллюстрирует проблему.

Сценарий p1:

#!/bin/bash
echo "P1: starting p1 and running p2 in bg"
exit_script() {
  echo "P1: Caught sigterm in p1, sending TERM to p2"
  kill -TERM $child
}

trap exit_script SIGINT SIGTERM

./p2 &
child=$!

echo "P1: waiting for p2 ($child)"
wait $child

echo "P1: Finished waiting for p2, exiting p1"

Сценарий p2:

#!/bin/bash
echo "P2: starting p2"
exit_script() {
  echo "P2: Caught sigterm"
  NEXT_WAIT_TIME=0
  until [ $NEXT_WAIT_TIME -eq 10 ]; do
    echo "P2: EXIT_SCRIPT loop $NEXT_WAIT_TIME"
    sleep $(( NEXT_WAIT_TIME++ ))
  done  
  exit
}

trap exit_script SIGINT SIGTERM

echo "P2: Sleeping for a while"

NEXT_WAIT_TIME=0
until [ $NEXT_WAIT_TIME -eq 10 ]; do
  echo "P2: Main Loop $NEXT_WAIT_TIME"
  sleep $(( NEXT_WAIT_TIME++ ))
done

echo "P2: Finished sleeping in p2"

Выход:

MBP:$ ./p1
P1: starting p1 and running p2 in bg
P1: waiting for p2 (74039)
P2: starting p2
P2: Sleeping for a while
P2: Main Loop 0
P2: Main Loop 1
P2: Main Loop 2
P2: Main Loop 3
P2: Main Loop 4
P1: Caught sigterm in p1, sending TERM to p2
P1: Finished waiting for p2, exiting p1
MBP:$ P2: Caught sigterm
P2: EXIT_SCRIPT loop 0
P2: EXIT_SCRIPT loop 1
P2: EXIT_SCRIPT loop 2
P2: EXIT_SCRIPT loop 3
P2: EXIT_SCRIPT loop 4
P2: EXIT_SCRIPT loop 5
P2: EXIT_SCRIPT loop 6
P2: EXIT_SCRIPT loop 7
P2: EXIT_SCRIPT loop 8
P2: EXIT_SCRIPT loop 9

Как видите, строка после вызова сценариев p1 к wait выполняется ДО кода в функции exit_script, которая вызывается при перехвате сигнала.

Решение состоит в том, чтобы заменить wait на цикл ожидания, который проверяет наличие дочернего PID, но почему wait не работает должным образом? Неверно ли использование wait?

1 Ответ

2 голосов
/ 10 апреля 2019

Ожидание прерывается входящим сигналом и не перезапускается.Вы должны иметь возможность просто добавить еще один вызов ожидания, чтобы заставить его завершить ожидание.Хотя, вероятно, есть лучший способ сделать это.

echo "P1: waiting for p2 ($child)"
wait $child
wait $child

echo "P1: Finished waiting for p2, exiting p1"
...