最后,经过一番努力,我找到了一个代码,它建议将timer_create()、clock_gettime() 与信号处理(处理 SIGALRM)结合使用,类似于 Basile Starynkevitch 在他的评论中建议的.
我在时钟类型为CLOCK_MONOTONIC 的 1 GHz Beaglebone 上尝试了 500us 的时间间隔。
在10000 次计时器到期后,2% 时间正好是 500 微秒(我忽略了纳秒的差异)。和96.6% 的时间在500 +/- 10us 的范围内。其余时间,平均误差不超过 +/- 50us。
This is the link for the code
我在这里发布了稍微修改过的代码版本。我对代码做了以下修改:
-
对于 ~10us 的小间隔,count 会无限递减,因此我在信号处理程序本身内添加了对测试数量 (count) 的控制。
-
在运行计时器的中间添加printf 会花费大量时间。因此,我将时间差存储在一个数组中,然后在最后,即在最后一次测试之后,我打印了所有内容。
-
我认为以unsigned long(即以纳秒为单位)计算时间差比以double(以秒为单位)计算更好,因为它更准确并且可能更快。因此,我修改了timerdiff 宏以输出以纳秒为单位的差异。由于我使用的是 500us 或更短的间隔,因此差异永远不会超出 unsigned long 的范围。
正如您所见,即使在修改之后,也只有 2% 的结果准确到
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
#define NSEC_PER_SEC 1000000000L
#define MAX_TESTS 10000
#define timerdiff(a,b) (((a)->tv_sec - (b)->tv_sec) * NSEC_PER_SEC + \
(((a)->tv_nsec - (b)->tv_nsec)))
static struct timespec prev = {.tv_sec=0,.tv_nsec=0};
static int count = MAX_TESTS;
unsigned long diff_times[MAX_TESTS];
void handler( int signo )
{
struct timespec now;
register int i, correct=0;
if(count >= 0)
{
clock_gettime(CLOCK_MONOTONIC, &now);
diff_times[count]=timerdiff(&now, &prev);
prev = now;
count --;
}
else
{
for(i=0; i<MAX_TESTS; ++i)
{
if(diff_times[i]/1000 < 510 && diff_times[i]/1000 > 490)
{
printf("%d->\t", i);
correct++;
}
printf("%lu\n", diff_times[i]);
}
printf("-> %d\n", correct);
exit(0);
}
}
int main(int argc, char *argv[])
{
int i = 0;
timer_t t_id;
struct itimerspec tim_spec = {.it_interval= {.tv_sec=0,.tv_nsec=500000},
.it_value = {.tv_sec=1,.tv_nsec=0}};
struct sigaction act;
sigset_t set;
sigemptyset( &set );
sigaddset( &set, SIGALRM );
act.sa_flags = 0;
act.sa_mask = set;
act.sa_handler = &handler;
sigaction( SIGALRM, &act, NULL );
if (timer_create(CLOCK_MONOTONIC, NULL, &t_id))
perror("timer_create");
clock_gettime(CLOCK_MONOTONIC, &prev);
if (timer_settime(t_id, 0, &tim_spec, NULL))
perror("timer_settime");
while(1);
return 0;
}