[ Pobierz całość w formacie PDF ]
.Linux I/O port programming mini-HOWTO: Mierzenie czasu z dużą dokładnościąNastępna stronaPoprzednia stronaSpis treści4.Mierzenie czasu z dużą dokładnością4.1 OpóźnieniaPrzede wszystkim trzeba stwierdzić że z powodu wielozadaniowej natury Linuxanie można zagwarantować że procesy w trybie użytkownika będą mieć dokładnąkontrolę czasu.Twój proces może zostać rozpoczęty w każdej chwili i może mu tozająć od 10 milisekund do kilku sekund (na bardzo obciążonych systemach).Jednakże,dla większości aplikacji używających portów I/O nie ma to większego znaczenia.Aby zminimalizować to zjawisko możesz nadać swemu procesowi wyższy priorytet(zobacz nice(2) w podręczniku man) lub używać zarządzania procesami w czasie rzeczywistym(patrz niżej)Jeśli chcesz berdziej precyzyjnie odmierzać czas niż pozwalają ci na to procesyw trybie użytkownika, możesz skorzystać ze wsparcia dla procesów użytkownikaw czasie rzeczywistym.Jądra 2.x.x mają niewielkie wsparcie dla czasu rzeczywistego;zobacz sched_setscheduler(2) w podręczniku man.Jest również specjalne jądro które mazaawansowane wsparcie dla czasu rzeczywistego.Zobaczhttp://luz.cs.nmt.edu/~rtlinuxaby uzyskać więcej informacji na ten temat.Opóźnianie za pomocą funkcji sleep() i usleep()Zacznijmy teraz od łatwiejszych wywołań.Jeśli chcesz opóźnień rzędu sekund tonajpewniej jest użyć sleep().Dla opóźnień rzędu przynajmniej dziesiątek milisekund(10ms wydaje się być najmniejszym możliwym opóźnieniem) powinieneś użyć usleep().Funkcje te oddają czas innym procesom a więc czas procesora nie jest marnowany.Zajrzyjdo sleep(3) i usleep(3) w podręczniku man po szczegóły dotyczące tych funkcji.Jeśli potrzebujesz opóźnień mniejszych niż 50 milisekund (w zależności od prędkościprocesora i samego komputera oraz obciążenia systemu) oddawanie czasu procesorazajmuje zbyt wiele czasu ponieważ linuxowy zarządca procesów (w architekturze x86)zwykle zabiera około 10-30 milisekund zanim zwróci kontrolę do twojego procesu.Z tego powodu dla małych opóźnień usleep() zwykle opóźnia o trochę więcej czasu niżpodajesz w parametrze a przynajmniej około 10 ms.Opóźnianie za pomocą funkcji nanosleep()W serii jąder 2.x znajduje się nowa funkcja systemowa nanosleep()(zobacz nanosleep(2) w podręczniku man) która pozwala opóźniać lub zatrzymywaćproces na krótkie okresy czasu (kilka mikrosekund lub więcej).Dla uzyskania opóźnień <=2ms, jeśli (i tylko wtedy) twój proces jest ustawiony na zarządzanie z wsparciem dla czasu rzeczywistego(poprzez sched_setscheduler()), nanosleep() używa pętli opóźniającej; w przeciwnym razie zatrzymujeproces tak jak usleep().Pętla opóźniająca używa udelay() (wewnętrznej funkcji jądra użtwanej przez wiele jego modułów)a długość pętli jest obliczana na podstawie wartości BogoMips(prędkość tego rodzaju pętli opóźniającej jest jedną z rzeczy którą BogoMips dokładniemierzy) Zobacz /usr/include/asm/delay.h po szczegóły odnośnie działania tego mechanizmu.Opóźnianie za pomocą operacji I/O na porcieInną metodą opóźniania o niewielkie ilości mikrosekund są operacje I/O na portach.Czytanie/zapisywanie jakiegokolwiek bajtu z/do portu 0x80 (patrz wyżej jak to się robi)powinno dać opóźnienie prawie dokładnie 1 mikrosekundy bez względu na typ procesorai jego prędkość.Możesz tak robić wiele razy aby uzyskać opóźnienie rzędu kilku mikrosekund.Sposób ten nie powinien wywoływać żadnych szkodliwych efektów ubocznych na żadnym standardowymkomputerze (nawet niektóre moduły jądra go używają).W ten sposób uzyskują opóźnienie funkcje{in|out}[bw]_p() (zobacz asm/io.h).W zasadzie, instrukcje I/O na większości portów w obszarze 0-0x3ff zbierają prawiedokładnie 1 mikrosekundę, więc jeśli na przykład używasz bezpośrednio portu równoległegopo prostu wykonaj kilka razy inb() z tego portu aby uzyskać opóźnienie.Opóźnianie za pomocą instrukcji asemblerowychJeśli znasz typ procesora i prędkośc zegara w komputerze na którym będzie działałtwój program, możesz na stałe uzyskać krótsze opóźnienia poprzez zastosowanie pewnychrozkazów asemblerowych (pamiętaj jednak że twój proces może się zacząć w dowolnym momenciewobec czego opóźnienia te mogą być większe od czasu do czasu) W tabeli poniżej wewnętrznaprędkość procesora określa liczbę cykli np dla procesora 50MHz (np.486DX-50 lub 486DX2-50)jeden cykl zegara zajmuje 1/50000000 sekundy.(200 nanosekund).Instrukcja cykle zegara na 386 cykle zegara na 486nop 3 1xchg %ax,%ax 3 3or %ax,%ax 2 1mov %ax,%ax 2 1add %ax,0 2 1(Przykro mi ale niewiele wiem o Pentium.Pewnie podobnie do 486.Nie mogę znaleźć żadnejinstrukcji która zajmowała by tylko jeden cykl zegara na 386.Jeśli możesz, używajinstrukcji jednocyklowych, w przeciwnym razie architektura potokowa (pipelining)używana w nowoczesnych procesorach może dać jeszcze krótsze czasy)Rozkazy nop i xchg z tabeli powyżej nie powinny powodować żadnych skutków ubocznych.Resztamoże modyfikować rejestr znaczników ale nie powinno mieć to znaczenia bo gcc powinnoto wykryć.Użycie nop jest dobrym wyborem.Aby skorzystać z powyższego, trzeba w swoim programie wywołać funkcjęasm("instrukcja") Składnia instrukcji jest taka sama jak w tabeli powyżej.Jeślichcesz użyć kilku instrukcji w jednym wywołaniu funkcji asm rozdziel je średnikami.Na przykład asm("nop ; nop ; nop ; nop") wywoła cztery razy instrukcję nop opóźniającnasz program o 4 cykle na 486 lub Pentium (lub 12 cykli na 386)Funkcja asm() jest przez gcc tłumaczona na wstawkę w asemblerze a więc nie ma zagrożeniaprzekroczenia limitu wywołań funkcji.Opóźnienia krótsze niż jeden cykl zegara nie są możliwe w architekturze Intel i386.Instrukcja rdtsc w PentiumJeśli masz Pentium, możesz odczytać ilość cykli zegara które upłynęły od ostatniegouruchomienia komputera.Robi się to za pomocą takiego kodu w C:extern __inline__ unsigned long long int rdtsc(){unsigned long long int x;__asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));return x;}Możesz odczytywać tą wartość w celu opóźniania o dowolną ilość cykli.4.2 Mierzenie czasuDla czasów rzędu sekundy prawdopodobnie najłatwiej będzie użyć funkcji time().Dla bardziej dokładnych pomiarów: gettimeofday() jest dokładne co do mikrosekundy(ale zobacz wyżej o zarządzaniu procesami) Dla Pentium fragment kodu rdtsc powyżejjest dokładny co do cykla zegarowego.Jeśli chcesz aby twój proces dostawał jakiś sygnał po jakimś czasie, użyj settimer()lub alarm().Zajrzyj do stron podręcznika man dotyczących tych funkcji.Następna stronaPoprzednia stronaSpis treści
[ Pobierz całość w formacie PDF ]