【问题标题】:What is the accuracy of interval timers in Linux?Linux中间隔计时器的准确性是多少?
【发布时间】:2013-11-24 22:48:04
【问题描述】:

我正在尝试描述 Linux 上的计时器抖动。我的任务是运行 100 毫秒的计时器,看看数字是如何计算出来的。

我正在开发一台多核机器。我使用带有 setitimer() 的标准用户程序,以 root 身份运行,然后使用处理器亲和性,最后使用处理器亲和性和进程优先级。然后我使用 PREEMPT_RT 内核运行相同的程序,然后使用 clock_nanosleep() 运行示例,就像 PREEMPT_RT 页面上的演示代码一样。在所有运行中,计时器的性能非常相似,尽管发生了变化,但并没有真正的区别。

我们的最终目标是稳定的计时器。我能经常得到的最好的最坏情况是大约 200us。所有情况的直方图都显示出非常奇怪的行为。一方面,我不希望计时器提前触发。但他们确实如此。正如您在直方图中看到的那样,我在 0 偏移量的任一侧都有低谷。这些在第二张图中的三个波段中可见。在第一张图中,X 轴以微秒为单位。在第二张图中,Y 轴以微秒为单位。

我运行 30 秒测试(即 300 个计时器事件)100 次以生成一些数字。您可以在下图中看到它们。 200us 有很大的下降。所有 30000 个计时器事件时钟偏移都显示在第二张图中,您可以在其中看到一些异常值。

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

这是代码。就像我之前说的,我在 PREEMPT_RT 网站上修改了使用 clock_nanosleep() 函数的示例代码,所以我不会包含我的最小更改。

#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);
    }
}

编辑:这是在 Xeon E31220L 上运行的,运行频率为 2.2 GHz,运行 x86_64 Fedora Core 19。

【问题讨论】:

  • 这是在 x86 上对吧?什么硬件?这里涉及的变量太多了,我认为做出任何有效的概括将非常困难。
  • 添加了有关 CPU 架构的信息。谢谢!
  • 请同时包含timer_handler()函数的来源。
  • 有人可能会指出,setitimer() 应该已经过时,整个 signal() 事件传递路径不可靠。考虑使用 timerfd_create() 和朋友,你可能会得到更好的结果。
  • @Ian 这只是 GnuPlot。旧的,但一旦你弄清楚一些基础知识就会很好用。如果你想要的话,我可以把我用来生成图表的脚本发给你。

标签: c linux timer real-time preempt-rt


【解决方案1】:

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

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

如果您希望 PREEMPT_RT 为您提供帮助,您需要为您的测试程序(SCHED_FIFOSCHED_RR)设置一个实时调度策略,这需要 root。

【讨论】:

  • 对于 PREEMPT_RT,我就是这样做的。我怀疑我的 gettimeofday() 调用是可疑的,您的解释可以解决问题。 PREEMPT_RT 表现出相同行为的原因是因为我使用相同的代码来比较时间。感谢您的回复。
  • 顺便说一句,您可以在该循环中使用pause(); 而不是sleep(1);
  • 另外,我已经heard gettimeofday()PREEMPT_RT 下不是实时安全的;正确的选择是clock_gettime()
【解决方案2】:

我对您的代码进行了一些更改,主要将timer替换为如下并将该进程作为RT进度(SCHED_FIFO)运行。

setitimer()      ->    timer_create()/timer_settime()
gettimeofday()   ->    clock_gettime()

我的测试平台是 i9-9900k CPU 和 PREEMPT-RT 补丁 Linux 与 5.0.21 内核。定时器的时间间隔为1ms,程序运行约10小时产生如下结果。

我还在我的机器上运行Cyclictest(基于nanosleep()),它显示出更好的延迟控制(最大延迟小于15us)。所以,在我看来,如果你想自己实现一个高分辨率的定时器,一个在独立内核上运行 nanosleep 的独立 RT 线程可能会有所帮助。我是RT系统新手,欢迎cmets。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多