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

#linux #bash #docker #delayed-job

#linux #bash #docker #отложенное задание

Вопрос:

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

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

Выполнение вызова Docker stop для контейнера отправляет 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 amp;
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:

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

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

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