bash behavior on sigterm

3

Tenha um script como abaixo:

#!/bin/bash
#
# run this script.  don't run it if it's already running.
#

PIDFILE=/tmp/script.pid
LOGFILE=script.log


if [[ -f $PIDFILE ]]; then
    echo "$PIDFILE exists.  Not going to run..."
    exit 0
fi

sleep 10m >> $LOGFILE 2>&1 &
PID=$!
echo $PID > $PIDFILE

trap "echo Exiting...; rm $PIDFILE; exit $?" INT TERM EXIT KILL

wait $PID

Estou invocando este script como abaixo:

timeout 2m ./test_script

No tempo limite ou ctrl + c, o script imprime "Saindo" duas vezes.

# timeout 2m ./test_script
^CExiting...
Exiting...
rm: cannot remove '/tmp/script.pid': No such file or directory

Abaixo está a saída de strace e mais dados:

# ps -ef | grep -v grep | egrep -i "sleep|time"
root      8571  4690  0 12:17 pts/0    00:00:00 timeout 2m ./test_script
root      8572  8571  0 12:17 pts/0    00:00:00 /bin/bash ./test_script
root      8573  8572  0 12:17 pts/0    00:00:00 sleep 10m
# cat /tmp/script.pid
8573
# strace -p 8571
Process 8571 attached - interrupt to quit
wait4(-1, 0x7fffc43fad4c, 0, NULL)      = ? ERESTARTSYS (To be restarted)
--- SIGINT (Interrupt) @ 0 (0) ---
kill(0, SIGINT)                         = 0
kill(0, SIGCONT)                        = 0
--- SIGCONT (Continued) @ 0 (0) ---
rt_sigreturn(0)                         = 61
--- SIGINT (Interrupt) @ 0 (0) ---
rt_sigreturn(0x2)                       = 61
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 8572
--- SIGCHLD (Child exited) @ 0 (0) ---
close(1)                                = 0
close(2)                                = 0
exit_group(0)                           = ?
Process 8571 detached

Alguém pode gentilmente me ajudar a entender as partes internas sobre por que o script captura o sinal duas vezes para imprimir "Saindo ..." 2 vezes?

    
por cog_n1t1v3 03.05.2015 / 21:23

1 resposta

1

Se você substituir sua instrução trap por essas três linhas:

trap "echo Exiting... INT;  exit $?" INT
trap "echo Exiting... TERM; exit $?" TERM
trap "echo Exiting... EXIT; exit $?" EXIT

você obterá a saída

Exiting... TERM
Exiting... EXIT

do qual podemos deduzir

  • A instrução trap … TERM faz com que o shell capture o sinal SIGTERM. O comando timeout envia ao processo um SIGTERM (por padrão) quando o tempo limite expirar. Então o shell pega o sinal e executa o comando especificado, incluindo echo , rm (em seu script real) e exit .
  • A instrução trap … EXIT faz com que o shell deixe uma nota adesiva dizendo "lembre-se de fazer isso antes de ir para casa". Então, quando a armadilha SIGTERM executa o comando exit , a armadilha EXIT é executada.
  • Quando o trap EXIT executa o comando exit , o script realmente sai, em vez de executar a armadilha EXIT e ir para o inferno da recursão.

Se você digitar Ctrl + C enquanto o script estiver sendo executado, você obterá a armadilha INT seguida pela armadilha EXIT. Se você executar o script sem timeout ou com uma duração de tempo limite maior que o tempo de espera, só obterá a interceptação EXIT.

Provavelmente é bom o suficiente para dizer

trap "exit $?" INT TERM
trap "echo Exiting...; rm $PIDFILE" EXIT

Eu acredito que a armadilha EXIT não precisa executar exit , porque você entra na armadilha EXIT executando um comando exit (incluindo o implícito no final do script), então, quando terminar de executar a interceptação EXIT ( echo e rm ), a casca não tem mais nada a fazer senão sair. A única pergunta é com qual status de saída o script sai. E, se você estivesse salvando algum valor de status de saída e fazendo rm $PIDFILE; exit $saved_status , isso pode ser interessante. Mas enquanto você estiver falando sobre rm $PIDFILE; exit $? , o script provavelmente irá sair com o status de saída do rm ; e isso provavelmente acontecerá por padrão se o rm for o último comando que você executa.

Eu fiz alguns testes rápidos que sugeriram que é possível deixar o

trap "exit" INT TERM

comando, mas eu não entendo isso. YMMV.

P.S. Como Thomas disse, trap … KILL é ineficaz.

    
por 06.05.2015 / 07:35