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.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Ich habe das gleiche problem mit einem nackten setitimer() setup.
Das problem ist, dass Ihr Prozess wird eingeplant, indem die SCHED_OTHER Politik auf statische Priorität 0 ist Standard. Das heißt, Sie sind in einem pool mit allen anderen Prozessen und dynamischen Prioritäten entscheiden. Im moment gibt es einige system-load, Sie zu bekommen-Wartezeiten.
Die Lösung ist die Verwendung der sched_setscheduler() system call, erhöhen Sie Ihre statische Priorität, um mindestens ein, und geben Sie SCHED_FIFO Politik. Es bewirkt eine dramatische Verbesserung.
Müssen Sie als root ausführen, um in der Lage sein, dies zu tun. Die alternative ist die Verwendung der chrt-Programm, das gleiche zu tun, aber Sie müssen wissen, die PID Ihres RT-Prozess.
Siehe mein blog-post darüber hier.
Unabhängig von der timing-Mechanismus, die Sie verwenden, läuft es auf eine Kombination von Veränderung in Ihrer Aufgabe führen Sie status, wenn der kernel scheduler aufgerufen wird (in der Regel 100 oder 1000 mal pro Sekunde), und die cpu-Konflikte bestehen mit anderen Prozessen.
Den Mechanismus habe ich gefunden, dass erreicht der "beste" timing auf Linux (Windows) ist Folgendes zu tun:
Es dauert einige Arbeit, aber Sie kann sehr gute Ergebnisse mit diesem Ansatz. Eine Verwandte Frage, möchten Sie vielleicht werfen Sie einen Blick auf finden hier.