czas (12)


1.7 Funkcje systemowe zwiazane z czasem Do spisu tresci tematu 1 1.7 Funkcje systemowe zwiazane z czasem Spis tresci Typy danych Struktury danych: timeval timespec timezone task_struct tms timex Wybrane zmienne i stale jadra: przerwanie zegarowe aktualny czas strefa czasowa zuzycie CPU synchronizacja z zegarem zewnetrznym Funkcje systemowe: time() stime() gettimeofday() settimeofday() adjtimex() times() alarm() Kody zrodlowe programow demonstracyjnych Bibliografia Typy danych zwiazane z czasem Oprocz standardowych typow danych beda nam potrzebne: typedef long time_t Czas mierzony w sekundach. Logiczniej byloby zdefiniowac go jako unsigned long, uniemozliwiloby to ustawianie zlych wartosci i rozszerzyloby dostepny zakres. Byc moze autorom chodzilo o mozliwosc reprezentacji czasow sprzed 1 stycznia 1970 ? (patrz dalej) typedef long clock_t Czas mierzony w okresach przerwania zegarowego. Poniewaz "okres przerwania zegarowego" to troche dluga nazwa, odtad bedziemy mowic "takt zegara" lub po prostu "takt" (ang. tick). Konwersja z taktow na czas rzeczywisty: zazwyczaj dlugosc jednego okresu jest rowna 10 ms (1 / HZ), ale teoretycznie mozliwa jest jej modyfikacja (zmienna tick i funkcja adjtimex()). Rowniez tutaj chyba lepiej byloby uzyc unsigned long. Uwaga: liczba taktow nie zawsze jest rowna ilosci obsluzonych przerwan zegarowych - czasami z jakis powodow nalezy odlozyc obsluge przerwania i wtedy jadro moze wywolac jedna procedure obslugi dla kilku zaleglych przerwan zegarowych. Wiecej na ten temat powinno byc na stronie o przerwaniach (1.6). Uwaga: wymienione wyzej typy sa stosowane raczej sporadycznie i podane wyzej opisy mozna stosowac tylko w jedna strone, tzn. jezeli zmienna jest typu clock_t, to reprezentuje ilosc taktow, ale z faktu ze zmienna reprezentuje ilosc taktow nie wynika, ze musi byc ona typu clock_t (np. zmienna jiffies jest typu unsigned long, ale funkcja times() zwraca ja juz jako clock_t). Struktury danych zwiazane z czasem Przy opisie struktur przyjeto nastepujaca zasade: jezeli struktura wystepuje jako typ ktorejs ze zmiennych jadra, lub jako pole innej struktury spelniajacej ten warunek, opisujemy znaczenie pol. Jezeli struktura wystepuje jedynie w interfejsie funkcji systemowych (struktury tms i timex), podajemy tylko jej definicje i odnosnik do miejsca zawierajacego opis odpowiadajacych jej polom zmiennych. timeval (plik include/linux/time.h) struct timeval { long tv_sec; /* sekundy */ long tv_usec; /* mikrosekundy */ }; timespec (plik include/linux/time.h) struct timespec { long tv_sec; /* sekundy */ long tv_nsec; /* nanosekundy */ }; Struktura ta ma w przyszlosci byc wykorzystywana przez procesy czasu rzeczywistego, potrzebujace bardzo precyzyjnego okreslenia czasu. W wersji 2.0 Linuxa zaimplementowane zostaly jedynie funkcje konwersji z timespec na clock_t (timespectojiffies()) i z powrotem (jiffiestotimespec()), oraz funkcja sys_nanosleep(), pozwalajaca procesom czasu rzeczywistego zasnac na krotki okres czasu (co najwyzej 2ms, czyli 2000000ns) z duza dokladnoscia, bez zwalniania procesora (nie nastepuje zmiana kontekstu i nie trzeba czekac na wywlaszczenie nowego uzytkownika). Ze wzgledu na nikle (na razie) znaczenie praktyczne, nie bedziemy ich tutaj omawiac timezone (plik include/linux/time.h) struct timezone { int tz_minuteswest; int tz_dsttime; }; Struktura ta okresla strefe czasowa. Znaczenie pol: tz_minuteswest Przesuniecie czasowe na zachod od Greenwich, mierzone w minutach. tz_dsttime DST = Daily Saving Time = czas letni. Pole to okresla tryb zmiany czasu z letniego na zimowy. task_struct (plik include/linux/sched.h) Struktura ta opisuje proces (patrz temat 1.3). Z jej pol wymienimy tylko piec, bezposrednio zwiazane z czasem: struct task_struct { /* ... */ long utime, stime, cutime, cstime, start_time; /* ... */ }; utime Czas CPU wykorzystany przez proces w trybie uzytkownika. stime Czas CPU wykorzystany przez proces w trybie jadra (systemowym). cutime Czas CPU wykorzystany przez potomkow procesu w trybie uzytkownika. Uwaga: czasy potomkow zbierane sa przez proces macierzysty dopiero po wykonaniu funkcji wait() lub podobnej. Tak wiec w rzeczywistosci nie jest to "sumaryczny czas potomkow", lecz "sumaryczny czas potomkow ktorzy zakonczyli prace" (i nie istnieja nawet w stanie TASK_ZOMBIE) . cstime Czas CPU wykorzystany przez potomkow procesu w trybie systemowym. Uwaga jak wyzej. start_time Czas rozpoczecia wykonywania procesu (mierzony od zaladowania systemu). Wszystkie czasy podane sa w taktach (okresach) zegara, by uproscic obsluge tych zmiennych: podczas przerwania zegarowego jadro po prostu sprawdza, ktory proces sie aktualnie wykonywal i w jakim trybie, a nastepnie zwieksza odpowiednie pole o 1 (to stwierdzenie nie jest do konca scisle - patrz uwaga przy opisie typu clock_t). tms (plik include/linux/times.h) struct tms { clock_t tms_utime; clock_t tms_stime; clock_t tms_cutime; clock_t tms_cstime; }; Struktura ta wystepuje jako interfejs funkcji times(). Znaczenie pol - patrz opis task_struct. timex (plik include/linux/timex.h) struct timex { unsigned int modes; /* tablica bitowa modyfikacji */ long offset; /* roznica faz [us] */ long freq; /* roznica czestotliwosci [ppm] */ long maxerror; /* maksymalny blad [us] */ long esterror; /* szacowany blad [us] */ int status; /* status synchronizacji */ long constant; /* stala pll */ long precision; /* (RO) dokladnosc zegara [us] */ long tolerance; /* (RO) stand odchyl czestotliwosci [ppm] */ struct timeval time; /* (RO) aktualny czas */ long tick; /* dlugosc taktu [us] */ long ppsfreq; /* (RO) (pps) czestotliwosc sygnalu */ long jitter; /* (RO) (pps) ? */ int shift; /* (RO) (pps) ? */ long stabil; /* (RO) (pps) stabilnosc sygnalu [ppm] */ long jitcnt; /* (RO) (pps) ? */ long calcnt; /* (RO) (pps) czas kalibrowania */ long errcnt; /* (RO) (pps) blad kalibrowania */ long stbcnt; /* (RO) (pps) odchylenia anormalne */ int :32; int :32; int :32; int :32; int :32; int :32; int :32; int :32; int :32; int :32; int :32; int :32; }; Struktura ta jest wykorzystywana w interfejsie funkcji adjtimex(), sluzacej do synchronizacji zegara z zegarem zewnetrznym. Wszystkie pola, z wyjatkiem modes, maja swoje odpowiedniki w zmiennych jadra, najwazniejsze sa omowione w rozdziale Zmienne - synchronizacja z zegarem zewnetrznym. Pola z komentarzem (RO) sa przeznaczone tylko do odczytu (Read Only), pola z komentarzem (pps) sa wykorzystywane tylko wtedy, gdy do synchronizacji uzywamy urzadzenia generujacego sygnal PPS. modes Tablica bitowa okreslajace ktore z pol chcemy modyfikowac. Tylko superuser ma prawo modyfikowac jakiekolwiek pola, i nawet on nie moze modyfikowac wszystkich. Pola niemodyfikowalne sa zaznaczone wyzej jako (RO). Dopuszczalne modyfikacje: #define ADJ_OFFSET 0x0001 /* roznica faz */ #define ADJ_FREQUENCY 0x0002 /* roznica czestotliwosci */ #define ADJ_MAXERROR 0x0004 /* maksymalny blad */ #define ADJ_ESTERROR 0x0008 /* szacowany blad */ #define ADJ_STATUS 0x0010 /* status synchronizacji */ #define ADJ_TIMECONST 0x0020 /* stala PLL */ #define ADJ_TICK 0x4000 /* dlugosc taktu */ #define ADJ_OFFSET_SINGLESHOT 0x8001 /* dozwolona roznica faz * * wieksza niz pol sekundy */ Dodatkowe ograniczenia Pole tick mozna modyfikowac co najwyzej o 10% wzgledem wartosci standardowej (jadro zaklada, ze jezeli jego sekunda i sekunda zegara zewnetrznego roznia sie o wiecej niz 10%, to cos jest bardzo nie w porzadku i precyzyjna synchronizacja jest w ogole bez sensu). Proba przekroczenia tego limitu powoduje blad EINVAL. Pole offset jest ograniczone przez wartosc stalej MAXPHASE. Jezeli podamy wartosc spoza zakresu -MAXPHASE<offset<+MAXPHASE, i w polu modes nie zaznaczymy ADJ_OFFSET_SINGLESHOT, to rowniez wystapi blad EINVAL. Wybrane zmienne i stale jadra Przerwanie zegarowe #define HZ 100 Czestotliwosc przerwania zegarowego w herzach. long tick = 1000000 / HZ; Dlugosc taktu, czyli okresu przerwania zegarowego w mikrosekundach. Standartowo rowny 10000 us = 10 ms, moze byc nieznacznie modyfikowany funkcja adjtimex(). Aktualny czas volatile struct timeval xtime; Aktualny czas z dokladnoscia do mikrosekundy, znaczenie pol - patrz opis struktury timeval. Jest to tak zwany czas uniwersalny (UTC = Coordinated Universal Time) - czas ktory minal od polnocy (00:00) 1 stycznia 1970 roku czasu GMT. Konwersja do czasu lokalnego i ewentualna zmiana z zimowego na letni dokonywana jest juz na poziomie funkcji uzytkownika, jadro zawsze pracuje w czasie uniwersalnym. unsigned long volatile jiffies = 0; Czas od momentu zaladowania systemu w taktach zegara. #define CURRENT_TIME (xtime.tv_sec) Czas UTC z dokladnoscia do sekundy (makro uzywane miedzy innymi przez funkcje time()). Strefa czasowa struct timezone sys_tz = { 0, 0}; Strefa czasowa w ktorej unieszczony jest system, znaczenie pol - patrz opis struktury timezone. Standartowo czas zimowy Greenwich, moze byc modyfikowany funkcja settimeofday(). W zasadzie zmienna ta nie jest wykorzystywana przez jadro - jest to tylko informacja dla uzytkownika; wyjatek zostanie opisany przy okazji funkcji settimeofday(). Zuzycie CPU struct task_struct task[NR_TASKS]; Tablica procesow. Zawiera miedzy innymi zuzycie czasu CPU przez poszczegolne procesy - patrz opis struktury task_struct i funkcji times. Synchronizacja z zegarem zewnetrznym Jezeli dysponujemy zegarem zewnetrznym, mozemy, za pomoca funkcji adjtimex(), sprobowac zsynchronizowac z nim nasz zegar wewnetrzny. Zegary uwazamy za zsynchronizowane, gdy "wybijaja" sekundy jednoczesnie (oczywiscie z pewna ustalona tolerancja). Niestety, sprawna synchronizacja wymaga duzej ilosci dodatkowych zmiennych i dosyc skomplikowanego algorytmu (autorstwa Davida Mills'a). Ze wzgledu na znaczna objetosc, ograniczymy sie tutaj do pobieznego omowienia wybranych zmiennych: int time_state = TIME_ERROR; Status synchronizacji. Gdy nie ma z czym synchronizowac, lub zegar jest rozstrojony, time_state = TIME_ERROR (lub TIME_BAD w starszych wersjach). Gdy jest synchronizowany, time_state = TIME_OK. Pozostale wartosci dotycza sytuacji zwiazanych z wstawianiem / usuwaniem dodatkowej sekundy. int time_status = STA_UNSYNC; Tablica bitowa zawierajaca dodatkowe informacje na temat statusu, oraz polecenia dla zegara. Wybrane bity z tej tablicy (oznaczone RO) dotycza statusu sprzetu i przeznaczone sa tylko do czytania): #define STA_INS 0x0010 /* wstaw dodatkowa sekunde */ #define STA_DEL 0x0020 /* pomin jedna sekunde */ #define STA_UNSYNC 0x0040 /* zegar nie jest synchronizowany */ #define STA_PPSSIGNAL 0x0100 /* (RO) dostepny jest sygnal PPS */ #define STA_CLOCKERR 0x1000 /* (RO) wystapil sprzetowy blad zegara */ long time_precision = 1; Dokladnosc zegara (w mikrosekundach). #define MAXPHASE 512000L Maksymalna roznica w fazie pomiedzy zegarami, rowna 512000us, czyli w przyblizeniu pol sekundy. long time_offset = 0; Aktualne przesuniecie w fazie wzgledem zegara zewnetrznego (w mikrosekundach). Jadro bedzie staralo sie uzgodnic fazy, przesuwajac kolejne przerwania zegarowe o niewielka wartosci (time_adjust_step), odpowiednio zmniejszajac wartosc time_offset. Z oczywistych wzgledow ograniczone przez MAXPHASE: -MAXPHASE<time_offset<+MAXPHASE int tickadj = 500/HZ; Gorne ograniczenie na wartosc time_adjust_step (patrz nizej). W sytuacji normalnej (tick = 1000000/HZ), oznacza to roznice pomiedzy kolejnymi okresami nie wieksze niz 1%. long time_adjust_step = 0; Dokonywana przez jadro, wyliczana na nowo przy kazdym przerwaniu, modyfikacja dlugosci taktu (w mikrosekundach). Uwaga: nie nalezy jej mylic z mozliwoscia zewnetrzej modyfikacji dlugosci taktu (wartosci zmiennej tick). Pierwsza jest stosowana, gdy nalezy uzgodnic czestotliwosci zegarow, druga - gdy oba zegary juz pracuja z mniej wiecej ta sama czestotliwoscia, ale nalezy jeszcze uzgodnic fazy (time_offset). Tak wiec w rzeczywistosci dlugosc taktu wynosi tick+time_adjust_step. long time_tolerance = MAXFREQ; Tolerancja (maksymalne odchylenie) od sredniej czestotliwosci zegara wewnetrznego wyrazona w ppm (parts per milion; 1ppm = 0.0001%). long time_freq = 0; Roznica czestotliwosci pomiedzy zegarami (w ppm). long esterror = MAXPHASE; Szacowane odchylenie wartosci time_offset od rzeczywistej roznicy faz. long maxerror = MAXPHASE; Maksymalne odchylenie wartosci time_offset od rzeczywistej roznicy faz. To nie wszystko: do synchronizacji mozemy uzyc rowniez podlaczanego przez zlacze RS232 urzadzenia generujacego sygnal PPS (pulse-per-second). Zmienne zwiazane z jego obsluga latwo rozpoznac - poprzedzone sa prefiksem pps_. Patrz takze: temat 1.6 oraz opis funkcji adjtimex(). Funkcje systemowe Algorytmy ponizej opisanych funkcji sa raczej trywialne - zazwyczaj ograniczaja sie do odczytania / zapisania wartosci odpowiednich zmiennych. Osoby zainteresowane implementacja tych funkcji zapraszamy do skorzystania z odpowiednich odnosnikow do kodu. Czytajac kod zrodlowy warto wiedziec, ze: verify_area() - to funkcja sprawdzajaca czy wywolujacy funkcje ma prawo odczytu (VERIFY_READ) lub zapisu (VERIFY_WRITE) we wskazanym obszarze. Oczywiscie, funkcja ta nie sprawdza czy proces wlasciwie zaalokowal ten obszar. cli() i sti() - to funkcje odpowiednio blokujace i odblokowywujace przerwania. Warto zauwazyc, ze zawsze gdy funkcja modyfikuje jednoczesnie pare zmiennych zwiazanych z czasem, wylacza przerwania - gdyby w trakcie modyfikacji nadeszlo np. przerwanie zegarowe, moglo by to prowadzic do niepozadanych efektow. Funkcja time() - odczytanie aktualnego czasu systemowego (z dokladnoscia do 1 sekundy). DEFINICJA: time_t time(int *tloc) WYNIK: aktualny czas mierzony w sekundach od 00:00 1.01.1970 UTC Funkcja odczytuje zawartosc zmiennej xtime.tv_sec, wykorzystujac makro CURRENT, i zwraca ja jako wynik. Dodatkowo, jezeli tloc != NULL, probuje ja zapisac pod wskazywany adres. Funkcja stime() - ustawienie aktualnego czasu systemowego (z dokladnoscia do 1 sekundy). DEFINICJA: int stime(int * tptr) WYNIK: 0 w przypadku sukcesu -1, gdy blad: errno = EPERM (wywolujacy nie jest superuserem) Funkcja sprawdza czy wywolujacy funkcje jest superuserem (tylko on ma prawo modyfikacji czasu systemowego), nastepnie z wylaczonymi przerwaniami modyfikuje odpowiednie zmienne: xtime.tv_sec = value; /* wartosc wskazywana przez tptr */ xtime.tv_usec = 0; time_state = TIME_ERROR; time_maxerror = MAXPHASE; time_esterror = MAXPHASE; Po takiej ingerencji, system uwaza zegar za rozstrojony (TIME_ERROR). Funkcja gettimeofday() - odczytanie aktualnego czasu i strefy czasowej DEFINICJA: int gettimeofday(struct timeval * tv, struct timezone * tz) WYNIK: 0 Dzialanie: if(tv != NULL) skopiuj zawartosc zmiennej xtime pod adres wskazywany przez tv; if(tz != NULL) skopiuj zawartosc zmiennej sys_tz pod adres wskazywany przez tz; Funkcja settimeofday() - ustawienie aktualnego czasu i strefy czasowej DEFINICJA: int settimeofday(struct timeval * tv, struct timezone * tz) WYNIK: 0 w przypadku sukcesu -1, gdy blad: errno = EPERM (wywolujacy nie jest superuserem) Dzialanie: static int firsttime = 1; if(wywolujacy nie jest superuserem) zwroc blad EPERM; if(tv != NULL) skopiuj zawartosc struktury wskazywanej przez tv na zmienna xtime; if(tz != NULL) { skopiuj zawartosc struktury wskazywanej przez tz na zmienna sys_tz; if(firsttime) { firsttime = 0; if(tv == NULL) { zablokuj przerwania; xtime.tv_sec += sys_tz.tz_minuteswest * 60; odblokuj przerwania; } } } Od dzialania oczywistego widzimy tutaj jeden wyjatek. Jezeli przy pierwszym ustawianiu strefy czasowej po zaladowaniu systemu nie podamy aktualnego czasu, jadro interpretuje to nastepujaco: do tej pory zegar chodzil w czasie lokalnym, i nalezy go niezwlocznie przestawic na UTC. Jest to zreszta jedyna sytuacja w ktorej strefa czasowa ma jakikolwiek wplyw na dzialanie lub zmienne jadra. Funkcja adjtimex() - odczytanie lub ustawienie parametrow synchronizacji z zegarem zewnetrznym DEFINICJA: int adjtimex(struct timex * buf) WYNIK: status synchronizacji w przypadku sukcesu TIME_OK - zegar jest synchronizowany TIME_ERROR - zegar nie jest synchronizowany -1, gdy blad: errno = EPERM (tylko superuser moze modyfikowac) EINVAL (niedozwolona wartosc jednego z pol) Funkcja modyfikuje wskazane przez pole buf->modes zmienne na wartosci okreslone przez odpowiednie pola. Po dokonaniu wszystkich modyfikacji funkcja uaktualnia zawartosc struktury wskazywanej przez buf, i zwraca status synchronizacji. Kliknij tutaj, by obejrzec zawartosc struktury timex i dopuszczane modyfikacje, oraz tutaj, by poczytac wiecej o synchronizacji. Funkcja times() - odczytanie danych o zuzyciu czasu procesora przez proces i jego potomkow DEFINICJA: clock_t times(struct tms * tbuf) WYNIK: liczba taktow od momentu zaladowania systemu Funkcja kopiuje zawartosc odpowiednich pol struktury task_struct, wskazywanej przez wskaznik current (aktualnie wykonujacy sie proces) pod adres wskazywany przez tms. Patrz opis struktur tms i task_struct. Jako wynik zwraca zawartosc zmiennej jiffies, czyli czas od momentu zaladowania systemu mierzony w taktach zegara. Funkcja alarm() - ustawienie (i/lub ew. wylaczenie) sygnalu pobudki. DEFINICJA: unsigned int alarm(unsigned int seconds) WYNIK: liczba sekund pozostala do wywolania poprzedniego alarmu 0, gdy nie bylo takowego Efektem wywolania tej funkcji jest: Wylaczenie poprzedniego alarmu. Czas pozostaly do jego wywolania, zaokraglony w gore do pelnych sekund zostanie zwrocony jako wynik funkcji (w gore, by odroznic przypadek, gdy do wywolania alarmu zostalo mniej niz sekunda, od przypadku gdy alarmu w ogole nie bylo). Jezeli seconds>0, ustawienie nowego alarmu. Po uplywie odpowiedniej ilosci sekund, jadro wysyla do procesu sygnal pobudki (SIGALRM). Jak taki sygnal przechwycic i wykorzystac ? Zapraszamy do tematu 1.5... Kody zrodlowe programow demonstracyjnych Teksty trzech prostych programow w C demonstrujacych dzialanie funkcji i pozwalajacych na obejrzenie aktualnego stanu odpowiednich zmiennych jadra: gettimeofday() times() adjtimex() Uwaga: kompilujac times.c zmien nazwe, lub wywoluj program wynikowy przez ./times, istnieje bowiem rowniez polecenie shella o tej samej nazwie. Bibliografia Pliki zrodlowe Linuxa: include/linux/sched.h (definicje stalych, struktur i makr) include/linux/time.h include/linux/times.h include/linux/timex.h include/asm/param.h (czestotliwosc pracy zegara) kernel/sched.c (implementacja funkcji alarm(), inicjacja wiekszosci zmiennych) kernel/sys.c (implementacja funkcji times()) kernel/time.c (implementacje pozostalych funkcji systemowych) Autor: Aleksander Buczynski

Wyszukiwarka

Podobne podstrony:
12 CZAS ZEGAROWY
248 12
Biuletyn 01 12 2014
12 control statements
Rzym 5 w 12,14 CZY WIERZYSZ EWOLUCJI
12 2krl
Bob Leman Czas robaka
Fadal Format 2 (AC) B807 12

więcej podobnych podstron