linux - 如何在 Linux 用户空间实现高精度定时器?

标签 linux timer beagleboard

我的 BeagleBoard 上安装了 Angstrom Linux。

我想实现每 500us 触发一次的非常精确的定时器。我阅读了有关 hrtimers 的内容,但我发现的所有实现都是针对内核空间的。我想在用户空间中实现它。

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

我已将 jiffy 设置为几纳秒。

最佳答案

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

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

10000 次计时器过期的情况下,2% 的时间正好是 500us(我忽略了纳秒的差异)。 96.6% 的时间在 500 +/- 10us 范围内。其余时间,平均误差不超过 +/- 50us。

This is the link for the code

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

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

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

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

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

#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 - 如何在 Linux 用户空间实现高精度定时器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24051863/

相关文章:

linux - 使用 Bash 发送多个 SSH 命令

c - 使用内核模块与 Beagleboard xM 进行 16*2 LCD 连接

android - 基于 Beaglebone 的 Android(Rowboat)外设?

android - 如何获得每个进程和进程空闲 CPU 时间的准确 CPU 使用率

c - 这个结构是做什么的?

java - JPanel 中的 Swing 计时器和动画

c++ - 如何获取进程中的定时器个数?

debian - Beaglebone BlackLib 库不再与 debian 10 一起使用

linux - CMake:如何在 add_custom_command(...) 中使用 if 条件

c# - 将计时器值存储在数据库中? C#