我正在尝试描述 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。