c linux timer

Temporizador de resolución de 1 ms bajo la forma recomendada de Linux



timer (12)

Necesito un ticker de temporizador con una resolución de 1 ms en Linux. Se usa para incrementar un valor de temporizador que a su vez se usa para ver si se deben activar varios eventos. El POSIX timerfd_create no es una opción debido al requisito de glibc. Intenté timer_create y timer_settimer, pero lo mejor que obtengo de ellos es una resolución de 10 ms, los valores más pequeños parecen tener una resolución predeterminada de 10 ms. Getittimer y setitimer tienen una resolución de 10 ms según la página de manual.

La única forma de hacer este temporizador en el que puedo pensar actualmente es utilizar clock_gettime con CLOCK_MONOTONIC en mi loop principal una prueba si ha pasado un ms, y si es así aumentar el contador (y luego verificar si los diversos eventos deberían dispararse).

¿Hay una mejor manera de hacer esto que consultar constantemente en el ciclo principal? ¿Cuál es la solución recomendada para esto?

El lenguaje que estoy usando es simple viejo c

Actualizar
Estoy usando un Kernel 2.6.26. Sé que puede hacer que lo interrumpa a 1kHz, y las funciones POSIX timer_ * pueden programarse hasta 1ms pero eso no parece ser confiable y no quiero usar eso, porque puede necesitar un kernel nuevo en algunos Sistemas. Algunos valores de Kernel parecen tener configurados los 100Hz. Y necesitaría detectar eso. La aplicación puede ejecutarse en otra cosa que no sea mi Sistema :)

No puedo dormir por 1 ms porque puede haber eventos de red a los que tengo que reaccionar.

Cómo lo resolví. Dado que no es tan importante, simplemente declare que el temporizador global tiene una resolución de 100 ms. Todos los eventos que usan su propio temporizador tienen que establecer al menos 100 ms para la expiración del temporizador. Me preguntaba más o menos si habría una mejor manera, de ahí la pregunta.

¿Por qué acepté la respuesta ? Creo que la respuesta del espacio libre mejor describió por qué no es realmente posible sin un sistema Linux en tiempo real.


¿Estás ejecutando en un núcleo Linux 2.4?

Del artículo # 1420 de VMware KB ( http://kb.vmware.com/kb/1420 ).

Los sistemas operativos invitados Linux mantienen el tiempo contando las interrupciones del temporizador. Los kernels sin parchar 2.4 y anteriores programan el temporizador del sistema virtual para solicitar interrupciones de reloj a 100Hz (100 interrupciones por segundo). 2.6 núcleos, por otro lado, solicitan interrupciones a 1000 Hz, diez veces más. Algunos kernels 2.4 modificados por proveedores de distribución para contener funciones 2.6 también solicitan interrupciones de 1000Hz o, en algunos casos, interrupciones a otras velocidades, como 512Hz.


¿Puedes al menos usar nanosleep en tu loop para dormir por 1ms? ¿O es eso algo glibc?

Actualización: No importa, veo desde la página del manual "puede tomar hasta 10 ms más de lo especificado hasta que el proceso vuelva a ser ejecutable"


Creo que tendrás problemas para lograr una precisión de 1 ms con Linux estándar incluso con consultas constantes en el ciclo principal, porque el kernel no garantiza que tu aplicación obtenga CPU todo el tiempo. Por ejemplo, puede quedarse dormido durante decenas de milisegundos debido a la multitarea preventiva y hay poco que puede hacer al respecto.

Es posible que desee examinar Linux en tiempo real .



No estoy seguro de que sea la mejor solución, pero podría considerar escribir un pequeño módulo de kernel que use los temporizadores de alta resolución del núcleo para hacer el cronometraje. Básicamente, crearía un archivo de dispositivo cuyas lecturas solo regresarían en intervalos de 1 ms.

Un ejemplo de este tipo de enfoque se utiliza en Asterisk PBX, a través del módulo ztdummy. Si buscas google para ztdummy, puedes encontrar el código que hace esto.


Parece que recuerdo haber obtenido buenos resultados con las encuestas basadas en gettimeofday / usleep - No necesitaba 1000 temporizadores por segundo ni nada por el estilo, pero necesitaba una buena precisión con los tiempos que necesitaba - mi aplicación era una caja de ritmos MIDI controlador, y parece que recuerdo obtener una precisión de menos de milisegundos, que necesita para una caja de ritmos si no quiere que suene como un baterista muy malo (especialmente contar las latencias incorporadas de MIDI) - iirc (era 2005 por lo que mi memoria es un poco confusa) Me estaba llegando a 200 microsegundos de los tiempos objetivo con el sueño.

Sin embargo, no estaba ejecutando mucho más en el sistema. Si tiene un entorno controlado, es posible que pueda salirse con una solución como esa. Si hay más cosas sucediendo en el sistema (ver cron disparando actualizadob, etc.) entonces las cosas pueden desmoronarse.


El sondeo en el bucle principal tampoco es una respuesta: es posible que su proceso no obtenga mucho tiempo de CPU, por lo que transcurrirán más de 10 ms antes de que se ejecute el código, lo que lo hace irrelevante.

10 ms se trata de la resolución de temporizador estándar para la mayoría de los sistemas operativos que no son en tiempo real (RTOS). Pero es irrelevante en un entorno que no sea RTOS: el comportamiento del programador y el despachador influirá en gran medida en la rapidez con la que puede responder al vencimiento de un temporizador. Por ejemplo, supongamos que tiene un temporizador de resolución inferior a 10 ms, pero no puede responder al vencimiento del temporizador si el código no se está ejecutando. Como no puede predecir cuándo se ejecutará su código, no puede responder con precisión al vencimiento del temporizador.

Por supuesto, hay núcleos Linux en tiempo real, consulte http://www.linuxdevices.com/articles/AT8073314981.html para obtener una lista. A RTOS ofrece instalaciones en las que puede obtener garantías suaves o duras sobre cuándo se ejecutará su código. Esta es la única forma de responder de manera confiable y precisa a los temporizadores que expiran, etc.


Para obtener 1 ms de resolución, los temporizadores hacen lo que libevent hace.

Organice sus temporizadores en un montón mínimo , es decir, la parte superior del montón es el temporizador con el tiempo de expiración (absoluto) más temprano (un árbol-rb también funcionaría pero con más sobrecarga). Antes de llamar a select() o epoll() en su bucle de evento principal, calcule el delta en milisegundos entre el tiempo de caducidad del primer temporizador y ahora. Use este delta como el tiempo de espera para select() . select() tiempos de espera select() y epoll() tienen una resolución de 1 ms.

Tengo una prueba de resolución de temporizador que utiliza el mecanismo explicado anteriormente (pero no libevent). La prueba mide la diferencia entre el tiempo de caducidad del temporizador deseado y su vencimiento real de temporizadores de 1 ms, 5 ms y 10 ms:

1000 deviation samples of 1msec timer: min= -246115nsec max= 1143471nsec median= -70775nsec avg= 901nsec stddev= 45570nsec 1000 deviation samples of 5msec timer: min= -265280nsec max= 256260nsec median= -252363nsec avg= -195nsec stddev= 30933nsec 1000 deviation samples of 10msec timer: min= -273119nsec max= 274045nsec median= 103471nsec avg= -179nsec stddev= 31228nsec 1000 deviation samples of 1msec timer: min= -144930nsec max= 1052379nsec median= -109322nsec avg= 1000nsec stddev= 43545nsec 1000 deviation samples of 5msec timer: min= -1229446nsec max= 1230399nsec median= 1222761nsec avg= 724nsec stddev= 254466nsec 1000 deviation samples of 10msec timer: min= -1227580nsec max= 1227734nsec median= 47328nsec avg= 745nsec stddev= 173834nsec 1000 deviation samples of 1msec timer: min= -222672nsec max= 228907nsec median= 63635nsec avg= 22nsec stddev= 29410nsec 1000 deviation samples of 5msec timer: min= -1302808nsec max= 1270006nsec median= 1251949nsec avg= -222nsec stddev= 345944nsec 1000 deviation samples of 10msec timer: min= -1297724nsec max= 1298269nsec median= 1254351nsec avg= -225nsec stddev= 374717nsec

La prueba se ejecutó como un proceso en tiempo real en Fedora 13 kernel 2.6.34, la precisión mejor lograda del temporizador de 1ms fue avg = 22nsec stddev = 29410nsec.


Si tiene como objetivo la plataforma x86, debe verificar los temporizadores HPET. Este es un temporizador de hardware con gran precisión. Debe ser compatible con su motherbord (en este momento todos ellos lo admiten) y su núcleo también debe contener un controlador. Lo he usado pocas veces sin ningún problema y pude lograr una resolución mucho mejor que 1ms.

Aquí hay algunos documentos y ejemplos:


Primero, obtenga la fuente del kernel y compílela con un parámetro HZ ajustado.

  • Si HZ=1000 , el temporizador interrumpe 1000 veces por segundo. Está bien usar HZ=1000 para una máquina i386.
  • En una máquina integrada, HZ podría estar limitado a 100 o 200.

Para un buen funcionamiento, la opción PREEMPT_KERNEL debe estar activada. Hay núcleos que no admiten esta opción correctamente. Puede consultarlos buscando.

Los kernels recientes, es decir 2.6.35.10, son compatibles con las opciones NO_HZ, que activan tics dinámicos. Esto significa que no habrá tics del temporizador cuando esté inactivo, pero se generará un tic del temporizador en el momento especificado.

Hay un parche RT para el kernel, pero el soporte de hardware es muy limitado.

En general, RTAI es una solución excelente para su problema, pero su soporte de hardware es muy limitado. Sin embargo, los buenos controladores CNC, como emc2, usan RTAI para su sincronización, tal vez 5000 Hz, pero puede ser un trabajo difícil de instalar.

Si puede, puede agregar hardware para generar pulsos. Eso haría un sistema que se puede adaptar a cualquier versión del sistema operativo.


No necesita un RTOS para una aplicación simple en tiempo real. Todos los procesadores modernos tienen temporizadores de propósito general. Obtenga una hoja de datos para cualquier CPU objetivo en la que esté trabajando. Busque en la fuente del kernel, debajo del directorio arch encontrará la fuente específica del procesador cómo manejar estos temporizadores.

Hay dos enfoques que puede tomar con esto:

1) Su aplicación SÓLO está ejecutando su máquina de estado, y nada más. Linux es simplemente tu "gestor de arranque". Crea un objeto kernel que instala un dispositivo de caracteres. Al insertarlo en el kernel, configure su GP Timer para que se ejecute continuamente. Usted sabe la frecuencia a la que está operando. Ahora, en el kernel, deshabilita explícitamente tu perro guardián. Ahora deshabilite las interrupciones (hardware Y software) En un kernel Linux de una sola CPU, llamar a spin_lock () lo logrará (nunca lo suelte). La CPU es SUYA. Busy loop, verificando el valor de GPT hasta que haya pasado el número requerido de ticks, cuando lo hayan hecho, establezca un valor para el próximo tiempo de espera e ingrese su ciclo de procesamiento. Solo asegúrate de que el tiempo de ráfaga para tu código sea menor a 1ms

2) Una segunda opción. Esto supone que está ejecutando un kernel de Linux preventivo. Configure un GPT no utilizado junto con su sistema operativo en ejecución. Ahora configure una interrupción para disparar un margen configurable ANTES de que su tiempo de espera de 1 ms pase (digamos 50-75 uSec.) Cuando la interrupción se dispare, inmediatamente deshabilitará las interrupciones y girará esperando a que ocurra la ventana de 1 ms, luego ingresará su máquina de estado y posteriormente habilitar interrupciones en su esperar FUERA. Esto explica el hecho de que está cooperando con OTRAS cosas en el núcleo que desactivan las interrupciones. Esto ASUME que no hay otra actividad del kernel que bloquee las interrupciones durante mucho tiempo (más de 100us). Ahora, puede MEDIR la precisión de su evento de disparo y aumentar la ventana hasta que cumpla con sus necesidades.

Si, en cambio, intentas aprender cómo funciona RTOS ... o si estás tratando de resolver un problema de control con más de una responsabilidad en tiempo real ... entonces usa un RTOS.


¿Qué hay de usar el dispositivo "/ dev / rtc0" (o "/ dev / rtc") y su interfaz ioctl () relacionada? Creo que ofrece un contador de tiempo preciso. No es posible configurar la velocidad solo a 1 ms, pero a un valor cercano o 1 / 1024seg (1024Hz), o a una frecuencia más alta, como 8192Hz.