我的系统需要至少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++;
}
}
Run Code Online (Sandbox Code Playgroud)
并执行如下:
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
Run Code Online (Sandbox Code Playgroud)
即使假设有一些可能的延迟,15毫秒的延迟听起来对我来说太多了.
Spu*_*d86 12
尝试改变它,如下所示,这应该非常鼓励,它永远不会错过唤醒,但要小心它,因为运行实时优先级可以锁定你的机器,如果它不睡觉,你也可能需要设置所以您的用户能够以实时优先级运行内容(请参阅参考资料/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++;
}
}
Run Code Online (Sandbox Code Playgroud)
如果您使用线程,则应使用pthread_setschedparam而不是sched_setscheduler.
实时也不是关于低延迟,而是关于保证,RT意味着如果你想在第二秒每秒唤醒一次,你会,正常的调度没有给你这个,它可能决定叫醒你100ms之后,因为当时还有其他一些工作要做.如果你想每10分钟醒来并且你真的需要,那么你应该将自己设置为一个实时任务,然后内核会每10分钟唤醒你一次.除非更高优先级的实时任务正忙着做事.
如果你需要保证你的唤醒间隔恰好是一段时间,无论它是1毫秒还是1秒,除非你作为实时任务运行,否则你不会得到它.内核有很好的理由为你做到这一点(节省能力是其中之一,更高的吞吐量是另一个,还有其他),但它完全有权这样做,因为你从来没有告诉它你需要更好的保证.大多数东西实际上并不需要这么准确,或者永远不要错过,所以你应该认真思考你是否真的需要它.
引自http://www.ganssle.com/articles/realtime.htm
硬实时任务或系统是指必须在指定的截止日期之前完成活动的任务或系统.截止日期可以是特定时间或时间间隔,或者可以是某个事件的到来.根据定义,如果他们错过这样的截止日期,那么硬实时任务就会失败.
请注意,此定义不对任务的频率或周期做出任何假设.一微秒或一周 - 如果错过最后期限会导致失败,那么任务就会有严格的实时要求.
软实时几乎是一样的,除了错过最后期限,虽然不受欢迎,但不是世界末日(例如视频和音频播放是软实时任务,你不想错过显示一个帧,或用完缓冲区,但如果你这样做只是一个短暂的打嗝,你只需继续).如果你想要做的是"软"实时,我不会为实时优先运行而烦恼,因为你通常应该及时唤醒(或者至少接近它).
编辑:
如果您没有实时运行,内核将默认为任何计时器提供一些"松弛",以便它可以合并您的请求以唤醒与您要求的接近时发生的其他事件(即如果其他事件在你的'松弛'时间内,它不会在你问的时候叫醒你,但是早一点或更晚,同时它已经会做其他事情,这节省了电力).
有关更多信息,请参阅高(但不是太高)分辨率超时和计时器松弛(注意我不确定这些内容中的任何一个是否真的在内核中,因为这两篇文章都是关于lkml邮件列表讨论,但是第一个真正存在于内核中的东西.