Wie bekomme ich die genauesten Echtzeit-periodische interrupts im Linux?

Ich soll unterbrochen werden kann, mit Frequenzen, die Vielfache von zehn, so aktivieren von interrupts aus /dev/rtc nicht ideal. Ich möchte schlafen 1 Millisekunde oder 250 Mikrosekunden zwischen den interrupts.

Ermöglicht periodische interrupts aus /dev/hpet funktioniert Recht gut, aber es scheint nicht zu funktionieren auf einigen Maschinen. Offensichtlich kann ich nicht verwenden Sie es auf Maschinen, die eigentlich nicht haben HPET. Aber ich kann nicht es funktioniert auf einigen Maschinen, die hpet als Clock-Quelle entweder. Zum Beispiel auf einem Core 2 Quad, die Beispiel-Programm enthalten in der kernel-Dokumentation nicht an HPET_IE_ON bei poll.

Wäre es schöner zu verwenden, die itimer-Schnittstelle zur Verfügung, die von Linux statt, die Verknüpfung mit der hardware-Gerätetreiber direkt. Und auf einigen Systemen, die itimer bietet periodische interrupts, die sind stabiler über die Zeit. Das ist, da die hpet nicht unterbrochen werden kann, um genau die Frequenz die ich will, die interrupts starten zu treiben, um von der Wand die Zeit. Aber ich sehe einige Systeme, die schlafen viel länger (10+ Millisekunden), als Sie sollte mit einem itimer.

Hier ist ein test-Programm mit itimer für interrupts. Auf einigen Systemen wird es nur drucken Sie eine Warnung, die es schlief etwa 100 Mikrosekunden oder so über der soll-Zeit. Auf der anderen, wird es drucken Sie Chargen der Warnung, dass es schlief 10+ Millisekunden über der soll-Zeit. Kompilieren mit -lrt und ausführen mit sudo chrt -f 50 [name]

#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;
}
  • Dies kann ein kernel-bug. Meine itimer Beispiel scheint gut zu funktionieren auf allen Rechnern, mit 2.6.32 aber nicht auf 2.6.35 oder 2.6.38.
InformationsquelleAutor Matt | 2011-04-29
Schreibe einen Kommentar