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 ZEGAROWY248 12Biuletyn 01 12 201412 control statementsRzym 5 w 12,14 CZY WIERZYSZ EWOLUCJI12 2krlBob Leman Czas robakaFadal Format 2 (AC) B807 12więcej podobnych podstron