Linux,timerfd 精度 [英] Linux, timerfd accuracy

查看:18
本文介绍了Linux,timerfd 精度的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的系统需要至少 10 毫秒的计时器精度.
我选择了 timerfd,因为它非常适合我,但发现即使时间长达 15 毫秒,它也根本不准确,或者我不明白它是如何工作的.

我测量的时间在 10 毫秒计时器上高达 21 毫秒.
我已经做了一个快速测试来显示我的问题.
这是一个测试:

#include #include #include #include #include #include #include #include int main(int argc, char *argv[]){int timerfd = timerfd_create(CLOCK_MONOTONIC,0);int 毫秒 = atoi(argv[1]);struct itimerspec timspec;bzero(&timspec, sizeof(timspec));timspec.it_interval.tv_sec = 0;timspec.it_interval.tv_nsec = 毫秒 * 1000000;timspec.it_value.tv_sec = 0;timspec.it_value.tv_nsec = 1;int res = timerfd_settime(timerfd, 0, &timspec, 0);如果(res <0){perror(timerfd_settime:");返回 1;}uint64_t 过期时间 = 0;整数迭代 = 0;while( res = read(timerfd, &expirations, sizeof(expirations))){if(res <0){ perror(read:");继续;}如果(到期时间> 1){printf("%" PRIu64 "到期,%d 次迭代
",到期,迭代);休息;}迭代++;}返回0;}

然后是这样执行的:

Zack ~$ for i in 2 4 8 10 15;做回声$i 毫秒的间隔";./test $i;done2 毫秒的间隔2 次到期,1 次迭代4 毫秒的间隔2 次到期,6381 次迭代8 毫秒的间隔2 次到期,21764 次迭代10 毫秒的间隔2 次到期,1089 次迭代间隔 15 毫秒2 次到期,3085 次迭代

即使假设有一些可能的延迟,15 毫秒的延迟对我来说也太多了.

解决方案

尝试如下改变它,这应该几乎保证它永远不会错过唤醒,但要小心它,因为运行实时优先级可以锁定你的机器如果它不睡觉很难,你也可能需要设置一些东西,以便你的用户能够以实时优先级运行东西(参见 /etc/security/limits.conf)

#include #include #include #include #include #include int main(int argc, char *argv[]){int timerfd = timerfd_create(CLOCK_MONOTONIC,0);int 毫秒 = atoi(argv[1]);struct itimerspec timspec;struct sched_pa​​ram schedparm;memset(&schedparm, 0, sizeof(schedparm));schedparm.sched_priority = 1;//最低 rt 优先级sched_setscheduler(0, SCHED_FIFO, &schedparm);bzero(&timspec, sizeof(timspec));timspec.it_interval.tv_sec = 0;timspec.it_interval.tv_nsec = 毫秒 * 1000000;timspec.it_value.tv_sec = 0;timspec.it_value.tv_nsec = 1;int res = timerfd_settime(timerfd, 0, &timspec, 0);如果(res <0){perror("timerfd_settime:");}uint64_t 过期时间 = 0;整数迭代 = 0;while( res = read(timerfd, &expirations, sizeof(expirations))){if(res < 0){ perror("read:");继续;}如果(到期时间> 1){printf("%ld 次过期,%d 次迭代
",过期,迭代);休息;}迭代++;}}

如果您使用线程,则应使用 pthread_setschedparam 而不是 sched_setscheduler.

Realtime 也不是关于低延迟,而是关于保证,RT 意味着如果你想在每秒准确地唤醒一次,你会的,正常的调度不会给你这个,它可能会决定唤醒你在 100 毫秒后启动,因为那时它还有一些其他工作要做.如果您想每 10 毫秒唤醒一次并且您确实需要这样做,那么您应该将自己设置为作为实时任务运行,然后内核将每 10 毫秒唤醒一次,而不会失败.除非更高优先级的实时任务正在忙着做事.

如果你需要保证你的唤醒间隔正好是某个时间,不管是 1ms 还是 1 秒都没有关系,除非你作为实时任务运行,否则你不会得到它.内核会对您这样做是有充分的理由的(节能是其中之一,更高的吞吐量是另一个,还有其他的),但是这样做完全在它的权利范围内,因为您从未告诉它您需要更好的保证.大多数东西实际上并不需要如此准确,或者不需要永远不会错过,所以你应该仔细考虑你是否真的需要它.

引自 http://www.ganssle.com/articles/realtime.htm

<块引用>

硬实时任务或系统是一个活动必须是完成 - 总是 - 由指定最后期限.截止日期可能是特定时间或时间间隔,或可能是某个事件的到来.难的根据定义,实时任务失败如果他们错过了这样的截止日期.

注意这个定义没有关于频率的假设或任务期间.微秒或一周 - 如果错过最后期限导致失败,那么任务有硬实时要求.

软实时几乎是一样的,除了错过最后期限虽然不受欢迎,但并不是世界末日(例如视频和音频播放是软实时任务,您不想错过显示帧,或用完缓冲区,但如果你这样做,它只是暂时的打嗝,你只需继续).如果您尝试做的是软"实时,我不会打扰以实时优先级运行,因为您通常应该及时唤醒(或至少接近它).

如果你不是实时运行,内核默认会给任何你做一些松弛"的计时器,以便它可以将你的唤醒请求与发生在你要求的时间附近的其他事件(即是如果另一个事件在您的空闲"时间内,它不会在您要求的时间唤醒您,但会早一点或晚一点,同时它已经准备做其他事情,这样可以节省电量).

有关更多信息,请参阅高(但不是太高)分辨率超时Timer slack(请注意,我不确定这些事情中的任何一个是否正是内核中的真正内容,因为两者都这些文章是关于 lkml 邮件列表的讨论,但类似于第一篇的内容确实存在于内核中.

I have a system that needs at least 10 mseconds of accuracy for timers.
I went for timerfd as it suits me perfectly, but found that even for times up to 15 milliseconds it is not accurate at all, either that or I don't understand how it works.

The times I have measured were up to 21 mseconds on a 10 mseconds timer.
I have put together a quick test that shows my problem.
Here a test:

#include <sys/timerfd.h>
#include <time.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <inttypes.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:");
       return 1;
    }
    uint64_t expirations = 0;
    int iterations = 0;
    while( res = read(timerfd, &expirations, sizeof(expirations))){
        if(res < 0){ perror("read:"); continue; }
        if(expirations > 1){
            printf("%" PRIu64 " expirations, %d iterations
", expirations, iterations);
            break;
        }
        iterations++;
    }
    return 0;
}

And executed like this:

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

Even assuming some possible delays, 15 milliseconds delays sounds too much for me.

解决方案

Try altering it as follows, this should pretty much garuntee that it'll never miss a wakeup, but be careful with it since running realtime priority can lock your machine hard if it doesn't sleep, also you may need to set things up so that your user has the ability to run stuff at realtime priority (see /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
", expirations, iterations);
            break;
        }
        iterations++;
    }
}

If you are using threads you should use pthread_setschedparam instead of sched_setscheduler.

Realtime also isn't about low latency, it's about guarantees, RT means that if you want to wake up exactly once every second on the second, you WILL, the normal scheduling does not give you this, it might decide to wake you up 100ms later, because it had some other work to do at that time anyway. If you want to wake up every 10ms and you REALLY do need to, then you should set yourself to run as a realtime task then the kernel will wake you up every 10ms without fail. Unless a higher priority realtime task is busy doing stuff.

If you need to guarantee that your wakeup interval is exactly some time it doesn't matter if it's 1ms or 1 second, you won't get it unless you run as a realtime task. There are good reasons the kernel will do this to you (saving power is one of them, higher throughput is another, there are others), but it's well within it's rights to do so since you never told it you need better guarantees. Most stuff doesn't actually need to be this accurate, or need to never miss so you should think hard about whether or not you really do need it.

quoting from http://www.ganssle.com/articles/realtime.htm

A hard real time task or system is one where an activity simply must be completed - always - by a specified deadline. The deadline may be a particular time or time interval, or may be the arrival of some event. Hard real time tasks fail, by definition, if they miss such a deadline.

Notice this definition makes no assumptions about the frequency or period of the tasks. A microsecond or a week - if missing the deadline induces failure, then the task has hard real time requirements.

Soft realtime is pretty much the same, except that missing a deadline, while undesirable, is not the end of the world (for example video and audio playback are soft realtime tasks, you don't want to miss displaying a frame, or run out of buffer, but if you do it's just a momentary hiccough, and you simply continue). If what you are trying to do is 'soft' realtime I wouldn't bother with running at realtime priority since you should generally get your wakeups in time (or at least close to it).

EDIT:

If you aren't running realtime the kernel will by default give any timers you make some 'slack' so that it can merge your request to wake up with other events that happen at times close to the one you asked for (that is if the other event is within your 'slack' time it will not wake you at the time you asked, but a little earlier or later, at the same time it was already going to do something else, this saves power).

For a little more info see High- (but not too high-) resolution timeouts and Timer slack (note I'm not sure if either of those things is exactly what's really in the kernel since both those articles are about lkml mailing list discussions, but something like the first one really is in the kernel.

这篇关于Linux,timerfd 精度的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆