
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 suceder esto?
Respuesta1
Si cambio de SIGINT a cualquier otra señal (probé SIGTERM, SIGUSR1), la trampa imprime "Ouch" como se esperaba.
Aparentemente no probaste SIGQUIT; probablemente encontrará 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 proceso o canalización en segundo plano, configuraba esos procesos para que ignoraran SIGINT y SIGQUIT, por lo que no terminarían si el usuario escribía posteriormente Ctrl+ C(interrumpir) o Ctrl+ \(salir) para una tarea de primer plano. Cuando apareció el control de trabajos, trajo consigo grupos de procesos, por lo que ahora todo lo que el shell necesita hacer es colocar el trabajo en segundo plano en un nuevo grupo de procesos; Siempre que ese no sea el grupo de procesos del terminal actual, los procesos no verán señales provenientes del teclado ( Ctrl+ C, Ctrl+ \ y Ctrl+ Z(SIGTSTP)). El shell puede dejar procesos en segundo plano con la disposición de señal predeterminada. De hecho, probablemente sea necesario para que Ctrl+ pueda eliminar los procesos Ccuando pasan a primer plano.
Pero los shells no interactivos no utilizan el control de trabajos. Tiene sentido que un shell no interactivo recurra al antiguo comportamiento de ignorar SIGINT y SIGQUIT para procesos en segundo plano, por la razón histórica: permitir que los procesos en segundo plano continúen ejecutándose, incluso si se les envían señales de tipo teclado. . Y los scripts de shell se ejecutan en shells no interactivos.
Y, si nos fijamos en el último párrafo bajo el trap
comando enfiesta(1), verás
Las señales ignoradas al ingresar al shell no se pueden capturar ni restablecer.
Entonces, si huyes ./scriptb.sh &
de tuinteractivosímbolo del sistema del shell, sus disposiciones de señal se dejan como están (aunque se ponen en segundo plano) y el trap
comando funciona como se esperaba. Pero, si lo ejecuta ./scripta.sh
(con o sin &
), ejecuta el script en un shell no interactivo. Y cuando se ejecuta ese shell no interactivo ./scriptb.sh &
, configura el scriptb
proceso para que ignore la interrupción y salga. Y por lo tanto el trap
comando scriptb.sh
falla silenciosamente.
Respuesta2
Con algo de seguimiento:
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
así scripta
lo ha bloqueado INT
y CHLD
señala; scriptb
hereda esas configuraciones a través de fork
(aquí llamado clone
). ¿Y qué está scriptb
haciendo? Si lo ejecutamos desde scripta
vía:
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 luego que las INT
señales primero reciben el manejo predeterminado y luego se ignoran. scriptb
ejecutar directamente desde el shell a continuación strace
muestra por contraste:
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 ignorarlo, antes de pasar al manejo repetido de las llamadas de suspensión. Bueno. Oh. Pongamos una cuña entre scripta
y scriptb
que se restablezca SIGINT
al estado predeterminado...
#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;
}
Se utiliza 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
se comporta de esta manera, ¿tal vez presentarles un error? El código sig.c
parece muy complicado y hay más manejo de señales en otros lugares...
Respuesta3
Problema extraño con trampa y SIGINT
Gracias a todos por las respuestas y por el tiempo que se tomaron para estudiar el problema.
Permítanme recapitular e integrar (con disculpas por lo que podría ser obvio en lo siguiente):
1) Olvidé agregar mi pregunta. También probé SIGQUIT y se comportó como SIGINT;
2) A partir de eso, ya sospechaba que el problema estaba relacionado con la disposición predeterminada de un bash interactivo para esas dos señales;
3) Su acción predeterminada que no ocurre al interactuar con bash se debe a que no tiene sentido salir o interrumpir nada cuando lo único que tienes es el mensaje. Si desea salir del shell, simplemente escriba exit;
4) No veo que SIGQUIT y SIGINT desempeñen un papel especial en el control del trabajo (al contrario de SIGTSTP, SIGTTOU, SIGTTIN);
5) No tiene sentido para mí que la disposición predeterminada de un bash interactivo para esas dos señales sea heredada por un shell en segundo plano (no interactivo) (el que ejecuta scriptb.sh en nuestro caso);
6) De hecho, así como el grupo de procesos en primer plano no hereda (del shell que lo inició) las disposiciones para SIGQUIT y SIGINT, en mi humilde opinión debería tener sentido que suceda lo mismo con los grupos de procesos en segundo plano.
7) Además, cualquiera que sea la disposición heredada, la trampa debería cambiarla.
8) Considerándolo todo, me inclino a estar de acuerdo con thrig y pensar que lo que estamos viendo aquí es un error.