Quiero ser interrumpido en frecuencias que son potencias de diez, por lo que habilitar interrupciones desde/dev/rtc no es ideal. Me gustaría dormir 1 milisegundo o 250 microsegundos entre interrupciones.¿Cómo obtengo las interrupciones periódicas más precisas en tiempo real en Linux?
Habilitar las interrupciones periódicas de/dev/hpet funciona bastante bien, pero parece que no funciona en algunas máquinas. Obviamente no puedo usarlo en máquinas que en realidad no tienen un HPET. Pero no puedo hacer que funcione en algunas máquinas que también tienen acceso como fuente de reloj. Por ejemplo, en un Core 2 Quad, el programa de ejemplo incluido en la documentación del kernel falla en HPET_IE_ON cuando se configura para sondear.
Sería mejor utilizar la interfaz itimer proporcionada por Linux en lugar de interactuar directamente con el controlador del dispositivo de hardware. Y en algunos sistemas, el temporizador proporciona interrupciones periódicas que son más estables a lo largo del tiempo. Es decir, dado que el hpet no puede interrumpir exactamente a la frecuencia que deseo, las interrupciones comienzan a desviarse del tiempo de la pared. Pero veo que algunos sistemas duermen mucho más tiempo (más de 10 milisegundos) de lo que deberían con un temporizador.
Aquí hay un programa de prueba que usa itimer para las interrupciones. En algunos sistemas, solo imprimirá una advertencia de que durmió aproximadamente 100 microsegundos más o menos durante el tiempo objetivo. En otros, imprimirá lotes de advertencia de que durmió 10+ milisegundos durante el tiempo objetivo. Compilar con -lrt y correr con chrt sudo -f 50 [nombre]
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <error.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <time.h>
#include <signal.h>
#include <fcntl.h>
#define NS_PER_SECOND 1000000000LL
#define TIMESPEC_TO_NS(aTime) ((NS_PER_SECOND * ((long long int) aTime.tv_sec)) \
+ aTime.tv_nsec)
int main()
{
// Block alarm signal, will be waited on explicitly
sigset_t lAlarm;
sigemptyset(&lAlarm);
sigaddset(&lAlarm, SIGALRM );
sigprocmask(SIG_BLOCK, &lAlarm, NULL);
// Set up periodic interrupt timer
struct itimerval lTimer;
int lReceivedSignal = 0;
lTimer.it_value.tv_sec = 0;
lTimer.it_value.tv_usec = 250;
lTimer.it_interval = lTimer.it_value;
// Start timer
if (setitimer(ITIMER_REAL, &lTimer, NULL) != 0)
{
error(EXIT_FAILURE, errno, "Could not start interval timer");
}
struct timespec lLastTime;
struct timespec lCurrentTime;
clock_gettime(CLOCK_REALTIME, &lLastTime);
while (1)
{
//Periodic wait
if (sigwait(&lAlarm, &lReceivedSignal) != 0)
{
error(EXIT_FAILURE, errno, "Failed to wait for next clock tick");
}
clock_gettime(CLOCK_REALTIME, &lCurrentTime);
long long int lDifference =
(TIMESPEC_TO_NS(lCurrentTime) - TIMESPEC_TO_NS(lLastTime));
if (lDifference > 300000)
{
fprintf(stderr, "Waited too long: %lld\n", lDifference );
}
lLastTime = lCurrentTime;
}
return 0;
}
Esto puede ser un error del kernel. Mi ejemplo de itimer parece funcionar bien en todas las máquinas que usan 2.6.32 pero no en 2.6.35 o 2.6.38. – Matt