Cómo propagar una señal a los procesos infantiles de un script bash
- 3319
- 643
- Mario Gollum
Supongamos que escribimos un script que genera uno o más procesos de ejecución larga; Si dicho script recibe una señal como Firme
o Siglo
, Probablemente queramos que sus hijos también sean terminados (normalmente cuando los padres mueren, los niños sobreviven). Es posible que también queramos realizar algunas tareas de limpieza antes de que salga el script en sí mismo. Para poder alcanzar nuestro objetivo, primero debemos aprender sobre los grupos de procesos y cómo ejecutar un proceso en segundo plano.
En este tutorial aprenderás:
- ¿Qué es un grupo de procesos?
- La diferencia entre los procesos de primer plano y fondo
- Cómo ejecutar un programa en segundo plano
- Cómo usar el caparazón
esperar
integrado para esperar un proceso ejecutado en segundo plano - Cómo terminar los procesos infantiles cuando el padre recibe una señal
Requisitos y convenciones de software utilizados
Categoría | Requisitos, convenciones o versión de software utilizada |
---|---|
Sistema | Distribución independiente |
Software | No se necesita software específico |
Otro | Ninguno |
Convenciones | # - requiere que los comandos de Linux dados se ejecuten con privilegios raíz directamente como un usuario raíz o mediante el uso de sudo dominiops - Requiere que los comandos de Linux dados se ejecuten como un usuario regular no privilegiado |
Un ejemplo simple
Creemos un script muy simple y simulemos el lanzamiento de un proceso de larga ejecución:
#!/bin/bash trampa "se recibió la señal de eco!"Sigint Echo" El guión pid es $ "Sleep 30
Copiar Lo primero que hicimos en el guión fue crear una trampa para atrapar Firme
e imprima un mensaje cuando se reciba la señal. Que hicimos que nuestro script imprima su pid: Podemos obtener al expandir el $$
variable. A continuación, ejecutamos el dormir
comandar simular un proceso de ejecución largo (30
segundos).
Guardamos el código dentro de un archivo (digamos que se llama prueba.mierda
), hacerlo ejecutable y lanzarlo desde un emulador terminal. Obtenemos el siguiente resultado:
El script pid es 101248
Si estamos enfocados en el emulador terminal y presione Ctrl+C mientras el script se está ejecutando, un Firme
la señal es enviada y manejada por nuestro trampa:
El script pid es 101248 ^Csignal recibido!
Aunque la trampa manejó la señal como se esperaba, el script fue interrumpido de todos modos. Por que esto pasó? Además, si enviamos un Firme
señal al script utilizando el matar
Comando, el resultado que obtenemos es bastante diferente: la trampa no se ejecuta de inmediato y el script continúa hasta que el proceso infantil no salga (después 30
segundos de "dormir"). Por qué esta diferencia? Vamos a ver…
Grupos de procesos, empleos en primer plano y antecedentes
Antes de responder las preguntas anteriores, debemos comprender mejor el concepto de grupo de procesos.
Un grupo de procesos es un grupo de procesos que comparten el mismo pgid (ID de grupo de procesos). Cuando un miembro de un grupo de proceso crea un proceso infantil, ese proceso se convierte en miembro del mismo grupo de proceso. Cada grupo de proceso tiene un líder; Podemos reconocerlo fácilmente porque es pid y el pgid son lo mismo.
Podemos visualizar pid y pgid de ejecutar procesos utilizando el PD
dominio. La salida del comando se puede personalizar para que solo se muestren los campos que nos interesan: en este caso CMD, Pid y Pgid. Hacemos esto usando el -O
Opción, proporcionando una lista de campos separada por comas como argumento:
$ ps -a -o pid, pgid, cmd
Si ejecutamos el comando mientras nuestro script está ejecutando la parte relevante de la salida que obtenemos es la siguiente:
PID PGID CMD 298349 298349 /Bin /Bash ./prueba.SH 298350 298349 Dormir 30
Podemos ver claramente dos procesos: el pid del primero es 298349
, Igual que su pgid: Este es el líder del grupo de procesos. Fue creado cuando lanzamos el script como puede ver en el CMD columna.
Este proceso principal lanzó un proceso infantil con el comando Dormir 30
: Como se esperaba, los dos procesos están en el mismo grupo de procesos.
Cuando presionamos CTRL-C mientras nos enfocamos en el terminal desde el cual se lanzó el script, la señal no se envió solo al proceso principal, sino a todo el grupo de proceso. Que grupo de procesos? El Grupo de procesos de primer plano de la terminal. Se llama a todos los procesos miembros de este grupo procesos en primer plano, Todos los demás se llaman procesos de fondo. Esto es lo que el manual de Bash tiene que decir al respecto:
SABÍAS?Para facilitar la implementación de la interfaz de usuario al control de trabajo, el sistema operativo mantiene la noción de una identificación de grupo de proceso terminal actual. Miembros de este grupo de proceso (procesos cuya ID de grupo de proceso es igual a la ID de grupo de proceso terminal actual) Reciba señales generadas por teclado como Sigint. Se dice que estos procesos están en primer plano. Los procesos de fondo son aquellos cuya identificación de grupo de proceso difiere del terminal; Dichos procesos son inmunes a las señales generadas por el teclado.Cuando enviamos el Firme
señal con el matar
Comando, en cambio, solo nos dirigimos al PID del proceso principal; Bash exhibe un comportamiento específico cuando se recibe una señal mientras espera que se complete un programa: el "código de trampa" para esa señal no se ejecuta hasta que ese proceso haya terminado. Es por eso que el mensaje "Señal recibido" se mostró solo después del dormir
comando salido.
Para replicar lo que sucede cuando presionamos Ctrl-C en el terminal usando el matar
Comando para enviar la señal, debemos apuntar al grupo de proceso. Podemos enviar una señal a un grupo de procesos utilizando La negación del PID del líder del proceso, Entonces, suponiendo el pid del líder del proceso es 298349
(Como en el ejemplo anterior), ejecutaríamos:
$ Kill -2 -298349
Administrar la propagación de la señal desde el interior de un script
Ahora, supongamos que lanzamos un script de ejecución larga desde un shell no interactivo, y queremos que dicho script administre la propagación de la señal automáticamente, de modo que cuando reciba una señal como Firme
o Siglo
Termina a su niño potencialmente largo, y finalmente realiza algunas tareas de limpieza antes de salir. Cómo podemos hacer esto?
Como lo hicimos anteriormente, podemos manejar la situación en la que se recibe una señal en una trampa; Sin embargo, como vimos, si se recibe una señal mientras el shell está esperando que se complete un programa, el "código de trampa" se ejecuta solo después de que salga el proceso infantil.
Esto no es lo que queremos: queremos que el código de trampa se procese tan pronto como el proceso principal reciba la señal. Para lograr nuestro objetivo, debemos ejecutar el proceso infantil en el fondo: Podemos hacer esto colocando el Y
símbolo después del comando. En nuestro caso escribiríamos:
#!/Bin/Bash Trap 'Echo Signal recibida!'Sigint Echo "El script pid es $" dormir 30 y
Copiar Si dejaríamos el script de esta manera, el proceso principal saldría justo después de la ejecución del Dormir 30
comando, dejándonos sin la oportunidad de realizar tareas de limpieza después de que termine o se interrumpe. Podemos resolver este problema usando el shell esperar
incorporado. La página de ayuda de esperar
lo define de esta manera:
Espera cada proceso identificado por una identificación, que puede ser una identificación de proceso o una especificación de trabajo, e informa su estado de terminación. Si no se da ID, espera todos los procesos infantiles activos actualmente, y el estado de retorno es cero.
Después de establecer un proceso para ser ejecutado en segundo plano, podemos recuperar su pid en el ps!
variable. Podemos pasarlo como argumento para esperar
Para hacer que el proceso de los padres espere a su hijo:
#!/Bin/Bash Trap 'Echo Signal recibida!'Sigint echo "El guión pid es $" Sleep 30 y espera $!
Copiar Terminamos? No, todavía hay un problema: la recepción de una señal manejada en una trampa dentro del guión, causa el esperar
construido para regresar de inmediato, sin esperar la terminación del comando en segundo plano. Este comportamiento está documentado en el manual de bash:
Para resolver este problema tenemos que usar esperar
de nuevo, tal vez como parte de la trampa en sí. Así es como nuestro guión podría verse al final:
#!/bin/bash cleanup () echo "limpieza ..." # Nuestro código de limpieza va aquí trampa 'Echo Signal recibida!; matar "$ child_pid"; esperar "$ child_pid"; Cleanup 'Sigint Sigterter Echo "El script pid es $" sleep 30 & child_pid = "$!"espera" $ child_pid "
Copiar En el guión creamos un limpiar
función donde pudimos insertar nuestro código de limpieza e hicieron nuestro trampa
atrapar también el Siglo
señal. Esto es lo que sucede cuando ejecutamos este script y le enviamos una de esas dos señales:
- Se inicia el script y el
Dormir 30
El comando se ejecuta en el fondo; - El pid del proceso infantil está "almacenado" en el
Child_pid
variable; - El script espera la terminación del proceso infantil;
- El guión recibe un
Firme
oSiglo
señal - El
esperar
El comando regresa inmediatamente, sin esperar la terminación del niño;
En este punto se ejecuta la trampa. En eso:
- A
Siglo
señal (elmatar
predeterminado) se envía alChild_pid
; - Nosotros
esperar
Para asegurarse de que el niño termine después de recibir esta señal. - Después
esperar
Devuelve, ejecutamos ellimpiar
función.
Propagar la señal a varios niños
En el ejemplo anterior trabajamos con un script que tenía un solo proceso de hijo. ¿Qué pasa si un guión tiene muchos hijos, y qué pasa si algunos de ellos tienen hijos propios??
En el primer caso, una forma rápida de obtener el pids de todos los niños es usar el Jobs -P
Comando: este comando muestra los PID de todos los trabajos activos en el shell actual. Podemos que usar matar
para terminarlos. Aquí hay un ejemplo:
#!/bin/bash cleanup () echo "limpieza ..." # Nuestro código de limpieza va aquí trampa 'Echo Signal recibida!; matar $ (trabajos -p); esperar; Cleanup 'Sigint Sigterter Echo "El script pid es $" Sleep 30 & Sleep 40 y espera
Copiar El script lanza dos procesos en segundo plano: mediante el uso del esperar
incorporado sin argumentos, esperamos a todos y mantenemos vivo el proceso principal. Cuando el Firme
o Siglo
Las señales son recibidas por el guión, enviamos un Siglo
a los dos, con sus pids devueltos por el Jobs -P
dominio (trabajo
es en sí mismo un caparazón incorporado, por lo que cuando lo usamos, no se crea un nuevo proceso).
Si los niños tienen su propio proceso de hijos, y queremos cancelarlos a todos cuando el antepasado reciba una señal, podemos enviar una señal a todo el grupo de proceso, como vimos antes.
Esto, sin embargo, presenta un problema, ya que al enviar una señal de terminación al grupo de procesos, ingresaríamos un bucle "Sign-Sent/Traped" Traped ". Piénselo: en el trampa
para Siglo
Enviamos un Siglo
señal a todos los miembros del grupo de procesos; Esto incluye el script principal en sí mismo!
Para resolver este problema y aún poder ejecutar una función de limpieza después de que se terminen los procesos infantiles, debemos cambiar el trampa
para Siglo
Justo antes de enviar la señal al grupo de proceso, por ejemplo:
#!/bin/bash cleanup () echo "limpieza ..." # Nuestro código de limpieza va aquí trampa 'trampa "" Sigterm; matar 0; esperar; Cleanup 'Sigint Sigterter Echo "El script pid es $" Sleep 30 & Sleep 40 y espera
Copiar En la trampa, antes de enviar Siglo
al grupo de procesos, cambiamos el Siglo
trampa, de modo que el proceso principal ignora la señal y solo sus descendientes se ven afectados por ella. Observe también que en la trampa, para indicar el grupo de proceso, utilizamos matar
con 0
como pid. Esta es una especie de atajo: cuando el pid pasó a matar
es 0
, todos los procesos en el actual Se señalan el grupo de procesos.
Conclusiones
En este tutorial aprendimos sobre los grupos de procesos y cuál es la diferencia entre los procesos de primer plano y los antecedentes. Aprendimos que Ctrl-C envía un Firme
Señal a todo el grupo de proceso de primer plano del terminal de control, y aprendimos cómo enviar una señal a un grupo de procesos utilizando matar
. También aprendimos a ejecutar un programa en segundo plano y cómo usar el esperar
Shell incorporado para esperar a que salga sin perder el caparazón de los padres. Finalmente, vimos cómo configurar un script para que cuando reciba una señal termine a sus hijos antes de salir. Me he perdido algo? ¿Tiene sus recetas personales para realizar la tarea?? No dudes en hacerme saber!
Tutoriales de Linux relacionados:
- Gestión de procesos de fondo bash
- Scripting de bash y gestión de procesos multiprocesos en el ..
- Una introducción a la automatización, herramientas y técnicas de Linux
- Cómo ejecutar el comando en el fondo en Linux
- Mastering Bash Script Loops
- Cómo instalar la señal en Linux
- Cómo rastrear las llamadas del sistema realizadas por un proceso con Strace On ..
- Mint 20: Mejor que Ubuntu y Microsoft Windows?
- Comparación de Linux Apache Prefork vs Worker MPMS
- Cómo trabajar con grupos de paquetes DNF
- « MySQL cambia la contraseña del usuario
- Cómo reconstruir un paquete utilizando el sistema de compilación Arch Linux »