Почему мой bash-скрипт так долго реагирует на kill, когда работает в фоновом режиме?

(Вопрос пересмотрен, теперь, когда я больше понимаю о том, что на самом деле происходит):

У меня есть скрипт, который работает в фоновом режиме, периодически выполняет некоторую работу, а затем спит в течение 30 секунд:

echo "background script PID: $$"
trap 'echo "Exiting..."' INT EXIT
while true; do
    # check for stuff to do, do it
    sleep 30
done &

Если я попытаюсь убить этот скрипт через kill или же kill INTдля ответа на сигнал требуется 30 секунд.

Я отвечу на этот вопрос ниже, так как я нашел хорошее объяснение онлайн.

(Мой оригинальный, неловко неисследованный вопрос)

Этот вопрос для сценария bash, который включает в себя следующую ловушку:

trap 'echo "Exiting...">&2; kill $childPID 2>/dev/null; exit 0' \
SIGALRM SIGHUP SIGINT SIGKILL SIGPIPE SIGPROF SIGTERM \
SIGUSR1 SIGUSR2 SIGVTALRM SIGSTKFLT

Если я запускаю скрипт на переднем плане и нажимаю CTRL-C, он сразу же получает сигнал и завершает работу (менее одной секунды).

Если я запускаю тот же скрипт в фоновом режиме (&) и убить его через kill или же kill -INTДля получения сигнала требуется 30 секунд.

Почему это так и как я могу это исправить?

3 ответа

Решение

Возможная причина: сигналы, выдаваемые во время спящего процесса, не доставляются до запуска процесса. При запуске через командную строку процесс не спит, поэтому сигнал доставляется немедленно.

Как объяснено в http://mywiki.wooledge.org/SignalTrap -

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

Эта страница имеет очень хороший обзор обработки сигналов в bash и обходных путей к этой проблеме. Вкратце, один правильный способ справиться с ситуацией - отправить сигнал группе процессов, а не только родительскому процессу:

kill -INT -123   # will kill the process group with the ID 123

Перейдите на страницу, на которую ссылаются, для полного объяснения (нет смысла в моем воспроизведении этого здесь).

@RashaMatt, я не смог получить read Команда работать как рекламируется в вики Грега. Отправка сигнала сценарию просто не прервала чтение. Мне нужно было сделать это:

#!/bin/bash
bail() {
        echo "exiting"
        kill $readpid
        rm -rf $TMPDIR
        exit 0
}

sig2() {
  echo "doing stuff"
}

echo Shell $$ started.
trap sig2 SIGUSR2
trap bail SIGUSR1 SIGHUP SIGINT SIGQUIT SIGTERM
trap -p

TMPDIR=$(mktemp -p /tmp -d .daemonXXXXXXX)
chmod 700 $TMPDIR
mkfifo $TMPDIR/fifo
chmod 400 $TMPDIR/fifo

while : ; do
        read < $TMPDIR/fifo & readpid=$!
        wait $readpid
done

... отправить желаемый сигнал на pid оболочки, отображаемый с Shell $$ started линии и смотреть волнение.

waitправда, спать проще, но у некоторых нет sleep infinityи я хотел посмотреть, как у Грега read пример будет работать (что не получилось).

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