Pregunta

Necesito un tick de temporizador con resolución de 1 ms bajo linux. Se utiliza para incrementar un valor de temporizador que, a su vez, se utiliza para ver si se deben activar varios eventos. 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 del manual.

La única forma de hacer este temporizador que actualmente se me ocurre es usar clock_gettime con CLOCK_MONOTONIC en mi bucle 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 antiguo c

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

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

Cómo lo resolví Dado que no es tan importante, simplemente declaré que el temporizador global tiene una resolución de 100 ms. Todos los eventos que utilizan su propio temporizador tienen que configurar al menos 100 ms para la caducidad 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 de freespace se describe mejor por qué no es realmente posible sin un sistema Linux en tiempo real.

¿Fue útil?

Solución

El sondeo en el bucle principal tampoco es una respuesta: es posible que el 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 convierte en discutible.

10ms es sobre la resolución de temporizador estándar para la mayoría de los sistemas operativos en tiempo real (RTOS ). Pero es discutible en un no RTOS: el comportamiento del programador y el despachador influirá enormemente en la rapidez con la que puede responder a un temporizador que expira. Por ejemplo, incluso suponiendo que tenía un temporizador de resolución de 10ms por debajo, no puede responder 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 de Linux en tiempo real, consulte http://www.linuxdevices.com/articles /AT8073314981.html para obtener una lista. Un RTOS ofrece instalaciones mediante las cuales puede obtener garantías flexibles 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.

Otros consejos

Para obtener un temporizador de resolución de 1 ms haga lo que libevent .

Organice sus temporizadores en un min-heap , es decir, la parte superior del montón es el temporizador con el primer tiempo de caducidad (absoluto) (un rb-tree también funcionaría pero con más sobrecarga). Antes de llamar a select () o epoll () en su bucle de eventos principal, calcule el delta en milisegundos entre el tiempo de caducidad del temporizador más antiguo y ahora. Utilice este delta como tiempo de espera para select () . Los tiempos de espera de 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 caducidad real de los 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 el kernel 2.6.34 de Fedora 13, la mejor precisión alcanzada de 1 ms en el temporizador fue avg = 22nsec stddev = 29410nsec.

No estoy seguro de que sea la mejor solución, pero podría considerar escribir un pequeño módulo del kernel que use los temporizadores de alta resolución del kernel para realizar la sincronización. Básicamente, crearías un archivo de dispositivo para el que las lecturas solo se devolverían en intervalos de 1 ms.

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

Creo que tendrá problemas para lograr una precisión de 1 ms con Linux estándar, incluso con consultas constantes en el bucle principal, porque el kernel no garantiza que su aplicación obtendrá CPU todo el tiempo. Por ejemplo, puedes dormirte durante docenas de milisegundos debido a la multitarea preventiva y hay poco que puedas hacer al respecto.

Es posible que desee consultar Linux en tiempo real .

Si está apuntando a la plataforma x86, debe verificar los temporizadores HPET. Este es el temporizador de hardware con gran precisión. Debe estar respaldado por su motherbord (en este momento todos ellos lo admiten) y su kernel también debe contener el controlador para ello. Lo he usado varias veces sin ningún problema y pude lograr una resolución mucho mejor que 1 ms.

Aquí hay algunos documentos y ejemplos:

Me parece recordar que obtuve buenos resultados con el sondeo basado en gettimeofday / usleep: no necesitaba 1000 temporizadores por segundo ni nada, pero necesitaba una buena precisión con el calendario para los ticks que necesitaba. Mi aplicación era una Controlador MIDI de la caja de ritmos, y me parece recordar que obtuve una precisión de menos de milisegundos, que necesitas para una caja de ritmos si no quieres que suene como un baterista muy malo (especialmente si contamos las latencias incorporadas de MIDI) - iirc (Era 2005, por lo que mi memoria está un poco borrosa) me estaba quedando dentro de los 200 microsegundos de los tiempos objetivo cuando dormíamos.

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 en el sistema (ver cómo cron se inicia updatedb, etc.), entonces las cosas pueden desmoronarse.

¿Está ejecutando en un kernel de Linux 2.4?

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

  

Los sistemas operativos invitados de Linux mantienen   tiempo contando las interrupciones del temporizador.   Unpatched 2.4 y kernels anteriores   programar el temporizador del sistema virtual para   pedir interrupciones de reloj a 100Hz (100   interrupciones por segundo). 2.6 granos,   Por otro lado, solicitar interrupciones.   a 1000Hz - diez veces más seguido. Algunos   2.4 kernels modificados por los proveedores de distribución para que contengan 2.6 características también   solicitar interrupciones de 1000Hz, o en algunos   casos, interrupciones a otras tarifas, tales   como 512Hz.

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 limitada a 100 o 200.

Para un buen funcionamiento, la opción PREEMPT_KERNEL debe estar activada. Existen Kernels que no soportan esta opción adecuadamente. Puedes verlos por buscando.

Los núcleos recientes, es decir, 2.6.35.10, admiten las opciones NO_HZ, que en las garrapatas dinámicas. Esto significa que no habrá tics del temporizador cuando esté inactivo, pero se generará una marca de temporizador en el momento especificado.

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

En general, RTAI es una solución totalmente asesina para su problema, pero su El soporte de hardware es muy limitado. Sin embargo, buenos controladores CNC, como emc2, use RTAI para su reloj, tal vez 5000 Hz, pero puede ser trabajo duro para instalarlo.

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

¿Puedes al menos usar nanosleep en tu bucle para dormir por 1 ms? ¿O es eso una cosa genial?

Actualización: No importa, lo veo en la página de manual " puede llevar hasta 10 ms más de lo especificado hasta que el proceso vuelva a ejecutarse "

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

Hay dos enfoques que puedes tomar con esto:

1) Su aplicación SOLO está ejecutando su máquina de estado, y nada más. Linux es simplemente su " cargador de arranque. & Quot; Crea un objeto del núcleo que instala un dispositivo de caracteres. Al insertarlo en el kernel, configure su GP Timer para que se ejecute continuamente. Usted sabe la frecuencia con la que opera. Ahora, en el kernel, deshabilita explícitamente tu perro guardián. Ahora deshabilite las interrupciones (hardware Y software) En un kernel de Linux de una sola CPU, llamar a spin_lock () lo logrará (nunca lo suelte). La CPU es SUYA. Bucle ocupado, verificando el valor del GPT hasta que haya pasado el número requerido de tics, cuando lo hayan hecho, establezca un valor para el siguiente tiempo de espera e ingrese su ciclo de procesamiento. Solo asegúrese de que el tiempo de ráfaga de su código sea inferior a 1 ms

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 transcurra el tiempo de espera de 1 ms (por ejemplo, 50-75 uSeg.) Cuando se dispara la interrupción, deshabilitará inmediatamente las interrupciones y girará a la espera de que se produzca la ventana de 1 ms. habilitando interrupciones en su espera OUT. Esto explica el hecho de que está cooperando con OTRAS cosas en el kernel que deshabilitan las interrupciones. Esto ASUME que no hay ninguna 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 agrandar la ventana hasta que satisfaga sus necesidades.

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

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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top