Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/azure/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在Linux中获得最准确的实时周期性中断?_Linux_Timer_Real Time - Fatal编程技术网

如何在Linux中获得最准确的实时周期性中断?

如何在Linux中获得最准确的实时周期性中断?,linux,timer,real-time,Linux,Timer,Real Time,我希望中断频率为十次方,因此从/dev/rtc启用中断并不理想。我想在中断之间睡1毫秒或250微秒 从/dev/hpet启用定期中断工作得很好,但在某些机器上似乎不起作用。显然,我不能在没有HPET的机器上使用它。但我也不能让它在一些有hpet作为时钟源的机器上工作。例如,在Core2Quad上,内核文档中包含的示例程序在HPET_IE_on设置为轮询时失败 最好使用Linux提供的itimer接口,而不是直接与硬件设备驱动程序接口。在某些系统上,itimer提供周期性中断,随着时间的推移,这种

我希望中断频率为十次方,因此从/dev/rtc启用中断并不理想。我想在中断之间睡1毫秒或250微秒

从/dev/hpet启用定期中断工作得很好,但在某些机器上似乎不起作用。显然,我不能在没有HPET的机器上使用它。但我也不能让它在一些有hpet作为时钟源的机器上工作。例如,在Core2Quad上,内核文档中包含的示例程序在HPET_IE_on设置为轮询时失败

最好使用Linux提供的itimer接口,而不是直接与硬件设备驱动程序接口。在某些系统上,itimer提供周期性中断,随着时间的推移,这种中断更加稳定。也就是说,由于hpet不能以我想要的频率中断,中断开始从墙时间漂移。但我发现一些系统的睡眠时间比使用itimer时长(10毫秒以上)

这里有一个使用itimer的中断测试程序。在某些系统上,它只会输出一个警告,即它在目标时间内睡眠大约100微秒。在其他情况下,它会打印成批的警告,指出它在目标时间内睡眠了10多毫秒。使用-lrt编译并使用sudo chrt-f 50[名称]运行

#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;
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#定义NS_/秒1000000000LL
#定义TIMESPEC_至_NS(aTime)((NS_/秒*((long long int)aTime.tv_sec))\
+aTime.tv_nsec)
int main()
{
//阻塞报警信号,将明确等待
西格塞特·特拉拉姆;
西格里塞特和拉拉姆;
sigaddset(&lAlarm,SIGALRM);
sigprocmask(SIG_块和lAlarm,空);
//设置周期中断计时器
结构itimerval lTimer;
int lReceivedSignal=0;
lTimer.it_value.tv_sec=0;
lTimer.it_value.tv_usec=250;
lTimer.it\u间隔=lTimer.it\u值;
//启动计时器
if(setitimer(ITIMER\u REAL,&lTimer,NULL)!=0)
{
错误(退出_失败,错误号,“无法启动间隔计时器”);
}
结构timespec lLastTime;
结构timespec lCurrentTime;
时钟获取时间(时钟实时和实时);
而(1)
{
//周期性等待
如果(信号等待(&lAlarm,&lReceivedSignal)!=0)
{
错误(EXIT_FAILURE,errno,“未能等待下一个时钟信号”);
}
时钟获取时间(时钟实时和当前时间);
长内差=
(TIMESPEC_至_NS(lCurrentTime)-TIMESPEC_至_NS(lLastTime));
如果(差异>300000)
{
fprintf(stderr,“等待时间太长:%lld\n”,l差异);
}
lLastTime=lCurrentTime;
}
返回0;
}

无论您使用什么计时机制,它归结为在调用内核调度程序(通常每秒100或1000次)时任务运行状态的变化,以及与其他进程的cpu争用

我发现在Linux(以及Windows)上实现“最佳”定时的机制是执行以下操作:

  • 将流程放置在
  • 让进程最初休眠1ms。如果在屏蔽CPU上,您的进程应该在OS调度器的勾选边界上正确唤醒
  • 直接使用RDTSC或时钟单元捕捉当前时间。将其用作计算未来所有时段绝对唤醒时间的零时间。这将有助于减少随时间的漂移。它不能完全消除,因为硬件计时随时间而波动(热问题等),但这是一个很好的开始
  • 创建一个睡眠函数,该函数的睡眠时间比目标绝对唤醒时间短1ms(因为这是操作系统调度器可以达到的最精确的时间),然后在一个紧密的循环中烧掉CPU,不断检查RDTSC/CLOCK_实时值

  • 这需要一些工作,但使用这种方法可以获得相当好的结果。您可能想查看的一个相关问题可以在这里找到。

    我对裸setitimer()设置也有同样的问题。 问题是,默认情况下,您的进程是由SCHED_OTHER策略在静态优先级为0的情况下调度的。这意味着您与所有其他流程处于一个池中,由动态优先级决定。一旦出现系统负载,就会出现延迟

    解决方案是使用sched_setscheduler()系统调用,将静态优先级增加到至少一个,并指定sched_FIFO策略。它带来了巨大的进步

    #include <sched.h>
    ...
    int main(int argc, char *argv[])
    {
        ...
        struct sched_param schedp;
        schedp.sched_priority = 1;
        sched_setscheduler(0, SCHED_FIFO, &schedp);
        ...
    }
    
    #包括
    ...
    int main(int argc,char*argv[])
    {
    ...
    结构sched_参数schedp;
    schedp.sched_优先级=1;
    调度设置调度器(0、调度FIFO和调度DP);
    ...
    }
    
    您必须以root用户身份运行才能执行此操作。另一种方法是使用chrt程序执行同样的操作,但是您必须知道RT进程的PID

    sudo chrt -f -p 1 <pid>
    
    sudo chrt-f-p1
    

    请参阅我的博客文章。

    这可能是一个内核错误。我的itimer示例似乎在所有使用2.6.32的机器上都能正常工作,但在2.6.35或2.6.38上却不能。Linux现在可以在任务准备就绪时安排任务,而不是依赖于每一次运行的调度程序(我相信这是一个不可用的选项)。HPET方法(在read()调用上阻塞)可以很好地用于每隔250微秒将上下文切换到高实时优先级任务(如果HPET方法可以工作的话)。itimer方法通常用于做相同的事情。在某些机器上,它一直工作。在其他情况下,它通常是有效的(我没有被警告信息淹没),但有时我会等待1MS+。