Sunteți pe pagina 1din 5

Laborator 4- Programare in timp real

Tema: Realizarea de aplicatii real-time in C/Posix sub QNX.


Scop: Mecanisme de sincronizare si comunicare sub QNX / POSIX.
Semafoare.

1.1 Noţiuni teoretice


Fire de execuţie - Mecanisme de sincronizare-Semafoare
În cazul firelor de execuţie POSIX avem la dispoziţie trei mecanisme de sincronizare:
mutexuri, semafoare şi variabile condiţie.

Semafoare

Semafoarele sunt obiecte de sincronizare ce reprezintă o generalizare a mutexurilor prin


aceea că salvează numărul de operaţii de eliberare (incrementare) efectuate asupra lor.
Practic, un semafor reprezintă un întreg care se incrementează/decrementează atomic.
Valoarea unui semafor nu poate scădea sub 0. Dacă semaforul are valoarea 0, operaţia de
decrementare se va bloca până când valoarea semaforului devine strict pozitivă.
Mutexurile pot fi privite, aşadar, ca nişte semafoare binare. Operaţiile care pot fi
efectuate asupra semafoarelor POSIX sunt: incrementarea (V), decrementarea blocantă
(P), decrementarea neblocantă, citirea valorii, iniţializarea şi distrugerea.
Semafoarele reprezinta un mecanism simplu de programare pentru excludere
mutuala si sincronizare pe anumite conditii, care pot fi utilizate atat pentru operatiile
executate de procese, cat si pentru cele executate de firele de executie. Operatiile care pot
fi executate pe un semafor, conform standardului POSIX, sunt de tip “post” (sem_post()
- echivalentul operatiei standard pe un semafor, signal) sau “wait”(sem_wait() echivalentul
operatiei standard pe un semafor, wait). Operatia de tip "post" realizeaza incrementarea
semaforului in timp ce operatia de tip “wait” il decrementeaza.
Semafoarele implementate in standardul POSIX sunt de tip generalizat (engl.
Counting semaphores). Astfel, un fir de executie care asteapta pe un semafor cu
valoare pozitiva nu se va bloca, dar asteptarea pe un semafor negativ duce la
blocarea firului respectiv pana cand se va executa o operatie de tip “post” (din alt
proces / fir de executie). Se pot executa mai multe decrementari ale unui semafor
inainte de a realiza o incrementare. Acest lucru permite unuia sau mai multor fire
de executie sa execute incrementari fara a se bloca.
Pentru o eficienta sporita, standardul POSIX recomanda utilizarea semafoarelor
pentru sincronizarea proceselor. De asemenea, folosind un manager de resurse, se pot
utiliza semafoarele pentru sincronizarea proceselor care ruleaza pe sisteme diferite,
conectate in retea.
Deoarece semafoarele, ca si variabilele conditionale, pot returna valori nenule
datorate unor false “treziri”, folosirea corecta presupune utilizarea unui ciclu “while” de
forma urmatoare:
while (sem wait(&s) && errno == EINTR){
nu face nimic();
}
executa_regiunea_critica();
Iniţializarea unui semafor

Pentru iniţializarea unui semafor POSIX se va apela funcţia:

int sem_init(sem_t *sem, int pshared, unsigned int value);

care va iniţializa semaforul sem la valoarea value. Parametrul pshared specifică dacă
semaforul este sau nu partajabil între procese diferite. Pe Linux această facilitate nu este
disponibilă (doar Unix / BSD). Parametrul poate avea 2 valori:

 PTHREAD_PROCESS_SHARED
 PTHREAD_PROCESS_PRIVATE - valoare suportată de toate implementările
(Linux).

Distrugerea unui semafor

Pentru a distruge un semafor şi a elibera memoria utilizată de acesta se va apela funcţia:

int sem_destroy(sem_t *sem);

Niciun fir de execuţie nu trebuie să aştepte la semafor în momentul apelului de


distrugere. În caz contrar, funcţia va întoarce codul de eroare EBUSY. Funcţia va întoarce
0 în caz de succes.

Incrementarea unui semafor

Pentru a incrementa atomic valoarea unui semafor, se va apela funcţia:

int sem_post(sem_t *sem);

Această funcţie nu blochează în nicio situaţie. Ea reprezintă operaţia V introdusă de


Dijkstra.

Decrementarea unui semafor

Pentru a suspenda execuţia până când valorea semaforului devine nenulă, se va folosi
apelul funcţiei:

int sem_wait(sem_t *sem);

Dacă valoarea semaforului este nenulă, ea va fi decrementată atomic şi funcţia va


întoarce imediat. Dacă valoarea semaforului este nulă , funcţia va suspenda firul de
execuţie apelant. Funcţia va întoarce abia când valoarea semaforului ajunge să fie
nenulă; această valoare va fi decrementată atomic cu o unitate, iar firul îşi va
continua execuţia. sem_wait reprezintă operaţia P introdusă de Dijkstra.

Decrementarea neblocantă a unui semafor


Varianta neblocantă a operaţiei anterioare este:

int sem_trywait(sem_t *sem);

Dacă valoarea semaforului este nenulă, ea va fi decrementată atomic şi funcţia va


întoarce imediat valoarea 0. În caz contrar, funcţia va întoarce imediat codul de eroare
EAGAIN.

Citirea valorii unui semafor

Pentru a afla valoarea unui semafor se va apela funcţia:

int sem_getvalue(sem_t *sem, int *sval);

Aceasta va stoca valoarea semaforului sem la adresa indicată de sval.

Aplicatie rezolvata.Utilizarea semafoarelor pentru sincronizarea firelor


de executie.
Sincronizare de tip one-to-many. Se considera o aplicatie formata din doua tipuri de
taskuri: un task (fir de executie) "producator" care produce un set de date si mai multe
taskuri (fire de executie) de tip "consumator" care consuma datele. Taskul "producator"
va putea produce un nou set de date doar dupa ce toate taskurile de tip "consumator" au
preluat datele.

Ca exemplu de utilizare, dăm o implementare a problemei producător-consumator cu


semafoare:

typedef struct {
char buf[BSIZE];
sem_t occupied;
sem_t empty;
int nextin;
int nextout;
sem_t pmut;
sem_t cmut;
} buffer_t;

buffer_t buffer;

sem_init(&buffer.occupied, 0, 0);
sem_init(&buffer.empty,0, BSIZE);
sem_init(&buffer.pmut, 0, 1);
sem_init(&buffer.cmut, 0, 1);
buffer.nextin = buffer.nextout = 0;

void producer(buffer_t *b, char item) {


sem_wait(&b->empty);
sem_wait(&b->pmut);

b->buf[b->nextin] = item;
b->nextin++;
b->nextin %= BSIZE;

sem_post(&b->pmut);
sem_post(&b->occupied);
}

char consumer(buffer_t *b) {


char item;

sem_wait(&b->occupied);

sem_wait(&b->cmut);

item = b->buf[b->nextout];
b->nextout++;
b->nextout %= BSIZE;

sem_post(&b->cmut);

sem_post(&b->empty);

return item;
}

Aplicatii suplimentare
Folosiţi directorul lin/ din arhiva de sarcini a laboratorului.

1. Intrați în directorul unsafe/.


o Inspectati continutul fisierului : unsafe.c .
o Analizati functiile increment si double_increment .
o Este necesara protejarea variabilei a daca functia increment este rulata de
mai multe thread-uri? Dar variabila b?
o Testati raspunsul dupa urmatorul scenariu:[ pentru increment ] creati
THREADS_NO thread-uri care sa ruleze functia increment si comparati
valoarea lui a cu valoarea pe care o asteptati pentru a. Pentru
double_increment procedati similar.
o Protejati variabilele , acolo unde este necesar , folosind un mutex.
o Hints
 Nu uitati sa apelati join pentru fiecare thread creat.
 Rulati programul vostru de mai multe ori.
2. Intrati in directorul increment/.
o Inspectati continutul fisierului: increment.c , executati make si rulati
executabilul obtinut.
o Programul increment.c creeaza doua fire de executie fiecare fir
incrementand variabila globala a. Aparitia unui semnal SIGSEGV va duce
la resetarea variabilei a.
o Ce problema poate sa apara in decursul rularii programului.?
o Observatie Exercitiul are insemnatate mai mult didactica .
o Hints
 Amintiti-va ce se intampla cu variabilele globale neinitializate.
3. Intrati in directorul h2o/.
o Inspectati continutul fisierului :h2o.c.
o In acest exercitiu vom crea apa. Va trebui sa punem impreuna doi atomi
de hidrogen si un atom de oxigen. Atomii sunt reprezentati de fire de
executie.Fiecare atom de hidrogen apeleaza functie hReady iar fiecare
atom de oxigen apeleaza functia oReady. Un atom de hidrogen nu se
combina ( thread-ul corespunzator lui nu se termina ) pana cand nu exista
un alt atom de hidrogen si un atom de oxigen. De asemenea un atom de
oxigen pentru a se combina are nevoie de doi atomi de hidrogen.
o Completati functiile hReady si oReady astfel incat sa obtineti apa.
 Hints
 Intr-o variabila globala putem tine numarul de atomi de
hidrogen .
 In functia hReady incrementam acest numar si ne folosim
de valoarea sa pentru a ne da seama daca trebuie sa mai
asteptam dupa inca un atom de hidrogen sau acesta deja
exista.In cazul in care avem 2 atomi de hidrogen ne mai
trebuie un atom de oxigen.
 In functia oReady asteptam 2 atomi de hidrogen dupa care
molecula de apa e gata. Ne mai ramane doar sa afisam un
mesaj si sa informam si threadurile hidrogen ca se pot
incheia.
 Folositi doua semafoare si aveti grija de variabila globala
care retine numarul de atomi de hidrogen.

S-ar putea să vă placă și