2010-06-26 2 views
6

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

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

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

+4

Мои данные о районе довольно ржавые. Кажется, вы хотите что-то делать в режиме реального времени. Проверьте Linux в режиме реального времени. В противном случае планировщик использует более длительную детализацию, чтобы избежать более высоких издержек. Вы хотели бы запустить свой процесс как RT (в реальном времени) один ('man sched_setparam') и, возможно, под root. Для нормальных процессов (например, игр или мультимедиа) вы выполняете (1) ожидание в узком цикле или (2) в каждом событии таймера вычисляете ошибку (ожидаемое фактическое время пробуждения) и берете ее в учетную запись при продвижении внутреннего времени, зависимого состояния. – Dummy00001

+0

Спасибо за подсказку, но я не считаю 20 миллисекундными интервалами частью домена реального времени. Я имею в виду, что стандартная система, которой просят дождаться 10 миллисекунд, никогда не должна ждать 21 миллисекунды, RT звучит слишком много для этого. –

+0

вы можете попробовать настроить свою программу на выполнение с приоритетом в реальном времени, вам, вероятно, не нужно использовать ядро ​​RT Linux, в наши дни основная линия довольно хороша в реальном времени и с учетом того, что она никогда не пропустила более одного срока действия только с приоритетом RT должно быть достаточно, чтобы вы никогда не пропустили один – Spudd86

ответ

12

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

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

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

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

EDIT:

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

Для немного дополнительной информации см High- (but not too high-) resolution timeouts и Timer slack (заметьте, я не уверен, если любой из этих вещей является именно то, что на самом деле в ядре, так как обе эти статьи о lkml обсуждения списка рассылки, но что-то как первый действительно находится в ядре

0

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

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

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

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

+0

Почему вы просто не получили дешевый микрофон, как PIC или Amtel, чтобы сделать работу? В настоящее время я использую Ardiuno для управления шаговым усилителем и использую USB-соединение для отправки им более быстрых/медленных/старт/стоп-сигналов. – Emyr

1

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

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

+0

Я не думаю, что многие настольные ориентированные дистрибутивы установлены от HZ до 250, и большинство из них в эти дни активируют бескабельную операцию, поэтому HZ не имеет большого значения для синхронизации. Большинство настроек в наши дни, как правило, вы можете легко получить миллисекундную гранулярность без ядра реального времени, и вам не нужны заплатки RT Linux, если ваша загрузка в реальном времени действительно очень плотная ... – Spudd86

+1

Mainline Linux - это система реального времени дней, это не так много людей знают это: P (ну, может быть, если вы напишете свой код, чтобы воспользоваться возможностями realitme) – Spudd86

3

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

время (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 является 1ns на моих (правду смешно мощные) системы 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. И даже тогда, возможно, другое ядро ​​могло бы лучше предоставить вам необходимую поддержку таймера программного обеспечения.

Удачи. :)

+1

да, у ядра есть hrtimers, чтобы он мог разбудить вас в течение 1 нс, когда вы его просили, погода или нет. WILL - это совсем другая история, таймеры имеют «слабину» (если вы не заданы в реальном времени), чтобы ядро ​​могло в целом сократить количество пробуждений. (т. е. если у него уже есть что-то делать в период провисания, он заставит вас проснуться в то же время, что и все, что см. http://lwn.net/Articles/296578/) – Spudd86