Extraño problema con trampa y SIGINT

Por favor explica esto:

#!/bin/bash # This is scripta.sh ./scriptb.sh & pid=$! echo $pid started sleep 3 while true do kill -SIGINT $pid echo scripta.sh $$ sleep 3 done 

 #!/bin/bash # This is scriptb.sh trap "echo Ouch;" SIGINT while true do echo scriptb.sh $$ sleep 1 done 

Cuando ejecuto ./scripta.sh la trampa no se imprime. Si cambio de SIGINT a cualquier otra señal (probé SIGTERM, SIGUSR1), la trampa imprime "Ouch" como se esperaba. ¿Cómo puede pasar esto?

Si cambio de SIGINT a cualquier otra señal (probé SIGTERM, SIGUSR1), la trampa imprime "Ouch" como se esperaba.

Aparentemente no has probado SIGQUIT; probablemente descubras que se comporta igual que SIGINT.

El problema es el control del trabajo.

En los primeros días de Unix, cada vez que el shell ponía un process o tubería en segundo plano, establecía esos processs para ignorar SIGINT y SIGQUIT, por lo que no se terminarían si el usuario escribía Ctrl + C (interrupción) o Ctrl + \ (salir) a una tarea en primer plano. Cuando apareció el control del trabajo, traía consigo grupos de processs, por lo que ahora todo lo que el shell necesita es colocar el trabajo de background en un nuevo grupo de processs; siempre que ese no sea el grupo de processs del terminal actual, los processs no verán señales provenientes del keyboard ( Ctrl + C , Ctrl + \ y Ctrl + Z (SIGTSTP)). El shell puede dejar processs en segundo plano con la disposition de señal pnetworkingeterminada. De hecho, probablemente tenga que hacerlo, para que los processs sean cancelados por Ctrl + C cuando se traigan al primer plano.

Pero las shells no interactivas no usan control de trabajo. Tiene sentido que un shell no interactivo recurra al antiguo comportamiento de ignorar SIGINT y SIGQUIT para processs en segundo plano, por la razón histórica: permitir que los processs en segundo plano continúen ejecutándose, incluso si se les envían señales de tipo keyboard. . Y los scripts de shell se ejecutan en shells no interactivos.

Y, si miras el último párrafo bajo el command trap en bash (1) , verás

Las señales ignoradas al ingresar al shell no se pueden atrapar o reiniciar.

Por lo tanto, si ejecuta ./scriptb.sh & desde el símbolo del sistema de su intérprete interactivo , sus disposiciones de señal se dejan en blanco (aunque se pongan en segundo plano) y el command trap funciona como se esperaba. Pero, si ejecuta ./scripta.sh (con o sin & ), ejecuta el script en un shell no interactivo. Y cuando ese shell no interactivo ejecuta ./scriptb.sh & , establece el process scriptb para ignorar la interrupción y salir. Y, por lo tanto, el command trap en scriptb.sh falla silenciosamente.

Con algún rastreo:

 strace -o aaa ./scripta 

podemos observar que por defecto

 read(255, "#!/bin/bash\n# this is scripta.sh"..., 153) = 153 rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0 rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0 rt_sigprocmask(SIG_BLOCK, [INT CHLD], [], 8) = 0 lseek(255, -108, SEEK_CUR) = 45 clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f1ee9b7ca10) = 2483 

entonces scripta ha bloqueado las señales INT y CHLD ; scriptb henetworkinga esas configuraciones a través del fork (aquí llamado clone ). ¿Y qué está haciendo scriptb ? Si lo ejecutamos desde scripta través de:

 strace -o bbb ./scriptb & 

Y luego buscamos cosas relacionadas con la señal, encontramos:

 rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0 rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7fb1b2e75250}, {SIG_IGN, [], 0}, 8) = 0 rt_sigaction(SIGINT, {SIG_IGN, [], SA_RESTORER, 0x7fb1b2e75250}, {SIG_DFL, [], SA_RESTORER, 0x7fb1b2e75250}, 8) = 0 

Lo que indica que no se está bloqueando nada, y que a las señales INT primero se les da el manejo pnetworkingeterminado, y luego se ignoran. scriptb ejecutar directamente desde el shell bajo strace por contraste muestra:

 rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7f2c3f6d4250}, {SIG_DFL, [], 0}, 8) = 0 rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7f2c3f6d4250}, {SIG_DFL, [], SA_RESTORER, 0x7f2c3f6d4250}, 8) = 0 rt_sigprocmask(SIG_BLOCK, [INT], [], 8) = 0 rt_sigaction(SIGINT, {0x45fbf0, [], SA_RESTORER, 0x7f2c3f6d4250}, {SIG_DFL, [], SA_RESTORER, 0x7f2c3f6d4250}, 8) = 0 rt_sigprocmask(SIG_BLOCK, [INT CHLD], [], 8) = 0 rt_sigprocmask(SIG_BLOCK, ~[RTMIN RT_1], [INT CHLD], 8) = 0 ... 

O nunca se ignora, antes de entrar en el manejo repetido de las llamadas de espera. Bueno. Uh. Vamos a poner un calce entre scripta y scriptb que restablece SIGINT al estado pnetworkingeterminado …

 #include <getopt.h> #include <signal.h> #include <stdlib.h> #include <unistd.h> int main(int argc, char *argv[]) { int ch; while ((ch = getopt(argc, argv, "h?")) != -1) { switch (ch) { case 'h': case '?': default: abort(); } } argc -= optind; argv += optind; if (argc < 1) abort(); signal(SIGINT, SIG_DFL); execvp(*argv, argv); abort(); return 1; } 

Utilizado de la siguiente manera:

 $ make defaultsig cc defaultsig.c -o defaultsig $ grep defaultsig scripta.sh ./defaultsig ./scriptb.sh & $ ./scripta.sh 12510 started scriptb.sh 12510 scriptb.sh 12510 scriptb.sh 12510 scripta.sh 12509 Ouch scriptb.sh 12510 scriptb.sh 12510 scriptb.sh 12510 scripta.sh 12509 Ouch ... 

Sí, funciona bien ahora. Sin embargo, no sé por qué bash comporta de esta manera, ¿tal vez presentar un error con ellos? El código en sig.c ve muy complicado, y hay más sig.c de señal en otro lado …

Extraño problema con trampa y SIGINT

Gracias a todos por las respuestas y por el time que le tomó estudiar el problema.

Permítanme recapitular e integrar (con disculpas por lo que podría ser obvio en lo siguiente):

1) Olvidé agregar en mi pregunta que había intentado SIGQUIT también y se comportó como SIGINT;

2) A partir de eso, ya sospeché que el problema estaba relacionado con la disposition pnetworkingeterminada de un bash interactivo para esas dos señales;

3) Su acción pnetworkingeterminada que no ocurre cuando se interactúa con bash es porque no tiene sentido dejar o interrumpir nada cuando lo único que tiene es el aviso. Si desea abandonar el shell, simplemente escriba exit;

4) No veo que SIGQUIT y SIGINT jueguen un papel especial en el control del trabajo (a diferencia de SIGTSTP, SIGTTOU, SIGTTIN);

5) No tiene sentido para mí que la disposition pnetworkingeterminada de un bash interactivo para esas dos señales sea henetworkingada por un shell de background (no interactivo), (el que ejecuta scriptb.sh en nuestro caso);

6) De hecho, así como el grupo de processs en primer plano no henetworkinga (del shell que lo inició) las disposiciones para SIGQUIT y SIGINT, en mi humilde opinión, debería tener sentido que ocurriera lo mismo con los grupos de processs en segundo plano.

7) Además, cualquiera que sea la disposition henetworkingada, la trampa debería cambiarla.

8) En general, me inclino a estar de acuerdo con thrig y pensar que lo que estamos viendo aquí es un error.