2010-06-26 10 views
6

Mam system, który potrzebuje co najmniej 10 milisekund dokładności dla timerów.
Poszedłem na timerfd, ponieważ idealnie do mnie pasuje, ale odkryłem, że nawet w czasie do 15 milisekund nie jest on w ogóle dokładny, albo nie rozumiem, jak to działa.Linux, dokładność timfd

Czasy I mierzone były do ​​21 mseconds na zegarowego 10 mseconds.
Połączyłem szybki test, który pokazuje mój problem.
Oto test:

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

i wykonany tak:

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 

Nawet zakładając kilka możliwych opóźnień, 15 milisekund opóźnienia brzmi dla mnie zbyt wiele.

+4

Moje dane na temat okolicy są dość zardzewiałe. Brzmi, że chcesz coś zrobić w czasie rzeczywistym. Sprawdź systemy Linux w czasie rzeczywistym. W przeciwnym razie program planujący używa większej ziarnistości, aby uniknąć wyższych narzutów. Chciałbyś uruchomić swój proces jako RT (w czasie rzeczywistym) ('man sched_setparam') i prawdopodobnie pod rootem. W przypadku normalnych procesów (np. Gier lub multimediów): (1) czekanie w ciasnej pętli lub (2) na każdym zdarzeniu czasomierza obliczyć błąd (oczekiwany v rzeczywisty czas budzenia) i wziąć go na konto przy przejściu na wewnętrzny czas stan zależny. – Dummy00001

+0

Dzięki za napiwek, ale nie uważam 20 milisekundowych interwałów za część domeny czasu rzeczywistego. Znaczy, standardowy system, który ma czekać na 10 milisekund, nigdy nie powinien czekać 21 milisekund, RT brzmi za dużo. –

+0

możesz spróbować ustawić program tak, by działał z priorytetem czasu rzeczywistego, prawdopodobnie nie będziesz potrzebował jądra RT Linuksa, mainline jest całkiem niezły w czasie rzeczywistym i biorąc pod uwagę to, że nigdy nie przegapił więcej niż jednego wygaśnięcia po prostu działającego z priorytetem RT powinno wystarczyć, abyś nigdy nie przegapił żadnego – Spudd86

Odpowiedz

12

Spróbuj zmienić to w następujący sposób, powinno to być dość kosztowne, że nigdy nie przegapisz przebudzenia, ale bądź ostrożny z nim, ponieważ uruchomienie priorytetu w czasie rzeczywistym może zablokować twoją maszynę mocno, jeśli nie zasypia, również być może trzeba wszystko skonfigurować tak, że użytkownik ma możliwość uruchamiania rzeczy z priorytetem czasu rzeczywistego (patrz /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++; 
    } 
} 

Jeśli używasz wątków należy użyć pthread_setschedparam zamiast sched_setscheduler.

W czasie rzeczywistym również nie chodzi o małe opóźnienie, chodzi o gwarancje, RT oznacza, że ​​jeśli chcesz się obudzić dokładnie raz na sekundę, to WILL, normalne planowanie nie daje tego, może zdecydować obudźcie się 100 ms później, ponieważ i tak musieliście wykonać jeszcze jedną pracę w tym czasie. Jeśli chcesz się obudzić co 10 ms i NAPRAWDĘ musisz to zrobić, to powinieneś ustawić się tak, by działał jako zadanie w czasie rzeczywistym, wtedy jądro będzie Cię budzić co 10ms bez przerwy.Chyba że zadanie w czasie rzeczywistym o wyższym priorytecie jest zajęte robieniem rzeczy.

Jeśli musisz zagwarantować, że czas przebudzenia jest dokładnie o pewien czas, nie ma znaczenia, czy jest to 1ms lub 1 sekunda, nie dostaniesz go, chyba że uruchomisz jako zadanie w czasie rzeczywistym. Istnieją dobre powody, dla których jądro zrobi to dla ciebie (oszczędzanie energii jest jednym z nich, większa przepustowość to kolejna, są inne), ale jest to w granicach jego uprawnień, ponieważ nigdy nie powiedziałeś, że potrzebujesz lepszych gwarancji. Większość rzeczy nie musi być tak dokładna, lub nie wolno jej przegapić, więc powinieneś pomyśleć o tym, czy naprawdę tego potrzebujesz.

cytując http://www.ganssle.com/articles/realtime.htm

Niełatwe zadanie czas rzeczywisty lub systemu jest jeden gdzie działalność po prostu musi być zakończone - zawsze - przez określony terminie. Ostatecznym terminem może być określony czas lub przedział czasu, lub może być nadejściem jakiegoś wydarzenia. Trudne zadania w czasie rzeczywistym zawodzą, z definicji, , jeśli przegapią taki termin.

Zauważ, że ta definicja nie zakłada żadnych założeń dotyczących lub okresu zadań. Mikrosekunda lub na tydzień - w przypadku braku terminu powoduje awarię, to zadanie ma trudne wymagania w czasie rzeczywistym.

Miękkie realtime jest prawie taka sama, z tym że brakuje terminu, podczas gdy niepożądane, nie jest koniec świata (na przykład wideo i odtwarzania dźwięku są miękkie zadań w czasie rzeczywistym, nie chcesz przegapić wyświetlania ramka lub zabrakło bufora, ale jeśli to zrobisz, to tylko chwilowa czkawka i po prostu kontynuuj). Jeśli to, co próbujesz zrobić, to "miękki" czas rzeczywisty, nie zawracałbym sobie głowy działaniem w priorytecie czasu rzeczywistego, ponieważ generalnie powinieneś dostać swoje przebudzenia na czas (lub przynajmniej blisko).

EDIT:

Jeśli nie są wyświetlane w czasie rzeczywistym jądro będzie domyślnie dać wszystkie zegary wprowadzić pewne „luzu” tak, że można go połączyć swoją prośbę, aby się obudzić z innych zdarzeń, które dzieją się w czasach bliskich ten, o który prosiłeś (to znaczy, jeśli drugie wydarzenie jest w twoim czasie "luzu", to nie obudzi cię w momencie, o który prosiłeś, ale trochę wcześniej lub później, w tym samym czasie już zamierzał zrobić coś innego, to oszczędza energię).

Za trochę więcej informacji patrz High- (but not too high-) resolution timeouts i Timer slack (należy pamiętać, że nie jestem pewien, czy któraś z tych rzeczy jest dokładnie to, co jest naprawdę w jądrze ponieważ oba te artykuły są o LKML lista dyskusyjna dyskusji, ale coś jak pierwszy naprawdę znajduje się w jądrze:

0

W zależności od tego, co inny system robi, może być nieco powolne przełączanie z powrotem do swojego zadania. Jeśli nie masz "prawdziwego" systemu czasu rzeczywistego, nie ma gwarancji, że zrobi to lepiej niż to, co widzisz, chociaż zgadzam się, że wynik jest nieco rozczarowujący.

Możesz (głównie) wyeliminować ten czas przełącznika zadań/harmonogramu. Jeśli masz do dyspozycji moc procesora (i energię elektryczną!), Brutalnym, ale skutecznym rozwiązaniem będzie pętla z długim czasem oczekiwania.

Chodzi o to, aby uruchomić program w ciasnej pętli, które stale odpytuje zegar na co jest czas, a następnie wywołuje swoje inny kod, gdy jest to właściwy moment. Kosztem tego, że twój system działa bardzo wolno na wszystko i ogrzewa twój procesor, skończysz z harmonogramem zadań, który jest w większości pozbawiony jittera.

pisałem system tak raz pod Windows XP, aby zakręcić silnik krokowy, dostarczając równomiernie rozmieszczonych impulsów do 40K razy na sekundę, i to działało dobrze. Oczywiście, twój przebieg może się różnić.

+0

Dlaczego po prostu nie dostałeś taniego mikro jak PIC lub Amtel, aby wykonać pracę nogą? W dzisiejszych czasach używałbym Ardiuno do sterowania krokowego i używałam połączenia USB, aby wysyłać do niego sygnały szybsze/wolniejsze/start/stop. – Emyr

1

Oto teoria. Jeśli dla twojego systemu HZ jest ustawione na 250 (tak jak jest to typowe), masz 4 milisekundową rozdzielczość zegara. Po zamianie procesu przez program planujący, prawdopodobnie wiele innych procesów zostanie zaplanowanych i uruchomionych, zanim proces otrzyma inny wycinek czasu. To może wyjaśnić, że widzisz rozdzielczość zegara w zakresie od 15 do 21 milisekund. Jedynym sposobem obejścia tego problemu byłoby uruchomienie jądra w czasie rzeczywistym.

Typowym rozwiązaniem dla wysokiej rozdzielczości czasu na systemach innych niż w czasie rzeczywistym jest w zasadzie ruchliwej poczekać z wezwaniem do wyboru.

+0

Nie sądzę, aby wiele dystrybucji zorientowanych na komputery ustawiło HZ na 250, a większość z nich w tych dniach włącza pracę bez tasowania, więc HZ nie powinno mieć znaczenia dla taktowania. Większość konfiguracje te dni powinny generalnie być w stanie uzyskać milisekundy ziarnistość bez jądra czasu rzeczywistego łatwo i nie trzeba łatki RT Linux, chyba że obciążenia w czasie rzeczywistym jest naprawdę bardzo mocno ... – Spudd86

+1

Mainline Linux jest systemem czasu rzeczywistego nich dzień, jest to, że wiele osób nie wie, że: P (no może to być, jeśli napisać kod, aby skorzystać z funkcji realitme) – Spudd86

3

Mam wrażenie, że Twój test jest bardzo zależny od sprzętu. Kiedy uruchomiłem Twój przykładowy program w moim systemie, wydawało się, że zawiesił się on na 1ms. Aby test był w ogóle istotny na moim komputerze , Musiałem zmienić z milisekund na mikrosekundy. (Zmieniłem mnożnik z 1_000_000 na 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 

(Nieco interesujące, że mam najdłuższe przebiegi z 16 i 47 mikrosekund, ale 17 i 48 były okropne.)

czas (7) ma jakieś sugestie dlaczego nasze platformy są tak różne:

 
    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.) 

Wszystkie „rozdzielczością” linie w moim/proc/timer_list są 1ns na moim (co prawda śmiesznie mocny) systemu x86_64 .

postanowiłem spróbować dowiedzieć się, gdzie „przełomowy” jest na moim komputerze, ale zrezygnował w biegu 110 mikrosekund:

 
$ 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 mikrosekund biegł za trzy miliony iteracji, zanim udało kilka czasy; to 22 razy lepsza rozdzielczość niż pierwszy test, więc powiedziałbym, że biorąc pod uwagę odpowiedni sprzęt, 10 ms nie powinno być w pobliżu trudnym. (90 mikrosekund to 111 razy lepsza rozdzielczość niż 10 milisekund).

Ale jeśli twój sprzęt nie zapewnia timerów dla timerów wysokiej rozdzielczości, Linux nie może ci pomóc bez uciekania się do SCHED_RR lub SCHED_FIFO. I nawet wtedy, być może inne jądro może lepiej zapewnić obsługę timera oprogramowania, którego potrzebujesz.

Powodzenia. :)

+1

tak jądro ma hrtimers więc może Cię obudzić w ciągu 1ns od kiedy zapytałem go, pogoda, czy nie będzie to jest inna historia, czasomierze mają trochę luzu „” (jeśli nie są to zadanie w czasie rzeczywistym), tak, że jądro może zmniejszyć liczba wybudzeń w sumie. (Czyli jeśli już ma coś w okresie luzu sprawi, że budzisz się w tym samym czasie, jak cokolwiek innego zobaczyć http://lwn.net/Articles/296578/) – Spudd86