如何在Linux用户空间中实现高度精确的计时器? [英] How to implement highly accurate timers in Linux Userspace?

查看:173
本文介绍了如何在Linux用户空间中实现高度精确的计时器?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的BeagleBoard上装有Angstrom Linux.

I have Angstrom Linux on my BeagleBoard.

我想实现一个非常精确的计时器,该计时器可以每500us触发一次.我读了有关hrtimers的文章,但是我发现的所有实现都是针对内核空间的.我想在用户空间中实现它.

I want to implement very accurate timer which can fire at every 500us. I read about hrtimers, but all the implementations I found were for kernel space. I want to implement it in user space.

是否有可以调用这些hrtimers的API,可以在用户空间中使用,或者可以在Linux中实现精确计时器的任何其他方式?

Is there any API which which can call into these hrtimers, which I can use in userspace or any other way of implementing accurate timer in linux?

我将jiffy设置为几纳秒.

推荐答案

最后,经过更多的努力,我发现了一个建议将timer_create()clock_gettime()与信号处理(处理SIGALRM)结合使用的代码,类似于 Basile Starynkevitch 在他的评论中提出了什么.

Finally, after some more efforts I found a code which suggested to use timer_create(), clock_gettime() in conjunction with signal handling (handling SIGALRM), similar to what Basile Starynkevitch suggested in his comment.

我在时钟类型为CLOCK_MONOTONIC的1 GHz Beaglebone上尝试了500us的时间.

I tried it on my 1 GHz Beaglebone with clock type CLOCK_MONOTONIC for the interval of 500us.

在计时器到期的10000次中,在2%次中,恰好是500us(我忽略了纳秒级的差异).并且96.6%的时间在500 +/- 10us的范围内.在其余时间中,平均误差不超过+/- 50us.

Out of 10000 times the timer expired, 2% of the time, it was exactly 500us (I ignored the nanosecond difference). and 96.6% of the time it was in the range of 500 +/- 10us. And the rest of the time, average error was not more than +/- 50us.

这是代码的链接

我在这里发布了经过稍微修改的代码版本.我对代码做了以下修改:

I have posted slightly modified version of code here. I did following modifications to the code:

  1. 在大约10us的小间隔内count会无限递减,因此我在信号处理程序本身内部添加了对测试数量(count)的控制.

  1. For small intervals ~10us the count was getting infinitely decremented, therefore I added the control on number of tests (count) inside the signal handler itself.

在运行计时器的中间添加printf会花费大量时间.因此,我将时差存储在一个数组中,然后在最后一次测试后,即打印了所有内容.

Adding a printf in the middle of running timer costs a lot of time. Hence I stored the time difference in an array and then at the end i.e. after last test, I printed everything.

我认为以unsigned long来计算时间差(即以纳秒为单位)要比以double来计算时间差(以秒为单位)更好,因为它更准确且可能更快.因此,我修改了timerdiff宏以输出纳秒级的差异.由于我使用的间隔为500us或更小,所以差异永远不会超出unsigned long的范围.

I thought calculating time difference in unsigned long (i.e. in nanoseconds) is better than calculating in double (in seconds) as it is more accurate and may be fast. Hence I modified the timerdiff macro to output difference in nanoseconds. Since I am using interval of 500us or less the difference will never overflow the range of unsigned long.

如您所见,即使进行了修改,也只有2%的结果准确到<1us.因此,现在我正在尝试一些其他修改,例如不运行不必要的linux进程,进一步简化程序等.

As you can see even after the modifications, only 2% of the results were accurate to <1us. Therefore now I am trying some more modifications like not running unnecessary linux processes, simplifying my program more, etc.

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

这篇关于如何在Linux用户空间中实现高度精确的计时器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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