代码之家  ›  专栏  ›  技术社区  ›  Arkaitz Jimenez

Linux,定时器精度

  •  6
  • Arkaitz Jimenez  · 技术社区  · 16 年前

    我有一个系统,需要至少10毫秒的精度计时器。

    在10毫秒计时器上,我测量的时间高达21毫秒。
    我做了一个快速测试来说明我的问题。
    这里有一个测试:

    #include <sys/timerfd.h>
    #include <time.h>
    #include <string.h>
    #include <stdint.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <inttypes.h>
    
    int main(int argc, char *argv[]){
    
        int timerfd = timerfd_create(CLOCK_MONOTONIC,0);
        int milliseconds = atoi(argv[1]);
        struct itimerspec timspec;
        bzero(&timspec, sizeof(timspec));
        timspec.it_interval.tv_sec = 0;
        timspec.it_interval.tv_nsec = milliseconds * 1000000;
        timspec.it_value.tv_sec = 0;
        timspec.it_value.tv_nsec = 1;
    
        int res = timerfd_settime(timerfd, 0, &timspec, 0);
        if(res < 0){
           perror("timerfd_settime:");
           return 1;
        }
        uint64_t expirations = 0;
        int iterations = 0;
        while( res = read(timerfd, &expirations, sizeof(expirations))){
            if(res < 0){ perror("read:"); continue; }
            if(expirations > 1){
                printf("%" PRIu64 " expirations, %d iterations\n", expirations, iterations);
                break;
            }
            iterations++;
        }
        return 0;
    }
    

    执行方式如下:

    Zack ~$ for i in 2 4 8 10 15; do echo "intervals of $i milliseconds"; ./test $i;done
    intervals of 2 milliseconds
    2 expirations, 1 iterations
    intervals of 4 milliseconds
    2 expirations, 6381 iterations
    intervals of 8 milliseconds
    2 expirations, 21764 iterations
    intervals of 10 milliseconds
    2 expirations, 1089 iterations
    intervals of 15 milliseconds
    2 expirations, 3085 iterations
    

    即使假设一些可能的延迟,15毫秒的延迟对我来说也太多了。

    4 回复  |  直到 5 年前
        1
  •  16
  •   Andy Lynch    11 年前

    /etc/security/limits.conf )

    #include <sys/timerfd.h>
    #include <time.h>
    #include <string.h>
    #include <stdint.h>
    #include <stdio.h>
    #include <sched.h>
    
    int main(int argc, char *argv[]) 
    {
        int timerfd = timerfd_create(CLOCK_MONOTONIC,0);
        int milliseconds = atoi(argv[1]);
        struct itimerspec timspec;
        struct sched_param schedparm;
    
        memset(&schedparm, 0, sizeof(schedparm));
        schedparm.sched_priority = 1; // lowest rt priority
        sched_setscheduler(0, SCHED_FIFO, &schedparm);
    
        bzero(&timspec, sizeof(timspec));
        timspec.it_interval.tv_sec = 0;
        timspec.it_interval.tv_nsec = milliseconds * 1000000;
        timspec.it_value.tv_sec = 0;
        timspec.it_value.tv_nsec = 1;
    
        int res = timerfd_settime(timerfd, 0, &timspec, 0);
        if(res < 0){
           perror("timerfd_settime:");
        }
        uint64_t expirations = 0;
        int iterations = 0;
        while( res = read(timerfd, &expirations, sizeof(expirations))){
            if(res < 0){ perror("read:"); continue; }
            if(expirations > 1){
                printf("%ld expirations, %d iterations\n", expirations, iterations);
                break;
            }
            iterations++;
        }
    }
    

    如果您使用的是线程,那么应该使用 pthread_setschedparam sched_setscheduler .

    实时性也不是关于低延迟,而是关于保证,RT意味着如果你想在第二天每秒钟醒来一次,你会的,正常的时间安排不会给你这个,它可能会决定在100毫秒后唤醒你,因为那时候它还有其他工作要做。如果您想每10毫秒唤醒一次,并且确实需要,那么您应该将自己设置为作为实时任务运行,那么内核将每10毫秒唤醒一次。除非有更高优先级的实时任务正忙着做事情。

    引用自 http://www.ganssle.com/articles/realtime.htm

    硬实时任务或系统是 由指定的 最后期限。最后期限可能是 可能是某个事件的到来。硬的 根据定义,实时任务失败, 如果他们错过了最后期限。

    注意这个定义没有 关于频率或 任务的周期。微秒或 导致失败,则任务已完成

    软实时几乎是一样的,除了错过一个最后期限,虽然不受欢迎,但不是世界末日(例如,视频和音频播放是软实时任务,你不想错过显示一帧,或用尽缓冲区,但如果你这样做只是一个短暂的打嗝,你只是继续)。如果你想做的是'软'实时,我不会费心运行在实时优先级,因为你一般应该得到你的唤醒时间(或至少接近它)。

    编辑:

    更多信息请参见 High- (but not too high-) resolution timeouts Timer slack (注意,我不确定这两篇文章是否都是关于lkml邮件列表的讨论,但是第一篇文章确实在内核中。

        2
  •  3
  •   sarnold    16 年前

    我觉得你的测试非常依赖硬件。当我在我的系统上运行你的示例程序时,它似乎挂起了1毫秒。为了让你的测试在我的电脑上有意义,我不得不从毫秒改为微秒(我把乘数从1000改为1000。)

    $ grep 1000 test.c
        timspec.it_interval.tv_nsec = microseconds * 1000;
    
    $ for i in 1 2 4 5 7 8 9 15 16 17\
     31 32 33 47 48 49 63 64 65 ; do\
     echo "intervals of $i microseconds";\
     ./test $i;done
    intervals of 1 microseconds
    11 expirations, 0 iterations
    intervals of 2 microseconds
    5 expirations, 0 iterations
    intervals of 4 microseconds
    3 expirations, 0 iterations
    intervals of 5 microseconds
    2 expirations, 0 iterations
    intervals of 7 microseconds
    2 expirations, 0 iterations
    intervals of 8 microseconds
    2 expirations, 0 iterations
    intervals of 9 microseconds
    2 expirations, 0 iterations
    intervals of 15 microseconds
    2 expirations, 7788 iterations
    intervals of 16 microseconds
    4 expirations, 1646767 iterations
    intervals of 17 microseconds
    2 expirations, 597 iterations
    intervals of 31 microseconds
    2 expirations, 370969 iterations
    intervals of 32 microseconds
    2 expirations, 163167 iterations
    intervals of 33 microseconds
    2 expirations, 3267 iterations
    intervals of 47 microseconds
    2 expirations, 1913584 iterations
    intervals of 48 microseconds
    2 expirations, 31 iterations
    intervals of 49 microseconds
    2 expirations, 17852 iterations
    intervals of 63 microseconds
    2 expirations, 24 iterations
    intervals of 64 microseconds
    2 expirations, 2888 iterations
    intervals of 65 microseconds
    2 expirations, 37668 iterations
    

    (有点有趣的是,我得到了16和47微秒的最长跑步记录,但17和48微秒非常糟糕。)

    《时代》周刊(7)对为什么我们的平台如此不同提出了一些建议:

       High-Resolution Timers
           Before Linux 2.6.21, the accuracy of timer and sleep system
           calls (see below) was also limited by the size of the jiffy.
    
           Since Linux 2.6.21, Linux supports high-resolution timers
           (HRTs), optionally configurable via CONFIG_HIGH_RES_TIMERS.  On
           a system that supports HRTs, the accuracy of sleep and timer
           system calls is no longer constrained by the jiffy, but instead
           can be as accurate as the hardware allows (microsecond accuracy
           is typical of modern hardware).  You can determine whether
           high-resolution timers are supported by checking the resolution
           returned by a call to clock_getres(2) or looking at the
           "resolution" entries in /proc/timer_list.
    
           HRTs are not supported on all hardware architectures.  (Support
           is provided on x86, arm, and powerpc, among others.)
    

    我的/proc/timer\u列表中的所有“resolution”行都是我的x86\u64系统上的1ns(公认功能强大得可笑)。

    我决定试着找出电脑上的“断点”在哪里,但放弃了110微秒的跑步:

    $ for i in 70 80 90 100 110 120 130\
     ; do echo "intervals of $i microseconds";\
     ./test $i;done
    intervals of 70 microseconds
    2 expirations, 639236 iterations
    intervals of 80 microseconds
    2 expirations, 150304 iterations
    intervals of 90 microseconds
    4 expirations, 3368248 iterations
    intervals of 100 microseconds
    4 expirations, 1964857 iterations
    intervals of 110 microseconds
    ^C
    

    90微秒运行了300万次迭代,最后失败了几次;这比第一次测试的分辨率高出22倍,所以我想说,如果硬件条件合适,10ms应该不会有什么困难(90微秒的分辨率是10毫秒的111倍。)

        3
  •  1
  •   Robert S. Barnes Antoni    16 年前

    这是一个理论。如果 HZ 对于您的系统设置为250(这是典型的),那么您有一个4毫秒的计时器分辨率。一旦您的进程被调度器调出,在您的进程获得另一个时间片之前,很可能会安排并运行许多其他进程。这也许可以解释为什么计时器的分辨率在15到21毫秒之间。解决这个问题的唯一方法是运行实时内核。

    在非实时系统上,高分辨率定时的典型解决方案基本上是忙着等待并调用select。

        4
  •  0
  •   Carl Smotricz    16 年前

    您可以(大部分)消除任务切换/调度时间。如果你有CPU电源(和电力!)为了节省时间,一个残酷但有效的解决方案将是一个繁忙的等待自旋循环。

    我曾经在windowsxp下写过这样一个系统来旋转一个步进电机,提供每秒40K次的均匀间隔脉冲,效果很好。当然,您的里程数可能会有所不同。