2010-06-26 11 views
6

Tengo un sistema que necesita al menos 10 mseconds de precisión para los temporizadores.
Fui por timerfd ya que me conviene perfectamente, pero encontré que incluso por un tiempo de hasta 15 milisegundos no es exacto en absoluto, o eso o no entiendo cómo funciona.Linux, precisión timerfd

Los tiempos que he medido fueron de hasta 21 mseconds en un temporizador de 10 mseconds.
He preparado una prueba rápida que muestra mi problema.
aquí una prueba:

#include <sys/timerfd.h> 
#include <time.h> 
#include <string.h> 
#include <stdint.h> 

int main(int argc, char *argv[]){ 

    int timerfd = timerfd_create(CLOCK_MONOTONIC,0); 
    int milliseconds = atoi(argv[1]); 
    struct itimerspec timspec; 
    bzero(&timspec, sizeof(timspec)); 
    timspec.it_interval.tv_sec = 0; 
    timspec.it_interval.tv_nsec = milliseconds * 1000000; 
    timspec.it_value.tv_sec = 0; 
    timspec.it_value.tv_nsec = 1; 

    int res = timerfd_settime(timerfd, 0, &timspec, 0); 
    if(res < 0){ 
     perror("timerfd_settime:"); 
    } 
    uint64_t expirations = 0; 
    int iterations = 0; 
    while(res = read(timerfd, &expirations, sizeof(expirations))){ 
     if(res < 0){ perror("read:"); continue; } 
     if(expirations > 1){ 
      printf("%lld expirations, %d iterations\n", expirations, iterations); 
      break; 
     } 
     iterations++; 
    } 
} 

y ejecutado como esto:

Zack ~$ for i in 2 4 8 10 15; do echo "intervals of $i milliseconds"; ./test $i;done 
intervals of 2 milliseconds 
2 expirations, 1 iterations 
intervals of 4 milliseconds 
2 expirations, 6381 iterations 
intervals of 8 milliseconds 
2 expirations, 21764 iterations 
intervals of 10 milliseconds 
2 expirations, 1089 iterations 
intervals of 15 milliseconds 
2 expirations, 3085 iterations 

Aun suponiendo algunos retrasos posibles, a 15 milisegundos retrasos suena demasiado para mí.

+4

Mis datos sobre la zona son bastante oxidados. Suena que quieres hacer algo en tiempo real. Verifique Linuxes en tiempo real. De lo contrario, el programador utiliza granularidades más largas para evitar una sobrecarga superior. Es posible que desee ejecutar su proceso como RT (en tiempo real) uno ('man sched_setparam') y probablemente en la raíz. Para procesos normales (por ejemplo, juegos o multimedia), usted (1) espera en circuito cerrado o (2) en cada evento de temporizador calcule el error (esperado frente a tiempo de activación real) y lo incorpora a la cuenta cuando avanza el tiempo interno estado dependiente. – Dummy00001

+0

Gracias por la sugerencia, pero no considero intervalos de 20 milisegundos como parte del dominio en tiempo real. Quiero decir, un sistema estándar al que se le pide que espere 10 milisegundos nunca debe esperar 21 milisegundos, la RT suena demasiado para eso. –

+0

puede intentar configurar su programa para que se ejecute con prioridad en tiempo real, probablemente no necesite usar el kernel de RT Linux. La línea principal es bastante buena en tiempo real en estos días, y dado que nunca se perdió más de una expiración simplemente ejecutándose con prioridad RT debería ser suficiente para que nunca se pierda uno – Spudd86

Respuesta

12

Intente modificarlo de la siguiente manera, esto debería suponer que nunca va a perder una activación, pero tenga cuidado con ella, ya que ejecutar la prioridad en tiempo real puede bloquear su máquina si no se detiene, también es posible que necesite preparar las cosas para que su usuario tiene la capacidad de ejecutar cosas en prioridad de tiempo real (ver /etc/security/limits.conf)

#include <sys/timerfd.h> 
#include <time.h> 
#include <string.h> 
#include <stdint.h> 
#include <stdio.h> 
#include <sched.h> 

int main(int argc, char *argv[]) 
{ 
    int timerfd = timerfd_create(CLOCK_MONOTONIC,0); 
    int milliseconds = atoi(argv[1]); 
    struct itimerspec timspec; 
    struct sched_param schedparm; 

    memset(&schedparm, 0, sizeof(schedparm)); 
    schedparm.sched_priority = 1; // lowest rt priority 
    sched_setscheduler(0, SCHED_FIFO, &schedparm); 

    bzero(&timspec, sizeof(timspec)); 
    timspec.it_interval.tv_sec = 0; 
    timspec.it_interval.tv_nsec = milliseconds * 1000000; 
    timspec.it_value.tv_sec = 0; 
    timspec.it_value.tv_nsec = 1; 

    int res = timerfd_settime(timerfd, 0, &timspec, 0); 
    if(res < 0){ 
     perror("timerfd_settime:"); 
    } 
    uint64_t expirations = 0; 
    int iterations = 0; 
    while(res = read(timerfd, &expirations, sizeof(expirations))){ 
     if(res < 0){ perror("read:"); continue; } 
     if(expirations > 1){ 
      printf("%ld expirations, %d iterations\n", expirations, iterations); 
      break; 
     } 
     iterations++; 
    } 
} 

Si está utilizando hilos se debe utilizar en lugar de pthread_setschedparamsched_setscheduler.

En tiempo real también no se trata de latencia baja, se trata de garantías, RT significa que si desea despertar exactamente una vez por segundo en el segundo, usted, la programación normal no le da esto, podría decidir te despertaría 100ms más tarde, porque de todos modos tenía otro trabajo que hacer en ese momento. Si quiere despertarse cada 10ms y REALMENTE lo necesita, entonces debe configurarlo para ejecutarlo como una tarea en tiempo real, entonces el núcleo lo despertará cada 10ms sin falta.A menos que una tarea en tiempo real de mayor prioridad esté ocupada haciendo cosas.

Si necesita garantizar que su intervalo de activación es exactamente cierto tiempo, no importa si es 1ms o 1 segundo, no lo obtendrá a menos que ejecute como una tarea en tiempo real. Hay buenas razones para que el kernel le haga esto (el poder de ahorro es uno de ellos, el mayor rendimiento es otro, hay otros), pero está en su derecho hacerlo ya que nunca dijo que necesita mejores garantías. La mayoría de las cosas en realidad no tienen que ser tan precisas, o no deben perderse nunca, por lo que debes pensar detenidamente si realmente las necesitas o no.

citando http://www.ganssle.com/articles/realtime.htm

Una tarea en tiempo real duro o el sistema es uno donde una actividad simplemente debe ser completado - siempre - por un plazo especificado. La fecha límite puede ser en un intervalo de tiempo o tiempo particular, o puede ser la llegada de algún evento. Duro tareas en tiempo real fallan, por definición, si se pierden dicho plazo.

Tenga en cuenta esta definición no hace suposiciones sobre la frecuencia o período de las tareas. Un microsegundo o por semana - si falta la fecha límite induce un fallo, entonces la tarea tiene requisitos de tiempo real.

tiempo real blando es más o menos la misma, excepto que no cumplir un plazo, mientras no deseable, no es el fin del mundo (por ejemplo, vídeo y reproducción de audio en tiempo real son tareas suaves, que no quiere perder la visualización un marco, o se queda sin buffer, pero si lo haces es solo un momento momentáneo, y simplemente continúas). Si lo que estás tratando de hacer es 'suave' en tiempo real, no me molestaría en ejecutar en tiempo real la prioridad, ya que generalmente debes tener tus despertadores a tiempo (o al menos cerca de eso).

EDIT:

Si no se está ejecutando en tiempo real el kernel por defecto dar ningún temporizador que realice algún 'holgura' para que se pueda fusionar su solicitud para despertar con otros eventos que suceden en momentos cercanos a el que usted solicitó (es decir, si el otro evento está dentro de su 'tiempo de inactividad' no lo despertará en el momento en que lo solicitó, pero un poco antes o después, al mismo tiempo ya iba a hacer otra cosa, esto ahorra energía).

Por un poco más información ver High- (but not too high-) resolution timeouts y Timer slack (tenga en cuenta que no estoy seguro si cualquiera de esas cosas es exactamente lo que realmente está en el núcleo, ya que ambos esos artículos son sobre las discusiones de listas de correo lkml, sino algo así como la primera realidad está en el kernel

0

Dependiendo de qué más esté haciendo el sistema, puede ser un poco lento para volver a su tarea. A menos que tenga un sistema "real" en tiempo real, no hay garantía de que vaya a funcionar mejor de lo que está viendo, aunque estoy de acuerdo en que el resultado es un poco decepcionante.

Puede (en su mayoría) eliminar el tiempo de cambio de tarea/programador. Si tiene energía de CPU (¡y energía eléctrica!) De sobra, una solución brutal pero efectiva sería un ciclo de espera agitado.

La idea es ejecutar su programa en un circuito cerrado que continuamente sondee el reloj por la hora que sea, y luego llame a su otro código cuando sea el momento adecuado. A expensas de hacer que su sistema actúe muy lento para todo lo demás y de calentar su CPU, terminará con una programación de tareas que en su mayoría no tiene jitter.

Escribí un sistema como este una vez en Windows XP para hacer girar un motor paso a paso, suministrando pulsos espaciados uniformemente hasta 40 K veces por segundo, y funcionó bien. Por supuesto, su kilometraje puede variar.

+0

¿Por qué no acaba de obtener un micro barato como un PIC o Amtel para hacer el trabajo de campo? Hoy en día usaría un Ardiuno para hacer el control de pasos y usar la conexión USB para enviar señales más rápidas/lentas/de inicio/detención. – Emyr

1

Aquí hay una teoría. Si HZ está configurado en 250 para su sistema (como es típico), entonces tiene una resolución de temporizador de 4 milisegundos. Una vez que el planificador haya cambiado el proceso, es probable que se programen y se ejecuten otros procesos antes de que su proceso reciba otro segmento de tiempo. Esto podría explicar que vea resoluciones del temporizador en el rango de 15 a 21 milisegundos. La única forma de evitar esto sería ejecutar un kernel en tiempo real.

La solución típica para la sincronización de alta resolución en sistemas que no son en tiempo real es básicamente esperar ocupado con una llamada para seleccionar.

+0

No creo que muchas distribuciones orientadas a computadoras de escritorio establezcan HZ a 250, y la mayoría de ellas en estos días activan la operación sin marca, por lo que HZ no debería importar mucho para el tiempo. La mayoría de las configuraciones en estos días debería ser capaz de obtener granularidad en milisegundos sin un kernel en tiempo real fácilmente, y no necesita los parches de RT Linux a menos que su carga en tiempo real sea muy estrecha ... – Spudd86

+1

Mainline Linux ES un sistema en tiempo real días, es que no mucha gente lo sabe: P (bueno PUEDE ser si escribes tu código para aprovechar las características del realitme) – Spudd86

3

Tengo la sensación de que su prueba depende mucho del hardware. Cuando ejecuté su programa de ejemplo en mi sistema, pareció colgarse a 1 ms. Para que su prueba sea significativa en mi computadora , Tuve que cambiar de milisegundos a microsegundos. (Cambié el multiplicador de 1_000_000 a 1_000).

 
$ grep 1000 test.c 
    timspec.it_interval.tv_nsec = microseconds * 1000; 
 
$ for i in 1 2 4 5 7 8 9 15 16 17\ 
31 32 33 47 48 49 63 64 65 ; do\ 
echo "intervals of $i microseconds";\ 
./test $i;done 
intervals of 1 microseconds 
11 expirations, 0 iterations 
intervals of 2 microseconds 
5 expirations, 0 iterations 
intervals of 4 microseconds 
3 expirations, 0 iterations 
intervals of 5 microseconds 
2 expirations, 0 iterations 
intervals of 7 microseconds 
2 expirations, 0 iterations 
intervals of 8 microseconds 
2 expirations, 0 iterations 
intervals of 9 microseconds 
2 expirations, 0 iterations 
intervals of 15 microseconds 
2 expirations, 7788 iterations 
intervals of 16 microseconds 
4 expirations, 1646767 iterations 
intervals of 17 microseconds 
2 expirations, 597 iterations 
intervals of 31 microseconds 
2 expirations, 370969 iterations 
intervals of 32 microseconds 
2 expirations, 163167 iterations 
intervals of 33 microseconds 
2 expirations, 3267 iterations 
intervals of 47 microseconds 
2 expirations, 1913584 iterations 
intervals of 48 microseconds 
2 expirations, 31 iterations 
intervals of 49 microseconds 
2 expirations, 17852 iterations 
intervals of 63 microseconds 
2 expirations, 24 iterations 
intervals of 64 microseconds 
2 expirations, 2888 iterations 
intervals of 65 microseconds 
2 expirations, 37668 iterations 

(Algo interesante que obtuve las ejecuciones más largas de 16 y 47 microsegundos, pero 17 y 48 fueron terribles.)

tiempo (7) tiene algunas sugerencias sobre qué nuestras plataformas son tan diferentes:

 
    High-Resolution Timers 
     Before Linux 2.6.21, the accuracy of timer and sleep system 
     calls (see below) was also limited by the size of the jiffy. 

     Since Linux 2.6.21, Linux supports high-resolution timers 
     (HRTs), optionally configurable via CONFIG_HIGH_RES_TIMERS. On 
     a system that supports HRTs, the accuracy of sleep and timer 
     system calls is no longer constrained by the jiffy, but instead 
     can be as accurate as the hardware allows (microsecond accuracy 
     is typical of modern hardware). You can determine whether 
     high-resolution timers are supported by checking the resolution 
     returned by a call to clock_getres(2) or looking at the 
     "resolution" entries in /proc/timer_list. 

     HRTs are not supported on all hardware architectures. (Support 
     is provided on x86, arm, and powerpc, among others.) 

Todas las líneas 'resolución' en mi/proc/timer_list son 1ns en mi la verdad es ridículamente poderoso sistema x86_64() .

yo decidimos probar a averiguar dónde está el 'punto de ruptura' es en mi equipo, pero di por vencido en los 110 microsegundos de ejecución:

 
$ for i in 70 80 90 100 110 120 130\ 
; do echo "intervals of $i microseconds";\ 
./test $i;done 
intervals of 70 microseconds 
2 expirations, 639236 iterations 
intervals of 80 microseconds 
2 expirations, 150304 iterations 
intervals of 90 microseconds 
4 expirations, 3368248 iterations 
intervals of 100 microseconds 
4 expirations, 1964857 iterations 
intervals of 110 microseconds 
^C 

90 microsegundos corrieron por tres millones de iteraciones antes de que fallara unos pocos veces; esa es una resolución 22 veces mejor que tu primera prueba, así que diría que con el hardware adecuado, 10 ms no debería ser difícil. (90 microsegundos es una resolución 111 veces mejor que 10 milisegundos.)

Pero si su hardware no proporciona los temporizadores para los temporizadores de alta resolución, entonces Linux no puede ayudarlo sin recurrir a SCHED_RR o SCHED_FIFO. E incluso entonces, tal vez otro kernel podría proporcionarle la compatibilidad con el temporizador de software que necesita.

Buena suerte. :)

+1

sí, el kernel tiene hrtimers por lo que PUEDE despertarte dentro de 1ns de cuando lo pediste, el clima o no, WILL es una historia diferente, los temporizadores tienen cierta 'holgura' (si no se trata de una tarea en tiempo real) para que el núcleo pueda reducir el número de activaciones en total. (es decir, si ya tiene algo que ver dentro del período de inactividad, hará que se despierte al mismo tiempo que lo que vea http://lwn.net/Articles/296578/) – Spudd86