![Diferencia entre programas C y scripts de shell que reciben señales del fusor](https://rvso.com/image/1126777/Diferencia%20entre%20programas%20C%20y%20scripts%20de%20shell%20que%20reciben%20se%C3%B1ales%20del%20fusor.png)
Escribí el siguiente script de shell para un laboratorio de mi universidad. Debe observar un archivo de registro que se actualiza con frecuencia desde otro proceso y crear una cantidad de copias pasadas durante la invocación. Aquí está el código (logrotate.sh):
#!/bin/bash
# Usage:
# logrotate.sh [-n int] [-s signal] logfile
# where:
# int is an optional integer used to make int number of copies of logfile
# signal is the name of signal which shell command fuser must send to the process managing logfile
# this script lacks of a strong parameters checking
NCOPIES=4
LOGSIGNAL=USR1
#use of getopts to parse the arguments
while getopts "n:s:" OPTION ; do
case $OPTION in
n) NCOPIES="$OPTARG"
;;
s) LOGSIGNAL="$OPTARG"
;;
?) printf "Usage: %s [-n copies to keep] [-s signal to send] filename\n" $(basename $0) >&2
exit 1
;;
esac
done
#shift to read the last parameter (logfile)
shift $(($OPTIND - 1))
LOGFILE=$1
#create logfile.2 logfile.3 ... logfile.NCOPIES
for i in `seq $NCOPIES -1 1` ; do
test -f $LOGFILE.$i && mv $LOGFILE.$i $LOGFILE.$[ $i + 1 ]
done
mv $LOGFILE $LOGFILE.1
#sending signal to process which is writing to logfile to keep on writing to $LOGFILE(original name, without any extensions)
fuser -k -"$LOGSIGNAL" $LOGFILE.1
Entonces escribí dos scripts que cada segundo escriben en el archivo.registro:
-el programa C (logtest.c):
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
int fd = open("log", O_WRONLY | O_APPEND);
if(fd < 0 ){
printf("Impossible to open file %s.\n", "log");
return -1;
}
for(;;){
if(write(fd, "Ciao bello mio\n", 15) != 15){
write(2, "Error in writing data.\n", 23);
}
sleep(1);
}
close(fd);
exit(0);
}
-y el script de shell (logtest.sh):
#! /bin/bash
while true
do
echo $(date) >> log
sleep 1
done
cuando lanzo
Registro ./logtest.sh y
./logrotate.sh
la secuencia de comandoslogrotate.shmueve todos los archivos con los nombres correctos (registrose convierteregistro.1) y envía la señal al proceso propietario del archivoregistropara ese momento (por lo que el script de shelllogtest.sh) que luego sigue escribiendo en el archivoregistro. Además, parece que no hay diferencia en cuanto a qué señal envío con el fusor: reaccionará siempre de la misma manera.
Sin embargo, si lanzo
./logtest y
./logrotate.sh registro
sucede que el programa Cprueba de registrorecibe la señal del comandofusory luego termina.
Mi pregunta es: ¿por qué los dos programas de registro tienen reacciones diferentes a la señal enviada desde el fusor? Quiero decir, ¿por qué el script Schell sigue funcionando mientras el programa C finaliza?
En la página de manual del fusor en la sección RESTRICCIONES dice
La opción -k solo funciona en procesos.
¿Podría ser que los scripts de Shell no se consideren procesos reales en el Shell? Esto sería nuevo para mí... He buscado en Internet pero no encontré ninguna página sobrefusorprofundizar en la sección de señalización.
Respuesta1
Su secuencia de comandos logtest.sh
solo escribe log
y cierra el descriptor de archivo inmediatamente. Entonces, cuando llama fuser
, log.1
no hay ningún proceso que tenga un descriptor de archivo activo para este archivo.
Puedes simular esto ejecutando el while
bucle dentro de unlist
(while true; do echo $(date); sleep 1; done) >> log
Y ambos logtest.sh
y logtest.c
terminarán sin importar cuál SIGNAL
envíe porque no maneja la señal. Con bash
esto se puede hacer con trap '<COMMAND>' USR1
(eche un vistazo a man bash-builtins
). Pero no tengo idea de cómo se hace esto en C (nunca aprendí C).
Respuesta2
El problema es que fuser
solo funciona en procesos que actualmente usan un archivo que tiene un descriptor de archivo abierto en el kernel.
Si bien esto es cierto para su C
programa, no lo es para su bash
script:
echo $(date) >> log
Simplemente abre el archivo, stdout
lo agrega y lo cierra inmediatamente. Por lo tanto, el kernel nunca considera que el archivo ha sido abierto tras fuser
la verificación.
Una solución simple sería cambiar su bash
script para que el archivo se mantenga abierto hasta que while
finalice el ciclo:
#! /bin/bash
while true
do
echo $(date) >> log
sleep 1
done < log
log
De esta manera , se crea un descriptor de archivo al while
inicio del ciclo y se mantiene abierto hasta el while
final del ciclo.