Linux中间隔计时器的准确度是多少?

Pax*_*ers 13 c linux real-time timer preempt-rt

我试图描述Linux上的计时器抖动.我的任务是运行100毫秒的计时器,看看这些数字是如何运作的.

我正在研究多核机器.我使用了setitimer()的标准用户程序,以root身份运行,然后使用处理器亲和性,最后使用处理器亲和性和进程优先级.然后我使用PREEMPT_RT内核运行相同的内容,然后使用clock_nanosleep()运行示例,如PREEMPT_RT页面上的演示代码中所示.在所有运行中,计时器性能非常相似,尽管有变化,但没有真正的差异.

我们的最终目标是稳定的计时器.我经常得到的最好的最坏情况是大约200us.所有案例的直方图都显示出奇怪的行为.首先,我不希望计时器提前开火.但他们这样做.正如您在直方图中看到的那样,我在0偏移的两边都得到了低谷.这些在第二个图中的三个波段中可见.在第一个图中,X轴以微秒为单位.在第二个图中,Y轴以微秒为单位.

我跑了30s测试(即300个计时器事件)100次以生成一些数字.您可以在下图中看到它们.200us大幅下降.所有30000定时器事件时钟偏移都在第二个图表中绘制,您可以在其中看到一些异常值.

X轴以微秒为单位Y轴以微秒为单位

所以问题是,之前有其他人做过这种分析吗?你有没有看到同样的行为?我的假设是RT内核可以帮助重载系统,但在我们的例子中,它无助于消除定时器抖动.那是你的经历吗?

这是代码.就像我之前说过的,我修改了使用clock_nanosleep()函数的PREEMPT_RT站点上的示例代码,因此我不会包含我的最小更改.

#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <stdlib.h>

#define US_PER_SEC 1000000
#define WAIT_TIME 100000
#define MAX_COUNTER 300

int counter = 0;
long long last_time = 0;
static long long times[MAX_COUNTER];
int i = 0;

struct sigaction sa;

void timer_handler(int signum)
{
    if (counter > MAX_COUNTER)
    {
        sigaction(SIGALRM, &sa, NULL);
        for (i = 0; i < MAX_COUNTER; i++)
        {
            printf("%ld\n", times[i]);
        }
        exit(EXIT_SUCCESS);
    }

    struct timeval t;
    gettimeofday(&t, NULL);

    long long elapsed = (t.tv_sec * US_PER_SEC + t.tv_usec);

    if (last_time != 0)
    {
        times[counter] = elapsed - last_time;
        ++counter;
    }

    last_time = elapsed; 
}

int main()
{
    struct itimerval timer;

    memset(&sa, 0, sizeof(sa));

    sa.sa_handler = &timer_handler;

    sigaction(SIGALRM, &sa, NULL);

    timer.it_value.tv_sec = 0;
    timer.it_value.tv_usec = 1;

    timer.it_interval.tv_sec = 0;
    timer.it_interval.tv_usec = WAIT_TIME;

    setitimer(ITIMER_REAL, &timer, NULL);

    while (1)
    {
        sleep(1);
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:这是在Xeon E31220L,运行在2.2 GHz,运行x86_64 Fedora Core 19.

caf*_*caf 2

你不希望计时器提前启动是对的 - 但它们确实没有。明显提前触发是因为您没有测量自上一个计时器到期以来的时间 - 您正在测量自上一次gettimeofday()调用以来的时间。如果计时器到期和实际安排进程之间存在延迟,那么您将看到这个gettimeofday()运行较晚,而下一个则提前运行相同的量

不要记录后续调用之间的差异gettimeofday(),而是尝试记录返回的绝对时间,然后将返回的时间与初始时间后的 N * 100 毫秒进行比较。

如果您想帮助您,您将需要为您的测试程序(或)PREEMPT_RT设置实时调度程序策略,这需要 root。SCHED_FIFOSCHED_RR