Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I get the most accurate realtime periodic interrupts in Linux?

I want to be interrupted at frequencies that are powers of ten, so enabling interrupts from /dev/rtc isn't ideal. I'd like to sleep 1 millisecond or 250 microseconds between interrupts.

Enabling periodic interrupts from /dev/hpet works pretty well, but it doesn't seem to work on some machines. Obviously I can't use it on machines that don't actually have a HPET. But I can't get it working on some machines that have hpet available as a clocksource either. For example, on a Core 2 Quad, the example program included in the kernel documentation fails at HPET_IE_ON when set to poll.

It would be nicer to use the itimer interface provided by Linux instead of interfacing with the hardware device driver directly. And on some systems, the itimer provides periodic interrupts that are more stable over time. That is, since the hpet can't interrupt at exactly the frequency I want, the interrupts start to drift from wall time. But I'm seeing some systems sleep way longer (10+ milliseconds) than they should using an itimer.

Here's a test program using itimer for interrupts. On some systems it will only print out one warning that's it slept about 100 microseconds or so over the target time. On others, it will print out batches of warning that it slept 10+ milliseconds over the target time. Compile with -lrt and run with 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;
}
like image 356
Matt Avatar asked Apr 29 '11 14:04

Matt


People also ask

How do you check interrupts in Linux?

The /proc/interrupts display shows how many interrupts have been delivered to each CPU on the system. As you can see from the output, the Linux kernel generally handles interrupts on the first CPU as a way of maximizing cache locality.

What is the standard IRQ setting for the realtime clock?

The Real Time Clock is less flexible; it handles only 15 possible interrupt rates between 2 Hz and 32767 Hz (theoretically, that is; in practice, the interrupt rate cannot go higher than 8 kHz on most systems).

What is ISR in Linux?

The ISR is a small program that processes certain data depending on the cause of the IRQ. Normal processing is interrupted until the ISR finishes. In the past, IRQs were handled by a separate microchip—the PIC—and I/O devices were wired directly to the PIC.

What are the three types of interrupts handled by operating system kernels?

There can be 3 types of gates: interrupt gate, holds the address of an interrupt or exception handler. Jumping to the handler disables maskable interrupts (IF flag is cleared). trap gates, similar to an interrupt gate but it does not disable maskable interrupts while jumping to interrupt/exception handler.


1 Answers

I've had the same problem with a bare setitimer() setup. The problem is that your process is scheduled by the SCHED_OTHER policy on static priority level 0 by default. This means you're in a pool with all other processes, and dynamic priorities decide. The moment there is some system load, you get latencies.

The solution is to use the sched_setscheduler() system call, increase your static priority to at least one, and specify SCHED_FIFO policy. It causes a dramatic improvement.

#include <sched.h>
...
int main(int argc, char *argv[])
{
    ...
    struct sched_param schedp;
    schedp.sched_priority = 1;
    sched_setscheduler(0, SCHED_FIFO, &schedp);
    ...
}

You have to run as root to be able to do this. The alternative is to use the chrt program to do the same, but you must know the PID of your RT process.

sudo chrt -f -p 1 <pid>

See my blog post about it here.

like image 196
SzG Avatar answered Sep 25 '22 13:09

SzG