我的系统需要至少10毫秒的计时器精度。 我选择了timerfd,因为它非常适合我,但是发现即使在长达15毫秒的时间内,它也不是完全准确的,或者是我不理解它是如何工作的。
在一个10毫秒的计时器上,我测量的时间高达21毫秒。 我已经进行了一次快速测试,以显示我的问题。 这里是一个测试:
#include <sys/timerfd.h> #include <time.h> #include <string.h> #include <stdint.h> int main(int argc, char *argv[]){ int timerfd = timerfd_create(CLOCK_MONOTONIC,0); int milliseconds = atoi(argv[1]); struct itimerspec timspec; bzero(&timspec, sizeof(timspec)); timspec.it_interval.tv_sec = 0; timspec.it_interval.tv_nsec = milliseconds * 1000000; timspec.it_value.tv_sec = 0; timspec.it_value.tv_nsec = 1; int res = timerfd_settime(timerfd, 0, &timspec, 0); if(res < 0){ perror("timerfd_settime:"); } uint64_t expirations = 0; int iterations = 0; while( res = read(timerfd, &expirations, sizeof(expirations))){ if(res < 0){ perror("read:"); continue; } if(expirations > 1){ printf("%lld expirations, %d iterations\n", expirations, iterations); break; } iterations++; } }
像这样执行:
Zack ~$ for i in 2 4 8 10 15; do echo "intervals of $i milliseconds"; ./test $i;done intervals of 2 milliseconds 2 expirations, 1 iterations intervals of 4 milliseconds 2 expirations, 6381 iterations intervals of 8 milliseconds 2 expirations, 21764 iterations intervals of 10 milliseconds 2 expirations, 1089 iterations intervals of 15 milliseconds 2 expirations, 3085 iterations
即使假设有些可能的延迟,对我来说15毫秒的延迟听起来也太多了。
尝试按以下方式进行更改,这应该确保它永远不会丢失唤醒,但要谨慎行事,因为运行实时优先级会在机器不hibernate时将其锁定,因此您可能需要进行设置您的用户可以实时运行内容(请参阅参考资料/etc/security/limits.conf)
/etc/security/limits.conf
#include <sys/timerfd.h> #include <time.h> #include <string.h> #include <stdint.h> #include <stdio.h> #include <sched.h> int main(int argc, char *argv[]) { int timerfd = timerfd_create(CLOCK_MONOTONIC,0); int milliseconds = atoi(argv[1]); struct itimerspec timspec; struct sched_param schedparm; memset(&schedparm, 0, sizeof(schedparm)); schedparm.sched_priority = 1; // lowest rt priority sched_setscheduler(0, SCHED_FIFO, &schedparm); bzero(&timspec, sizeof(timspec)); timspec.it_interval.tv_sec = 0; timspec.it_interval.tv_nsec = milliseconds * 1000000; timspec.it_value.tv_sec = 0; timspec.it_value.tv_nsec = 1; int res = timerfd_settime(timerfd, 0, &timspec, 0); if(res < 0){ perror("timerfd_settime:"); } uint64_t expirations = 0; int iterations = 0; while( res = read(timerfd, &expirations, sizeof(expirations))){ if(res < 0){ perror("read:"); continue; } if(expirations > 1){ printf("%ld expirations, %d iterations\n", expirations, iterations); break; } iterations++; } }
如果您使用的是线程,则应使用pthread_setschedparam而不是sched_setscheduler。
pthread_setschedparam
sched_setscheduler
实时也与低延迟无关,它与保证有关,RT表示,如果您想每秒精确地每秒唤醒一次,那么您将,正常的调度不会给您这样做,它可能会决定将您唤醒100毫秒后来,因为当时还是有其他工作要做。如果您确实需要每10毫秒唤醒一次,那么您应该将自己设置为作为实时任务运行,然后内核将每10毫秒唤醒您一次,而不会失败。除非更高优先级的实时任务忙于做事。
如果您需要确保唤醒间隔恰好是某个时间,那么它是1毫秒还是1秒无关紧要,除非您作为实时任务运行,否则您不会得到它。内核会为您做到这一点有充分的理由(节省功率是其中之一,更高的吞吐量是另外一个,还有其他方面),但是这样做是有权利的,因为您从未告诉过您需要更好的保证。大多数东西实际上并不需要那么精确,或者永远不要错过,所以您应该认真考虑您是否真的需要它。
引用http://www.ganssle.com/articles/realtime.htm
硬实时任务或系统是必须在指定的截止日期之前(总是)完成一项活动的系统。截止日期可以是特定时间或时间间隔,也可以是某个事件的到来。根据定义,如果硬实时任务错过了这样的期限,它们就会失败。 请注意,此定义不对任务的频率或周期做出任何假设。一微秒或一周-如果错过最后期限会导致失败,那么任务对实时性有严格要求。
硬实时任务或系统是必须在指定的截止日期之前(总是)完成一项活动的系统。截止日期可以是特定时间或时间间隔,也可以是某个事件的到来。根据定义,如果硬实时任务错过了这样的期限,它们就会失败。
请注意,此定义不对任务的频率或周期做出任何假设。一微秒或一周-如果错过最后期限会导致失败,那么任务对实时性有严格要求。
软实时几乎是相同的,只是错过了最后期限,尽管不是很理想,但这并不是世界末日(例如,视频和音频播放是软实时任务,您不想错过显示帧或用尽时间)缓冲区,但是如果您这样做只是暂时的打hi,您可以继续执行)。如果您想做的是“软”实时的,那么我不会为实时优先运行而烦恼,因为您通常应该及时(或至少接近)唤醒您。
编辑:
如果您不是实时运行的,则内核默认会给您提供一些“松弛”的计时器,以便它可以合并您的请求以唤醒其他事件,这些事件在接近您要求的时间发生(即是否另一个事件是在您的“空闲”时间内,它不会在您询问时唤醒您,但会早一点或晚一点,同时已经在做其他事情了,这样可以节省电量。
有关更多信息,请参阅高分辨率(但不是太高)超时和计时器松弛(请注意,我不确定这些事情中的任何一个是否真正在内核中真正存在,因为这两篇文章都是关于lkml邮件列表的讨论,但是像第一个这样的东西确实在内核中。