¡Que inventen ellos!
Reflexiones y otros escritos de un geek
Tareas en script cada n segundos
Como el geekismo de este blog ha bajado mucho últimamente, creo que es hora de contar alguna cosilla friki.

El grueso de la funcionalidad de observación, aquí en el NOT, se obtiene ejecutando programas disponibles en el shell de unas estaciones Linux. Esto nos permite hacer (con bastante rapidez) shell scripts que ejecuten tareas habituales, como las de obtención de imágenes de calibración, observación múltiple de un objeto para aplicar dithering, etc.

Hoy nos ha llegado el jefe (que de astrofísica sabrá un huevo, pero de ordenadores, el pobre...) preguntando si podemos hacer que una tarea se ejecute en segundo plano cada (digamos) 15 segundos dentro de esos scripts. Empezamos diciéndole que sí, pero yo que me huelo las dificultades de sus ideas a la milla, le pregunto: "cuando dices que hay que hacerlo cada 15 segundos, te refieres que la tarea tiene que empezar cada 15 segundos exactamente?". La respuesta, como me temía, fue... "sí". Nuestra respuesta fue: "olvídalo". Solemos hacerlo cuando no estamos seguros de si habrá una manera fácil y queremos desanimarle de entrada ;)

Si alguno no ha captado la sutileza, no es lo mismo ejecutar algo cada 15 segundos que ejecutar, dejar pasar 15 segundos y ejecutar de nuevo. Obsérvese:


El diagrama no necesita mucha explicación. Representa la ejecución de dos tareas y el tiempo crece hacia la derecha. Vemos que la tarea en la parte de arriba se ejecuta cada 5 unidades de tiempo (digamos, segundos), mientras que la segunda se ejecuta cada 5.2 unidades de tiempo. Lo primero es lo que quiere el jefe. En el segundo caso, ponemos a ejecutar la tarea y entonces indicamos que queremos esperar 5 segundos. Ese 0.2 de unidad de tiempo extra se debe a lo que tarda el sistema en hacer cosas (empezar a ejecutar la tarea que va a segundo plano, devolvernos el control, etc) y lo he representado con lo que va desde un "inicio de tarea" hasta las líneas punteadas.

Si hemos dicho que las unidades de tiempo son 5 segundos, puede sorprendernos que el ordenador tarde 2 décimas en permitirnos continuar. En realidad es una exageración, que nos permite ver enseguida los efectos del retraso acumulado por cada ejecución. Imaginemos que podemos obtener información horaria del ordenador por cada ejecución, con precisión de segundos. Si el retraso fuera de 2 centésimas de segundo, veríamos los efectos en menos de 5 minutos. Aún si fuera de 2 milésimas de segundo, en menos de tres cuartos de hora. Es un claro ejemplo de que una piedrecita no hace nada, pero poco a poco haces una montaña. Y en aplicaciones de tiempo real "hard" (no es nuestro caso), es totalmente inaceptable.

El problema que se nos presenta, en particular, es que las herramientas estándar que nos ofrece el sistema no incluyen ningún temporizador, que yo sepa. Todo lo más podemos usar sleep, que sufre el problema de acumulación de retraso. Hay maneras de reducir el impacto del retraso, como solicitar una espera ligeramente menor que el tiempo acordado, o ligeramente mayor, según veamos la evolución del sistema. Pero eso es más bien un parche que una solución y depende muchísimo de la precisión conque podamos especificar las esperas al sistema, y en el caso de sleep la precisión es de segundos. No nos sirve.

Lo que sí puede hacer el shell (tanto bash como tcsh, los dos que usamos) es "capturar" señales y responder a ellas. Claro que necesitamos primero algo que envíe esa señal y que además lo haga en el momento adecuado.

Hemos optado por escribir un pequeño programa en C para esta tarea:

#include <signal.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>

int parent;

void lanza(int a) {
kill(parent, SIGALRM);
}

int main() {
struct itimerval timer;

parent = getppid();
timer.it_value.tv_sec = 5;
timer.it_value.tv_usec = 0;
timer.it_interval.tv_sec = 5;
timer.it_interval.tv_usec = 0;
setitimer(ITIMER_REAL, &timer, NULL);

signal(SIGALRM, lanza);
while(1) {
pause();
}
}


Este programa es muy sencillote y no hace otra cosa que enviar una señal (ALRM, en este caso) cada 5 segundos. Si lo fuésemos a usar al final, probablemente lo modificaría para tomar el tiempo de espera como argumento. La función que usa el programa para establecer la temporización es setitimer, a la que hemos indicado como modo de funcionamiento ITIMER_REAL, ya que queremos que el "cronómetro" funcione en "tiempo real" (esto quiere decir "todo el tiempo", en lugar de, por ejemplo, sólo cuando el programa tiene el procesador).

La señal se repetirá cada 5 segundos mientras no especifiquemos lo contrario (y aquí tenemos un bucle infinito :P), siendo el único limitante la precisión interna del propio sistema operativo, cosa de la que no podemos escapar. En caso de que la ejecución se retrase un poco, no habrá acumulaciones.

El script que he usado para probarlo es éste:

#!/bin/bash

function printdate () {
date
}

trap printdate 14

./rt &

echo "Sleeping..."
while true
do
wait
done


"rt" es el nombre del programa en C y "14" es el número de la señal SIGALRM.

¿Alguna otra idea feliz?

2008-11-18 | 16:47 | Heimy | 15 Comentarios | #

Referencias (TrackBacks)

URL de trackback de esta historia http://quie.blogalia.com//trackbacks/60605

Comentarios

1
De: Heimy Fecha: 2008-11-18 16:49

Uh... Ahora falta que averigüe qué leche hice mal con la hoja de estilo para el código O_o



2
De: Heimy Fecha: 2008-11-18 16:59

Arreglao :)



3
De: Pablo Martínez Fecha: 2008-11-18 17:34

Me parece muy inteligente como lo has resuelto, supongo que la primera alternativa que se me ocurriría es usar crontab -e O:-) así que ahora mismo no habría sabido darte otra alternativa de solo shell scripting (usando otro lenguaje ya sí)



4
De: Chewie Fecha: 2008-11-18 17:39

if [ $(($(date +%s)%15)) -eq 0 ]; then echo "Ahorarl"; fi

¿No?



5
De: Heimy Fecha: 2008-11-18 17:50

@Pablo: el problema de crontab es que sólo te permite múltiplos de 1 minuto :), y cada ejecución es un script separado.

@Chewie: esa aproximación es interesante, pero tiene algunos problemas:

- tienes que hacer polling (2 veces por segundo, al menos)
- estás obligando a que las tareas se ejecuten en momentos concretos del minuto, y te limita a 60 segundos.
- no estás garantizando que la ejecución está lo más próxima posible al comienzo del segundo (estamos funcionando en edge trigger).
- la precisión sigue siendo, como mucho, de segundos.

El primer problema y el último son más bien molestias, pero no son deseables.



6
De: Chewie Fecha: 2008-11-18 18:49

No te limita a 60 segundos, %s te da los segundos desde el epoch :-)

Para todo lo demás, es subsanable, o suavizable, aumentando la precisión (y consecuentemente la frecuencia de muestreo), porque a date le puedes pedir hasta nanosegundos. ¡Es más guay todo en bash, no digas que no!



7
De: Chewie Fecha: 2008-11-18 18:51

Para lo de momentos concretos (múltiplos de 15 segundos desde el epoch), ¿no basta con hacer una suma?



8
De: Heimy Fecha: 2008-11-18 18:57

Chewie: ups, fallo técnico. No me acordaba de memoria lo que hace %s :) (y que le puedes subir la precisión).

De todas maneras, el hecho de tener que muestrear sigue jodiendo el invento :D. Si tienes que muestrear periodos más cortos, aumentas el gasto de CPU (inconveniente para la tarea en 2º plano).



9
De: Chewie Fecha: 2008-11-18 19:19

El hardware es barato X-D



10
De: Heimy Fecha: 2008-11-18 19:26

Chewie: mira, mira

18:14:18.504628258
18:14:23.504410061
18:14:28.504457589
18:14:33.504361045
18:14:38.504367733
18:14:43.504620897
18:14:48.504477268

Precisión hasta la milésima de segundo :D



11
De: Heimy Fecha: 2008-11-19 05:25

Chewie: Jur. se jodió lo del %N. Acabo de probarlo en la máquina destino... El SO es tan viejo que date no admite escupir la fecha con precisión menor al segundo.



12
De: up Fecha: 2011-05-26 17:26

ioo



13
De: up Fecha: 2011-05-26 17:26

qyuyweuyfiuyqgf
qhjfh



14
De: up Fecha: 2011-05-26 17:26

qyuyweuyfiuyqgf
qhjfh



15
De: gaby 26 Fecha: 2011-08-22 21:45

jhdtuidtuidtuidtuidtuidtuidtuidtuidtuidtuidtuidtuidtuidtuidtuidtuisr



Nombre
Correo-e
URL
Dirección IP: 54.80.236.48 (96222336e2)
Comentario

<Octubre 2017
Lu Ma Mi Ju Vi Sa Do
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
29 30 31        
Blogalia
Blogalia