termA no se congela. Solo muestra la devolución de llamada en el cuadro de entrada. Simplemente presione el Enter
tecla para continuar con la entrada.
Antes de explicar tu problema, un poco de contexto sobre cómo read
el comando funciona. Lee los datos de entrada de stdin
hasta el EOF
se encuentra Es seguro decir la llamada a read
El comando no bloquea cuando se trata de leer archivos en el disco. Pero cuando stdin
está conectado a la terminal, el comando se bloqueará hasta que el usuario escriba algo.
¿Cómo funcionan los controladores de señales?
Una explicación simple sobre cómo funciona el manejo de la señal. Vea el siguiente fragmento en C
que solo actúa sobre SIGINT
(también conocido como CTRL+C
)
#include <stdio.h>
#include <signal.h>
/* signal handler definition */
void signal_handler(int signum){
printf("Hello World!\n");
}
int main(){
//Handle SIGINT with a signal handler
signal(SIGINT, signal_handler);
//loop forever!
while(1);
}
Registrará el manejador de señal y luego entrará en el bucle infinito. Cuando lleguemos a Ctrl-C
, todos podemos estar de acuerdo en que el controlador de señal signal_handler()
debe ejecutar y "Hello World!"
imprime en la pantalla, pero el programa estaba en un ciclo infinito. Para imprimir "Hello World!"
debe haber sido el caso que rompió el bucle para ejecutar el controlador de señal, ¿verdad? Por lo tanto, debería salir del bucle y del programa. Veamos:
gcc -Wall -o sighdl.o signal.c
./sighdl.o
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
Como indica el resultado, cada vez que emitimos Ctrl-C
, "Hello World!"
imprime, pero el programa vuelve al bucle infinito. Es solo después de emitir un SIGQUIT
señal con Ctrl-\
¿Salió realmente el programa? Dependiendo de tu ulimit
configuraciones, volcaría un núcleo o imprimiría el número de señal recibido.
Si bien la interpretación de que el ciclo saldría es razonable, no considera la razón principal para el manejo de la señal, es decir, el manejo de eventos asincrónicos. Eso significa que el manejador de señales actúa fuera del flujo estándar del control del programa; de hecho, todo el programa se guarda dentro de un contexto, y se crea un nuevo contexto solo para que se ejecute el controlador de señal. Una vez que el controlador de señal ha completado sus acciones, el contexto se vuelve a cambiar y comienza el flujo de ejecución normal (es decir, el while(1)
).
Para responder a sus preguntas,
La conclusión verificó que cuando bash está ejecutando un comando externo en primer plano, no maneja ninguna señal recibida hasta que finaliza el proceso en primer plano
Lo más importante a tener en cuenta aquí es el externo parte de mando. En el primer caso, donde sleep
es un proceso externo pero en el segundo caso, read
es un incorporado desde el propio shell. Entonces, la propagación de la señal a estos dos difiere en ambos casos
type read
read is a shell builtin
type sleep
sleep is /usr/bin/sleep
1. Abra una terminal, llamada termA, y ejecute el archivo creado callback.sh con /bin/bash callback.sh por primera vez, la información aparecerá instantáneamente.
Sí, este comportamiento es el esperado. Porque en este momento solo se define la función y el controlador de trampas está registrado en la función myCallback
y la señal aún no se recibe en el script. A medida que avanza la secuencia de ejecución, el mensaje del read
se lanza el aviso por primera vez.
2.Abra una nueva terminal, llamada termB y ejecute pkill -USR1 -f callback.sh
primera vez, la información aparece instantáneamente en termA
Sí, mientras que el read
el comando está esperando una cadena seguida de Enter pulsación de tecla que señala el EOF
, recibe una señal SIGUSR1
desde el otro terminal, el contexto de ejecución actual se guarda y el control cambia al controlador de señal que imprime la cadena con la fecha actual.
Tan pronto como el controlador termina de ejecutarse, el contexto se reanuda en el while
bucle en el que read
el comando todavía está esperando una cadena de entrada. Hasta el read
el comando es exitoso, todas las trampas de señal posteriores simplemente imprimirán la cadena dentro del controlador de señal.
Continúe en el término B, ejecute pkill -USR1 -f callback.sh
la segunda vez.
Igual que se explicó anteriormente, el read
El comando no está completo por una vez en su bucle while, solo si tiene éxito al leer una cadena, la siguiente iteración del bucle comenzará y se lanzará un nuevo mensaje de aviso.
Fuente de la imagen:La interfaz de programación de Linux por Michael KerrisK
La conclusión verificó que cuando bash está ejecutando un comando externo en primer plano, no maneja ninguna señal recibida hasta que finaliza el proceso en primer plano.
bash
lo hace manejar las señales en C
/ nivel de implementación, pero no ejecutará los controladores establecidos con trap
hasta que el proceso de primer plano haya terminado. Eso es requerido por el estándar:
When a signal for which a trap has been set is received while the shell is waiting for the completion of a utility executing a foreground command, the trap associated with that signal shall not be executed until after the foreground command has completed.
Tenga en cuenta que el estándar no hace ninguna diferencia entre comandos internos y externos; en el caso de una "utilidad" como read
, que no se ejecuta en un proceso separado, no es obvio si una señal ocurre en su "contexto" o en el del shell principal, y si un controlador se establece con trap
debe ejecutarse antes o después del read
regresa.
problema 1:callback.sh contiene un ciclo while infinito, cómo explicar que no maneja ninguna señal recibida hasta que finaliza el proceso en primer plano. En este caso, el proceso en primer plano nunca termina.
Eso está mal. bash
no está ejecutando el while
bucle en un proceso separado. Dado que no hay un proceso en primer plano bash
está esperando, puede ejecutar cualquier conjunto de controladores con trap
inmediatamente. Si read
fuera un comando externo, eso y no while
sería el proceso de primer plano.
problema 2:No please input something for foo: shown
en el término A;
vaya a termB, ejecute pkill -USR1 -f callback.sh
la tercera vez.
La siguiente información se muestra en termA nuevamente:callback function called at Mon Nov 19 09:07:24 HKT 2018
Todavía no please input something for foo:
se muestra en el término A. Por qué la información please input something for foo:
¿congelarse?
No se congela. Simplemente presione Entrar y volverá a aparecer.
Es simplemente que
a) bash
reiniciará el read
incorporado en su lugar cuando es interrumpido por una señal, no regresará y volverá a pasar por el while
bucle
b) no volverá a mostrar el aviso establecido con -p
en ese caso.
Note que bash
ni siquiera volverá a mostrar el aviso en el caso de que SIGINT
desde el teclado fue manejado, incluso si descarta la cadena leída hasta ahora del usuario:
$ cat goo
trap 'echo INT' INT
echo $$
read -p 'enter something: ' var
echo "you entered '$var'"
$ bash goo
24035
enter something: foo<Ctrl-C>^CINT
<Ctrl-C>^CINT
<Ctrl-C>^CINT
bar<Enter>
you entered 'bar'
Eso imprimirá you entered 'foobar'
si la señal fue enviada desde otra ventana con kill -INT <pid>
en lugar de con Ctrl-C de la terminal
Todo el material de esta última parte (cómo read
se interrumpe, etc) es muy bash
específico. En otros shells como ksh
o dash
, e incluso en bash
cuando se ejecuta en modo POSIX (bash --posix
), cualquier señal manejada en realidad interrumpirá el read
incorporado. En el ejemplo anterior, el shell imprimirá you entered ''
y salir después del primer ^C, y si el read
se llama desde un bucle, el bucle se reiniciará.