Sunteți pe pagina 1din 33

PROGRAMAREA APLICATIILOR IN TIMP REAL

Curs 10

Planificarea taskurilor pe conditie de timp


(continuare)

Contoare de timp (timere)

Cuprins curs 10:

timere notificare prin intermediul pulsurilor

Timere:

timerele POSIX furnizeaza un mecanism prin care un fir de executie poate fi anuntat ca a expirat un anumit interval de timp (timeout) masurat cu un anumit ceas necesita utilizarea mecanismului de transmitere de semnale al sistemului de operare conform modelului de timere POSIX un timer poate expira:

la o data exprimata in valoare absoluta la o data exprimata in valoare relativa (e.g. n nanosecunde din acest moment) ciclic (i.e. la fiecare n nanosecunde)

Timere:

Apeluri de sistem pentru timere sub QNX Programarea Aplicatiilor in Timp Real. Teorie si Practica", tabelul 6.5, pag. 177

Timere:

timerele sunt considerate a fi o sursa de generare de evenimente in sistem, din care cauza apeleaza la sistemul de livrare a evenimentelor asociat nucleului

o aplicatie poate cere ca orice eveniment suportat de microkernelul Neutrino sa-i fie livrat la expirarea unui timeout din aceasta cauza, expirarea unui timer poate fi semnalizata cu un semnal SIGALRM (asociat functiei alarm()) sau cu orice alt semnal POSIX de timp-real

Timere:

pentru aplicatiile de timp real sunt utile doua tipuri de timere:

one-shot. Este un timer armat initial cu o valoare de expirare, relativ la momentul curent sau absolut (in functie de o baza de timp). Expira o singura data si apoi este dezarmat; periodic. Este un timer armat initial cu o valoare de expirare, relativ la momentul curent sau absolut, si cu un interval de repetitie. La expirarea intervalului initial, timer-ul este reincarcat cu intervalul de repetitie si continua sa numere

un timer QNX este de fapt o structura de date care trebuie sa fie creata, utilizata o data sau de mai multe ori, si apoi distrusa

Crearea timerelor:

principalele structuri de date pentru manipularea timerelor: Programarea Aplicatiilor in Timp Real. Teorie si Practica", tabelul 6.6, pag. 178

Crearea timerelor:

principalele functii pentru manipularea timerelor: "Programarea Aplicatiilor in Timp Real. Teorie si Practica", tabelul 6.6, pag. 178

Crearea timerelor:

Setarea timerelor:

timer-ul se armeaza prin asocierea unui timp de alarmare si (optional) a unei perioade de repetitie timpul de alarmare poate fi setat relativ (la momentul apelului) sau absolut de exemplu, un timer care va suna prima data dupa o secunda si apoi repetat, la fiecare 50 ms, se instaleaza astfel:

// seteaza parametrul timer-ului la 1 secunda, 0 nanosecunde timer.it_value.tv_sec = 1; timer.it_value.tv_nsec = 0; // seteaza parametrii pentru repetarea automata a timer-ului la 50ms timer.it_interval.tv_sec = 0; timer.it_interval.tv_nsec = 50000000; timer_settime(tid, 0, &timer, NULL);

Setarea timerelor:

timerul poate fi setat sa sune o singura data alegand it_interval

= 0:

timer_t tid; ... timer.it_value.tv_sec = 1; timer.it_value.tv_nsec = 0; timer.it_interval.tv_sec = 0; timer.it_interval.tv_nsec = 0; ... timer_settime(tid, 0, &timer, NULL);

Setarea timerelor:

timerul poate fi dezactivat alegand it_value

= 0:

timer_t tid; ... timer.it_value.tv_sec = 0; timer.it_value.tv_nsec = 0; timer.it_interval.tv_sec = 0; timer.it_interval.tv_nsec = 50000000; ... timer_settime(tid, 0, &timer, NULL);

Planificarea periodica a taskurilor prin intermediul timerelor:

posibilitatea de a planifica un timer pentru activare periodica este utila pentru planificarea periodica a firelor de executie un timer este o sursa periodica de evenimente care poate fi utilizata pentru a forta un thread sa se activeze la anumite momente de timp, dupa care sa intre in sleep pana la urmatorul eveniment thread-ul va programa timer-ul o singura data si apoi va raspunde la aparitia periodica a evenimentelor un timer este asemanator unui ceas cu alarma care face o numaratoare inversa pe un anumit interval, semnalizand expirarea acestuia spre deosebire de sleep(), un timer nu suspenda procesul (thread-ul) apelant

Planificarea ciclica a taskurilor prin intermediul timerelor:

un fir de executie isi poate seta ciclic un timer, asteptand un semnal pentru care nu are un handler pentru setarea timer-ului se poate folosi semnalul SIGALRM.

OBSERVATIE:

deoarece semnalele sunt tratate global In cadrul unui proces POSIX, ele nu sunt legate de un anumit thread din cadrul procesului. De aceea, pentru ca acest exemplu sa functioneze, nici un alt thread nu trebuie sa foloseasca semnalul SIGALRM pentru (orice) alt scop sunt necesare doua structuri de lucru, sigevent si itimerspec

Ceasuri si timere:

cum primeste kernelul intreruperi periodice de la hardware si ce face cu aceste intreruperi?

OBSERVATIE:

kernelul poate replanifica procese si /sau fire de executie in trei situatii: la aparitia unei intreruperi hardware cand se face un apel de sistem (system call) la declararea unei exceptii (fault) exemplu: daca un thread face sleep(), biblioteca C contine cod care in final face apel la o functie (apel) de sistem acest apel scoate thread-ul din coada RUNNING si porneste un timer (countdown) in acelasi timp kernelul primeste intreruperi hardware la intervale egale de la ceasul hardware (de exemplu, la 10 ms) aceste intreruperi sunt tratate de catre rutina de tratare a intreruperilor de ceas a kernelului (ISR)

Ceasuri sursa intreruperilor de ceas:

sursa circuite hardware (capitolul Clocks, Timers, and Getting a Kick Every So Often):

Help QNX Momentics IDE QNX Neutrino Realtime Operating System > Getting Started with QNX Neutrino: A Guide for Realtime Programmers

Timere:

tipuri de timere

relative = perioada de expirare (timeout) este specificata relativ la momentul curent (peste ...) absolute = momentul de expirare (timeout) este specificat exact la momentul dorit (la data de de ...) one-shot = suna o singura data periodic = suna periodic, la intervalul specificat

cum verificam ca a expirat timpul? scheme de notificare OBSERVATIE: daca se face un simplu delay, la expirarea timpului de asteptare procesul sau thread-ul este trecut inapoi in starea READY (gata de executie) in cazul timerelor periodice sau one-shot, se poate: trimite un puls trimite un semnal crea un thread

Pulsuri:

spre deosebire de serviciile sincrone Send/Receive/Reply, nucleul Neutrino suporta si mesaje fara blocare de dimensiune fixa = pulsuri pulsurile sunt mesaje scurte formate din 8 biti de cod si 32 biti de date sunt folosite adesea drept mecanism de notificare n cadrul rutinelor de tratare a ntreruperilor de asemenea, permit serverelor sa notifice clientii, fara a-i bloca se foloseste acelasi mecanism de transmitere de mesaje prin intermediul canalelor de comunicatie sub QNX

Pulsuri:

Interfata C /POSIX pentru mesaje de tip puls "Programarea Aplicatiilor in Timp Real. Teorie si Practica", tabelul 4.3, pag. 144

Scheme de notificare:

in orice schema de notificare se foloseste o structura sigevent detalii ale definitiilor se gasesc in <sys/siginfo.h>

struct sigevent { int sigev_notify; // tipul de notificare union { int sigev_signo; int sigev_coid; int sigev_id; void (*sigev_notify_function) (union sigval); }; union sigval sigev_value; union { struct { short sigev_code; short sigev_priority; }; pthread_attr_t *sigev_notify_attributes; };

};

Scheme de notificare tipul de notificare:

sigev_notify: determina tipul de notificare selectat, adica:


SIGEV_PULSE se va transmite un puls SIGEV_SIGNAL, SIGEV_SIGNAL_CODE, sau SIGEV_SIGNAL_THREAD se va transmite un semnal SIGEV_UNBLOCK not used in this case; used with kernel timeouts SIGEV_INTR not used in this case; used with interrupts SIGEV_THREAD creaza un thread

deoarece folosim aceasta structura pentru timere, ne intereseaza doar SIGEV_PULSE, SIGEV_SIGNAL si SIGEV_THREAD

Scheme de notificare notificare prin pulsuri:

se trimite un puls la expirarea timer-ului se seteaza campul sigev_notify la SIGEV_PULSE se transmite informatie suplimentara, astfel:

Scheme de notificare notificare prin semnale:

Utilizarea timerelor:

operatii necesare pentru utilizarea timerelor:


crearea unui obiect de tip timer se decide schema de notificare (semnal, puls sau creare thread) si se creaza structura de notificare (struct sigevent) se alege tipul de timer (relativ / absolut sau one-shot / periodic) se porneste timerul

Crearea timerelor:

timerul se creaza cu functia timer_create()

#include <time.h> #include <sys/siginfo.h> int timer_create (clockid_t clock_id, struct sigevent *event, timer_t *timerid);

argumentul clock_id specifica baza de timp cu care va fi creat acest timer

OBSERVATIE: standardul POSIX spune ca se pot folosi mai multe baze de timp, pe diferite platforme, dar orica platforma trebuie sa suporte cel putin baza de timp data de CLOCK_REALTIME sub Neutrino pot fi alese 3 baze de timp: CLOCK_REALTIME CLOCK_SOFTTIME CLOCK_MONOTONIC

Ce fel de timer:

se specifica cu o combinatie de argumente in functia timer_settime(), utilizata pentru pornirea timer-ului:

#include <time.h> int timer_settime (timer_t timerid,int flags, struct itimerspec *value, struct itimerspec *oldvalue);

argumentul timerid este valoarea returnata de functia timer_create() intr-o aplicatie pot fi create mai multe timere si apoi se apeleaza timer_settime() individual pentru fiecare pentru setare si pornire argumentul flags specifica tipul de timer

daca este setat la valoarea constanta TIMER_ABSTIME, timerul va fi de tip absolut daca este setat la zero, timerul va fi considerat relativ la momentul curent

Cum se specifica timpul:

cu ajutorul celor doua structuri definite in <time.h>

struct timespec { long tv_sec, tv_nsec; }; struct itimerspec { struct timespec it_value, it_interval; };

struct itimerspec are doi membri: it_value the one-shot value it_interval the reload value

Setarea timerelor:

timer-ul se armeaza prin asocierea unui timp de alarmare si (optional) a unei perioade de repetitie timpul de alarmare poate fi setat relativ (la momentul apelului) sau absolut de exemplu, un timer care va suna prima data dupa o secunda si apoi repetat, la fiecare 50 ms, se instaleaza astfel:

timer_t tid; // seteaza parametrul timer-ului la 1 secunda, 0 nanosecunde timer.it_value.tv_sec = 1; timer.it_value.tv_nsec = 0; // seteaza parametrii pentru repetarea automata a timer-ului la 50ms timer.it_interval.tv_sec = 0; timer.it_interval.tv_nsec = 50000000; timer_settime(tid, 0, &timer, NULL);

Exemplu - executia periodica prin intermediul timerelor notificare prin pulsuri:


#include #include #include #include <stdio.h> <time.h> <sys/netmgr.h> <sys/neutrino.h> _PULSE_CODE_MINAVAIL

#define MY_PULSE_CODE

typedef union { struct _pulse pulse; } tip_mesaj;

Exemplu - executia periodica prin intermediul timerelor notificare prin pulsuri (cont):
main(){ timer_t tid; struct itimerspec timer; struct sigevent event; int chid, rcvid; tip_mesaj msg; chid = ChannelCreate(0); // pentru notificare se trimite un puls pe un canal de comunicatie event.sigev_notify = SIGEV_PULSE; event.sigev_coid = ConnectAttach(0, 0, chid, _NTO_SIDE_CHANNEL, 0); event.sigev_priority = getprio(0); event.sigev_code = MY_PULSE_CODE; ...

// identicatorul timer-ului // o specificatie de timer // o specificatie de eveniment

Exemplu - executia ciclica a threadurilor prin intermediul timerelor notificare prin pulsuri (cont):

timer_create(CLOCK_REALTIME, &event, &tid); // timerul este setat la 1.5 secunde timer.it_value.tv_sec = 1; timer.it_value.tv_nsec = 500000000; /* 0.5 secunde */

// intervalul de reprogramare este de 1.5 secunde timer.it_interval.tv_sec = 1; timer.it_interval.tv_nsec = 500000000; timer_settime(tid, 0, &timer, NULL); while(1) { rcvid = MsgReceive(chid, &msg, sizeof(msg), NULL); if (rcvid == 0) /* s-a primit un puls */ if (msg.pulse.code == MY_PULSE_CODE) printf("S-a primit un puls de la timer ... \n"); /* 0.5 secunde */

Exemplu - executia ciclica a threadurilor prin intermediul timerelor notificare prin pulsuri (cont):

rezultatul executiei programului: primit primit primit primit primit un un un un un puls puls puls puls puls de de de de de la la la la la timer timer timer timer timer ... ... ... ... ...

S-a S-a S-a S-a S-a

Atentie:

intarzierea unui proces pe un anumit interval de timp: GRESIT: constructie software de tip busy-wait un task asteapta trecerea unui interval de 10 secunde intr-o organizare de tip busy-wait
start := clock; // citeste ceasul loop exit when (clock start) > 10.0; end loop;

corect: se folosesc functiile specificate pentru asteptare, care elibereaza CPU cind taskul se blocheaza asteptand trecerea intervalului de asteptare (sleep, delay, etc)