Oprocz standardowych typow danych beda nam potrzebne:
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).
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.
(plik include/linux/time.h)
struct timeval { long tv_sec; /* sekundy */ long tv_usec; /* mikrosekundy */ };
(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
(plik include/linux/time.h)
struct timezone { int tz_minuteswest; int tz_dsttime; };
Struktura ta okresla strefe czasowa. Znaczenie pol:
(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; /* ... */ };
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).
(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.
(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.
#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
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:
#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 */
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().
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:
- 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.
- 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).
- 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;
- 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.
- 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.
- 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.
- 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:
Teksty trzech prostych programow w C demonstrujacych dzialanie funkcji i pozwalajacych na obejrzenie aktualnego stanu odpowiednich zmiennych jadra:
Uwaga: kompilujac times.c zmien nazwe, lub wywoluj program wynikowy przez ./times, istnieje bowiem rowniez polecenie shella o tej samej nazwie.