Linux, timerfd точность

У меня есть система, которой нужны по крайней мере 10 mseconds точности для таймеров.
Я пошел для timerfd, поскольку он подходит мне отлично, но нашел, что даже в течение многих времен до 15 миллисекунд это не точно вообще, или это, или я не понимаю, как он работает.

Времена, которые я измерил, были до 21 mseconds на 10 mseconds таймерах.
Я соединил быстрый тест, который показывает мою проблему.
Здесь тест:

#include <sys/timerfd.h>
#include <time.h>
#include <string.h>
#include <stdint.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:");
    }
    uint64_t expirations = 0;
    int iterations = 0;
    while( res = read(timerfd, &expirations, sizeof(expirations))){
        if(res < 0){ perror("read:"); continue; }
        if(expirations > 1){
            printf("%lld expirations, %d iterations\n", expirations, iterations);
            break;
        }
        iterations++;
    }
}

И выполняемый как это:

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 задержек миллисекунд звучат слишком много для меня.

6
задан Arkaitz Jimenez 26 June 2010 в 17:38
поделиться

4 ответа

Попробуйте изменить его следующим образом, это должно гарантировать, что он никогда не пропустит пробуждение, но будьте осторожны, так как выполнение приоритета в реальном времени может сильно заблокировать вашу машину, если она не спит, также вам может потребоваться установить все устроено так, что ваш пользователь имеет возможность запускать данные с приоритетом в реальном времени (см. /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 мс в обязательном порядке. Если только задача реального времени с более высоким приоритетом не занята чем-то.

Если вам нужно гарантировать, что ваш интервал пробуждения составляет ровно какое-то время, неважно, 1 мс или 1 секунда, вы не получите его, если не запустите задачу в реальном времени. Есть веские причины, по которым ядро ​​сделает это с вами (одна из них - экономия энергии, другая - более высокая пропускная способность, есть и другие), но это вполне в его праве, поскольку вы никогда не говорили, что вам нужны более надежные гарантии. Большинство вещей на самом деле не обязательно должны быть такими точными или никогда не пропустить, поэтому вы должны хорошо подумать, действительно ли вам это нужно.

цитата из http: //www.ganssle.com / article / realtime.htm

Задача или система жесткого реального времени тот, где деятельность просто должна быть завершено - всегда - указанным крайний срок. Крайний срок может быть конкретное время или временной интервал, или может быть приход какого-то события. Жесткий задачи в реальном времени терпят неудачу по определению, если они пропустят такой срок.

Обратите внимание, что это определение не делает предположения о частоте или период выполнения задач. Микросекунда или в неделю - если пропустить срок вызывает сбой, то задача требования жесткого реального времени.

Мягкое реальное время в значительной степени то же самое, за исключением того, что отсутствие крайнего срока, хотя и нежелательно, не является концом света (например, воспроизведение видео и аудио - это мягкие задачи в реальном времени, вы не хотите пропустить отображение кадра, или закончился буфер, но если вы это сделаете, это просто кратковременная икота, и вы просто продолжите). Если то, что вы пытаетесь сделать, является «мягким» режимом реального времени, я бы не стал беспокоиться о запуске с приоритетом в реальном времени, так как вы обычно должны получать пробуждение вовремя (или, по крайней мере, близко к нему).

РЕДАКТИРОВАТЬ:

Если вы не работаете в режиме реального времени, ядро ​​по умолчанию предоставит любые таймеры, которые вы сделаете «вялым», чтобы оно могло объединить ваш запрос на пробуждение с другими событиями, которые происходят временами, близкими к одному вы просили (то есть, если другое событие находится в пределах вашего `` свободного '' времени, оно не разбудит вас в то время, когда вы просили, а немного раньше или позже, в то же время, когда оно уже собиралось сделать что-то еще, это экономит власть).

Для получения дополнительной информации см. Таймауты с высоким (но не слишком высоким) разрешением и Время ожидания таймера (обратите внимание, я не уверен, что это именно то, что действительно в ядре, поскольку обе эти статьи посвящены обсуждениям списков рассылки lkml, но что-то вроде первой действительно есть в ядре.

12
ответ дан 8 December 2019 в 14:40
поделиться

У меня такое ощущение, что ваш тест сильно зависит от оборудования. Когда я запустил ваш пример программы в своей системе, оказалось, что она зависает со скоростью 1 мс. Чтобы сделать ваш тест хоть сколько-нибудь значимым на моем компьютере, мне пришлось изменить миллисекунды на микросекунды. (Я изменил множитель с 1_000_000 на 1_000.)

$ 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 были ужасными.)

time (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_list равны 1 нс в моей (по общему признанию, смехотворно мощной) системе x86_64.

Я решил попытаться выяснить, где находится «критическая точка» на моем компьютере, но отказался от выполнения 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 микросекунд были выполнены на три миллиона итераций, прежде чем они потерпели неудачу несколько раз; это в 22 раза лучшее разрешение, чем у вашего самого первого теста, поэтому я бы сказал, что при правильном оборудовании 10 мс не должно быть трудным. (90 микросекунд - это в 111 раз лучшее разрешение, чем 10 миллисекунд.)

Но если ваше оборудование не предоставляет таймеры для таймеров высокого разрешения, то Linux не сможет вам помочь, не прибегая к SCHED_RR или SCHED_FIFO. И даже тогда, возможно, другое ядро ​​могло бы лучше предоставить вам необходимую поддержку программного таймера.

Удачи. :)

3
ответ дан 8 December 2019 в 14:40
поделиться

Вот теория. Если для параметра HZ установлено значение 250 для вашей системы (как обычно), то разрешение таймера составляет 4 миллисекунды.После того, как ваш процесс будет заменен планировщиком, вполне вероятно, что ряд других процессов будет запланирован и запущен до того, как ваш процесс получит другой временной интервал. Это может объяснить, что вы видите разрешение таймера в диапазоне от 15 до 21 миллисекунды. Единственный способ обойти это - запустить ядро ​​реального времени.

Типичное решение для синхронизации с высоким разрешением в системах, не работающих в режиме реального времени, - это, по сути, ожидание занятости с вызовом для выбора.

1
ответ дан 8 December 2019 в 14:40
поделиться

В зависимости от того, что еще делает система, переключение на вашу задачу может быть немного медленным. Если у вас нет «настоящей» системы реального времени, нет никакой гарантии, что она будет работать лучше, чем то, что вы видите, хотя я согласен, что результат немного разочаровывает.

Вы можете (в основном) исключить время переключения задач / планировщика. Если у вас есть мощность процессора (и электрическая мощность!), Жестоким, но эффективным решением будет цикл ожидания с ожиданием.

Идея состоит в том, чтобы запустить вашу программу в замкнутом цикле, который непрерывно опрашивает часы, сколько сейчас времени, а затем вызывает другой ваш код, когда время подходит. За счет того, что ваша система будет работать очень медленно для всего остального и нагреть ваш процессор, вы в конечном итоге получите планирование задач, в котором в основном отсутствует джиттер.

Я однажды написал такую ​​систему под Windows XP, чтобы вращать шаговый двигатель, подающий равномерно распределенные импульсы до 40 000 раз в секунду, и она работала нормально. Конечно, ваш пробег может отличаться.

0
ответ дан 8 December 2019 в 14:40
поделиться
Другие вопросы по тегам:

Похожие вопросы: