Sunteți pe pagina 1din 35

3. Gestionarea activitilor paralele..................................................................................................................................

2
3.1. Exemple introductive.................................................................................................................................................2
3.1.1. Administrarea tamponat a intrrilor-ieirelor...................................................................................................2
3.1.2. Comanda unui proces industrial.........................................................................................................................3
3.2. Noiune de proces secvenial.....................................................................................................................................3
3.2.1. Proces unic. Context...........................................................................................................................................3
3.2.2. Relaii ntre procese............................................................................................................................................4
3.2.2.1. Mulimi de procese. Paralelism...................................................................................................................4
3.2.2.2. Concurena proceselor. Resurse virtuale.....................................................................................................5
3.2.2.3. Excludere mutual.......................................................................................................................................6
3.3. Sincronizarea proceselor...........................................................................................................................................6
3.3.1. Exprimarea i implementarea restriciilor de precedare.....................................................................................6
3.3.2. Probleme de realizare a sincronizrii.................................................................................................................7
3.3.3. Monitorul mecanism de sincronizare..............................................................................................................8
3.3.3.1. Definiii........................................................................................................................................................8
3.3.3.2. Exemple de utilizare....................................................................................................................................9
3.4. Implementarea sincronizrii....................................................................................................................................10
3.4.1. Probleme-tip.....................................................................................................................................................10
3.4.2. Administrarea unei resurse partajate................................................................................................................10
3.4.2.1. Alocarea resurselor banalizate...................................................................................................................10
3.4.2.2. Modelul cititorului i redactorului.............................................................................................................12
3.4.3. Comunicarea ntre procese...............................................................................................................................13
3.4.3.1. Modelul productorului i consumatorului...............................................................................................13
3.4.3.2. Primitive de comunicare............................................................................................................................14
3.4.3.3. Aplicaii : relaia client-server...................................................................................................................16
3.4.4. Administrarea intrrilor-ieirilor.......................................................................................................................16
3.4.4.1. Administrarea unui periferic......................................................................................................................16
3.4.4.2. Buferizarea imprimrii..............................................................................................................................17
3.4.5. Sincronizare temporal.....................................................................................................................................18
3.5. Gestionarea dinamic a proceselor..........................................................................................................................19
3.6. Sincronizarea n Windows.......................................................................................................................................21
3.6.1. Procese i fire...................................................................................................................................................21
3.6.2. Necesitatea sincronizrii...................................................................................................................................21
3.6.3. Structura mecanismului de sincronizare n Windows......................................................................................21
3.6.4. Administrarea obiectelor de sincronizare n Windows.....................................................................................22
3.6.4.1. Excluderea mutual...................................................................................................................................22
3.6.4.2. Evenimentele.............................................................................................................................................22
3.6.4.3. Semafoarele...............................................................................................................................................22
3.6.4.4. Seciunile critice........................................................................................................................................22
3.6.4.5. Protejarea accesrii variabilelor................................................................................................................23
3.6.4.6. Sincronizarea n MFC................................................................................................................................23
3.6.5. Exemplu de sincronizare n Windows..............................................................................................................23
3.6.6. Utilizarea seciunilor critice n Windows.........................................................................................................24
3.6.6.1. Structura RTL_CRITICAL_SECTION.....................................................................................................25
3.6.6.2. Funcii API pentru seciunile critice..........................................................................................................26
3.6.6.3. Clase de seciuni critice.............................................................................................................................28
3.6.6.4. Depanarea seciunilor critice.....................................................................................................................28
3.7. Exerciii la capitolul 3.............................................................................................................................................31

14.11.17 21:27 /conversion/tmp/scratch/371682329.doc p.1


3. Gestionarea activitilor paralele
Una din caracteristicile principale ale sistemelor de calcul este existena activitilor simultane. Acest capitol
prezint conceptele i utilitarele, care permit descrierea i coordonarea acestor activiti.

3.1. Exemple de activiti paralele


3.1.1. Administrarea tamponat a intrrilor-ieirelor
Relum exemplul imprimrii cu gestionarea unor zone tampon pe disc, descris n capitolul 2. Analiza acestui mod de
funcionare (fig.3.1) pune n eviden patru activiti, care pot avea loc simultan:
1) primitiva scriere_linie SL (unitatea central)
2) scriere pe disc SD (canal 1)
3) citire de pe disc CD (canal 2)
4) imprimare fizic IF (canal 3)
scriere_linie
SL SD CD IF

tm1 tm2
(memorie) (memorie)
td

(disc)
Fig.3.1. Gestionarea buferizat a unei imprimante
Aceste patru activiti sunt n mare msur autonome, or ele sunt executate pe procesoare distincte, cu programe
diferite. Ele nu sunt totui independente, deoarece acceseaz obiecte comune: dou tampoane n memorie, tm1 i tm2 i
un tampon pe disc, td. Pot fi evideniate dou tipuri de condiii, care trebuie respectate:
1) Condiii, care stabilesc posibilitatea existenei unor activiti
O nregistrare nu poate fi preluat dintr-un tampon nainte de a nu fi depozitat aici. Tampoanele au capaciti
limitate; dac un tampon este ocupat cu nregistrri, care nu au fost nc preluate, este imposibil depozitarea fr a
pierde informaii. Aciunile de depozitare i preluare sunt, deci, supuse unor condiii de posibilitate de existen,
enumerate mai jos.
Activitate Aciune Condiie
SL scriere n tm1 tm1 nu este plin
SD citire din tm1 tm1 nu este vid
SD scriere n td td nu este plin
CD citire din td td nu este vid
CD scriere n tm2 tm2 nu este plin
IF citire din tm2 tm2 nu este vid
Execuia activitilor modific veridicitatea acestor condiii: astfel, imprimarea unei linii pune n TRUE condiia
tm2 nu este plin.
O activitate, care nu poate executa o aciune, deoarece condiia asociat are valoare FALSE, trebuie s atepte, adic
execuia unei aciuni este retardat pn cnd valoarea logic a condiiei devine TRUE. n capitolul 2 a fost discutat
realizarea acestui mecanism de ateptare i continuare cu ajutorul ntreruperilor.
2) Condiii de validitate a informaiilor partajate
Dac vom examina procesul de accesare a tampoanelor, vom descoperi o alt form de interaciune, cauzat de
posibilitatea de accesare simultan de ctre dou activiti a unui amplsament de memorie. Astfel, dac SD citete
coninutul unei nregistrri din tm1 pe care SL este n curs de a o modifica, rezultatul acestei citiri risc s fie incoerent,
dac nu au fost luate msuri speciale de precauie. Problema poate fi rezolvat impunnd una din activiti, aflate n
conflict, s atepte pn cnd cealalt va termina accesarea.
Concluziile acestei prime analize:
Lucrarea imprimare tamponat este realizat prin patru activiti simultane, n mare msur autonome, care
coopereaz pentru atingerea scopului final.

n realitate, citirea i scrierea pe disc sunt executate pe acelai canal, ceea ce poate impune unele restricii privind simultaneitatea executrii lor.
Vom ignora aici aceste restricii, justificnd mai apoi acest mod de procedare.

14.11.17 21:27 /conversion/tmp/scratch/371682329.doc p.2


Executarea corect a lucrrii impune restricii logice n vederea derulrii acestor activiti. Aceste restricii pot
conduce la ntrzierea execuiei unei activiti, care este obligat s atepte producerea unui eveniment, provocat de o
alt activitate.
3.1.2. Comanda unui proces industrial
S relum exemplul din capitolul 1, n care un calculator comand un reactor chimic. Considerm dou reactoare
identice, R1 i R2, care funcioneaz n paralel. Vom examina dou soluii posibile:
1) utilizarea a dou calculatoare (cte unul pentru fiecare reactor),
2) folosirea unui singur calculator pentru comanda ambelor reactoare.
Prima variant nu prezint nimic nou n raport cu descrierea din capitolul 1. Soluia a doua cere stabilirea condiiilor
suplimentare pentru realizarea sa. Implementarea variantei doi impune verificarea posibilitii apariiei unor condiii
suplimentare. Fie P1, P2, D1, D2 segmentele procedurilor i datelor pentru comanda celor dou reactoare R1 i R2,
memoria principal avnd capacitatea necesar pentru pstrarea acestor segmente. Programele P1 i P2 sunt executate pe
rnd de procesor. Raionnd ca i n capitolul 1, concluzionm c relaia 2t<T trebuie s fie respectat. Dac acesta va fi
cazul, funcionarea reactoarelor pentru un observator extern pare identic pentru ambele soluii. Trebuie, totui s
subliniem, c soluia doi impune restricii mai severe n ceea ce privete performanele calculatorului (capacitatea
memoriei i viteza de procesare)
Vom examina mai jos modalitile de implementare a soluiei doi.
1) Partajarea procesorului
ntre dou execuii succesive ale lui P1, procesorul este utilizat de P2, ca rezultat starea sa intern (coninutul
registrelor, cuvntul de stare, contorul ordinal) va fi modificat. Pentru a permite reluarea lui P1, informaiile sale
trebuiesc salvate la terminarea fiecrei secvene de execuie i restabilite la nceperea execuiei urmtoare. Aceleai
afirmaii sunt valabile i n cazul executrii lui P2.
2) Partajarea programului
Programele P1 i P2, fiind identice, putem pstra doar o singur copie, s-i zicem P, pentru economisirea memoriei.
S examinm condiiile necesare pentru partajarea programului P:
nu este permis modificarea codului programului de execuia sa,
atunci cnd P este executat pentru reactorul Ri (i=1 sau 2), el va accesa segmentul de date Di; aceast adresare
selectiv a datelor va fi realizat de un mecanism care nu modific textul programului.
Un program, existent n exemplar unic, dar care poate fi utilizat pentru executarea mai multor activiti
independente, eventual simultane, se numete program reentrant (reenterabil). Acest mod de utilizare implic:
invariana textului programului n cursul executrii sale,
desemnarea uniform, de ctre program, a datelor proprii fiecrei activiti.
Printre altele, dac activitile partajeaz un procesor unic, informaiile despre starea acestuia (n particular, cele care
servesc la desemnarea datelor) trebuiesc salvate la fiecare comutare.
S rezumm rezultatele celui de-al doilea exemplu.
1. Au fost evideniate dou activiti logic independente: comanda reactoarelor R1 i R2. Aceste dou activiti pot
fi puse pe dou maini distincte fr s existe vre-o legtur ntre ele.
2. Considerente de economie pot impune ca aceste activiti s partajeze resurse fizice i resurse program
comune. Buna funcionare a acestei partajri restricioneaz execuia activitilor (utilizarea alternativ a
procesorului) i modul de utilizare a obiectelor partajate (salvarea contextului de executare, reentrana
programelor).
Ca i concluzie pentru ambele exemple putem evidenia dou momente:
existena unor activiti evolutive, care pot derula simultan,
existena unor relaii ntre aceste activiti: cooperare pentru executarea unor sarcini comune, concuren n
utilizarea resurselor partajate. Aceste relaii sunt determinate de specificaiile de bun funcionare. Ele se
pot traduce n restricii de execuie, care conduc la ntrzieri temporare a progresrii unei activiti.
Continuarea acestui capitol are drept scop elaborarea unui suport formal pentru aceste noiuni, introducnd
conceptele de proces i sincronizare i descriind modalitile lor de utilizare n cadrul unui sistem informatic.

3.2. Noiune de proces secvenial


Noiunea de proces este mai nti introdus ntr-un mod concret pentru cazul unui calculator care conine un
procesor unic i o memorie adresabil. Vom trece mai apoi la definiia abstract a unui proces independent de suportul
su fizic, ceea ce ne va permite introducerea problemei sincronizrii.
3.2.1. Proces unic. Context
Noiunea de proces pune la dispoziie un model pentru reprezentarea unei activiti, care rezult din executarea unui
program pe calculator. Starea calculatorului este definit de starea procesorului (coninutul registrelor) i starea
memoriei (coninutul amplasamentelor). Aceast stare este modificat de ctre procesor prin executarea instruciunilor

14.11.17 21:27 /conversion/tmp/scratch/371682329.doc p.3


programului. Fiecare executare a unei instruciuni constituie o aciune: o aciune are ca efect trecerea n timp finit a
calculatorului dintr-o stare iniial ntr-o stare final. Acest efect este descris n specificaiile instruciunii.
Executarea unei instruciuni va fi considerat indivizibil sau atomar, adic este interzis observarea sau definirea
strii mainii n timpul acestei execuii. Timpul utilizat pentru descrierea evoluiei strii este un parametru t, care poate
lua o serie de valori discrete cresctoare, corespunztoare nceputului i sfritului instruciunilor. Aceste instane sunt
numite puncte de observare, iar strile corespunztoare ale mainii sunt numite puncte observabile. Prin abuz de
limbaj, acest ultim termen desemneaz n egal msur i punctele de observare. Acestea permit s se asocieze o dat
(valoarea curent a lui t) unei stri a mainii; o atare stare datat definete un eveniment. Evenimentele permit reperarea
modificrilor strii mainii. nceputul i sfritul unei aciuni a sunt evenimente datele crora sunt notate nceput(a) i
sfrit(a); vom avea, evident, nceput(a)<sfrit(a).
Executarea unui program se traduce, deci, ntr-o suit de aciuni a1, a2,..., ai,..., cu nceput(ai)<sfrit(ai). O astfel
de suit este numit proces secvenial (sau simplu proces).
Un proces poate, deci, fi descris cu ajutorul succesiunii evenimentelor nceput(a1), sfrit(a1), nceput(a2),
sfrit(a2),... Aceast suit de stri datate ale mainii se numete traiectorie temporal (sau istorie) a procesului. Putem
de asemenea defini un proces ca o suit de activiti n sensul lui 2.1.
Mulimea informaiilor pe care aciunile unui proces le pot consulta sau modifica se numete contextul procesului.
Relund modelul de execuie secvenial introdus n 2.1, contextul unui proces rezultant din executarea unui program
conine:
1) Contextul procesorului (cuvntul de stare i registrele),
2) Un context n memorie, sau spaiul de lucru (segmente procedur, date, stiv de execuie),
3) O mulime de atribute ataate procesului i care specific diferite proprieti:
a) Nume. Numele unui proces, care servete pentru identificarea lui, este de regul, un numr intern atribuit la
crearea procesului i care permite s se ajung la reprezentarea contextului su (v.4.2).
b) Prioritate. Prioritatea proceselor permite ordonarea lor pentru alocarea procesorului (v.4.2, 4.3). Dac toate
procesele au aceeai prioritate, alocarea se face conform ordinii primul sosit, primul servit.
c) Drepturi. Drepturile unui proces specific operaiile care i sunt permise, n scopul asigurrii proteciei
informaiei (v. 5.1.4).
Traiectoria temporal a unui proces este definit de irul strilor contextului su (procesor i memorie) preluate dup
execuia fiecrei instruciuni. Prin definiie, starea restului mainii nu este modificat de execuia procesului.
3.2.2. Relaii ntre procese
Vom cerceta n continuare execuia unei mulimi de procese i interaciunea reciproc a acestora. Noiunile,
introduse n 3.2.1 pot fi extinse pentru o mulime de procese:
traiectoria temporal a unei mulimi de procese este irul evenimentelor formate de nceputurile i sfriturile
aciunilor rezultante din executarea programelor acestor procese.
contextele unor procese diferite pot avea pri comune. Dou procese, contextele crora sunt disjuncte, se
numesc independente; ele nu pot avea interaciuni reciproce. Partea contextului, care aparine unui singur
proces, se numete context privat al procesului dat.
Exemplul 3.1. Fie dou procese care partajeaz o procedur reentrant. Segmentul executabil, care conine aceast procedur aparine contextului
comun. Segmentul de date i stiva fiecrui proces formeaz contextul lor privat.
3.2.2.1. Mulimi de procese. Paralelism
S considerm dou programe distincte P i Q, fiecare avnd n memorie un segment cod i un segment de date.
Numim p i q procesele rezultante din executarea respectiv a acestor dou programe. Executarea setului (p, q) poate s
se produc n diferite moduri, caracterizate de forma particular a traiectoriei sale temporale. Aceste traiectorii sunt
reprezentate n figura 3.2.
Schemele de mai sus pot fi comentate astfel:
schema 1: este executat mai nti tot procesul p, apoi procesul q la fel n ntregime,
schema 2: sunt executate iruri de instruciuni ale procesului p n mod alternativ cu iruri de instruciuni ale
procesului q, i tot aa pn la terminarea ambelor procese,
schema 3: executarea proceselor p i q este simultan; n acest caz sunt necesare dou procesoare.
Pentru compararea acestor scheme de execuie introducem noiunea nivel de observare. Putem considera o suit de
aciuni ale unui proces ca o aciune unic, adic s observm derularea unui proces considernd o unitate de execuie

ntr-un mod mai riguros ar trebui s adugm aici punctele ntreruptible care pot fi prezente n unele instruciuni de lung durat; o executare a
unei asemenea instruciuni poate fi considerat o suit de mai multe aciuni.

Pentru comoditatea expunerii considerm, c sfritul unei aciuni i nceputul aciunii urmtoare sunt dou evenimente distincte, datele crora sunt
diferite, dei strile corespunztoare sunt identice.

14.11.17 21:27 /conversion/tmp/scratch/371682329.doc p.4


p q
(1)

p q p q p q
(2)

p q
(3)

Fig. 3.2. Executarea unei mulimi de procese


mai puin fin dect instruciunea. De exemplu, dac vom redefini noiunea de aciune elementar ca execuie a unei
proceduri, traiectoria procesului va conine doar strile definite de fiecare apel i retur de procedur. Nivelul de
observare cel mai fin (cel al instruciunilor) este numit nivel de baz.
S ne situm mai nti la nivelul de observare la care, prin convenie, executarea complet a fiecrei dintre
programele P i Q reprezint o aciune unic. Definiiile care urmeaz sunt pentru acest nivel.
a) schema de tip 1 este a unei execuii secveniale a lui p i q. Ea este caracterizat de relaiile:
sfrit(q) < nceput(p) sau sfrit(p) < nceput(q)
b) schemele de tip 2 sau 3 sunt scheme de execuie paralel. Ele sunt caracterizate de
sfrit(p) > nceput(q) sau sfrit(q) > nceput(p).
Revenim la nivelul de baz. Putem face o distincie ntre schemele 2 i 3. ntr-adevr, n schema 2, din considerente
de existen a unui singur procesor, la un moment de timp dat doar o singur aciune poate fi executat, contrar schemei
3. Se va spune c n schema 3 are loc un paralelism real, iar n schema 2 un pseudo-paralelism. Paralelismul real
necesit dou procesoare distincte. Dou observaii importante sunt necesare:
1) Diferena acestor scheme de execuie este legat de alegerea nivelului de observare. Astfel, la nivelul de baz,
diferena dintre schemele 1 i 2 dispare: ambele sunt secveniale.
Exemplu 3.2. Utilizatorii unui sistem de operare, care funcioneaz n timp partajat pe un singur procesor, au impresia c programele lor sunt
executate n mod paralel, deoarece nivelul lor de observare este cel al comenzilor limbajului de comand, compuse din multe
instruciuni. La nivelul de baz, ns, aceste instruciuni sunt atomare i executate de procesor n mod secvenial. Exemplul celor
dou reactoare chimice, prezentate n 3.1.2, conduce la observaii analogice.

2) Alegerea nivelului de baz depinde de fineea fenomenelor, care dorim s le considerm elementare. Dac
trebuie s studiem executarea instruciunilor n pipe-line pe un procesor microprogramat, n calitate de nivel
de baz va fi ales nivelul microinstruciunilor, iar contextul va fi completat cu memoria microprogramelor i
registrele interne.
Vom examina realizarea, la nivelul de baz, a unei scheme de execuie de tipul 2. La fiecare realocare a procesorului,
contextul procesului curent trebuie salvat, pentru a permite reluarea ulterioar a acestei execuii. Dac memoria are o
capacitate suficient pentru a pstra toate segmentele, doar contextul procesorului trebuie salvat. Dac memoria
principal poate conine, la un moment de timp dat, doar segmentul procedur i datele unui singur proces, aceste
segmente de asemenea trebuie salvate pe disc. Aceast remarc justific definiia operaional, adesea ntlnit, a
contextului unui proces ca mulime a informaiilor care trebuie salvat pentru a permite reluarea ulterioar a
procesului, dac execuia acestuia a fost ntrerupt.
3.2.2.2. Concurena proceselor. Resurse virtuale
Situaia descris de schemele 1 i 2 nu rezult dintr-o legtur logic ntre p i q, ci doar din existena unui singur
procesor. Ea poate fi caracterizat astfel: fie o mulime de procese contextele crora au un obiect comun, care poate fi
utilizat la un moment de timp dat de un singur proces. Se va spune n acest caz, c obiectul constituie o resurs critic
pentru procesele date sau c procesele sunt n excludere mutual (excludere reciproc sau concuren) pentru
utilizarea unei resurse. n situaia descris, procesorul este o resurs critic pentru procesele p i q.
Observm c excluderea mutual a unei resurse conduce la serializarea execuiei proceselor concurente, n cazul
unor aciuni, care cer aceast resurs (n exemplul dat, toate aciunile). Schemele 1 i 2 difer doar prin nivelul de finee
la care este executat serializarea.
Funcionarea corect a unei mulimi de procese, care particip la ndeplinirea unei lucrri comune, implic relaii
logice de cooperare (v.3.1.1). Este comod s se separe aceast cooperare de concurena pentru resursele fizice cu
scopul de a simplifica nelegerea i aplicarea celor dou tipuri de relaii. Pentru aceasta este folosit noiunea de
resurse virtuale: fiecrei resurse fizice critice i se asociaz tot attea copii imaginare (sau virtuale) ale acestei resurse
cte procese concurente solicit utilizarea ei. Suntem nevoii s tratm dou probleme distincte:
1) respectarea relaiilor de cooperare ntre procesele, care, fiecare posed (conceptual) resursele fizice solicitate i
pentru care paralelismul n execuie nu este restricionat de competiia pentru resurse,

14.11.17 21:27 /conversion/tmp/scratch/371682329.doc p.5


2) reglarea problemei de concuren pentru resursele fizice printr-o serializare convenabil a execuiei proceselor
n cauz. Se va spune n acest caz, c realizm o alocare a resurselor fizice.
Introducerea resurselor virtuale are o consecin foarte important pe care o vom ilustra-o prin exemplul proceselor
p i q, definite n 3.2.2.1. S atam fiecrui proces un procesor virtual. Conceptual, totul va avea loc ca i cum
procesele s-ar derula paralel, conform unei scheme, numite logice sau virtuale, analogice schemei 3 din fig.3.2. Cu toate
acestea, trebuie de menionat, c aceast schem logic reprezint doar o notaie compact pentru mulimea schemelor
reale posibile i c ele sunt obligatoriu de forma 1 sau 2 din considerentele unicitii procesorului. Pentru o schem
real i una virtual a unui proces dat este pstrat doar ordinea de succesiune a evenimentelor (nceputul i sfritul
aciunii) i nu sunt pstrate valorile absolute ale intervalelor de timp, care le separ. n absena altor informaii, nu
putem spune nimic apriori despre ordinea evenimentelor, asociate unor procese distincte. Timpul folosit la reperarea
evenimentelor n schema logic este numit timp logic; relaiile sale cu timpul real sunt prezentate n fig.3.3.
a1 a2
+------+-----------------+------+ procesul p
b1 b2 (timp logic)
+--+-------+---------+ procesul q
a1 a2
+------+---+ +----------+ +----+------+ p (timp real, execuia 1)
+--+--+ +-----+---------+ q
b1 b2
a1 a2
+---+ +---+-----------------+------+ p (timp real, execuia 2)
+--+----+ +---+---------+ q
b1 b2
n toate cazurile a1 precede a2, b1 precede b2.
Fig.3.3. Timpul logic i timpul real
Vom considera, c evoluia proceselor are loc n timp logic, adic vor fi interzise orice ipoteze despre viteza relativ
a proceselor n cauz: aceasta este consecina simplificrii considerabile, introduse de noiunea de resurse virtuale, care
permite ignorarea mecanismelor de alocare. O singur excepie, totui va fi fcut pentru studierea sincronizrii
temporale, n care timpul fizic intervine n mod precis ca msur a duratei i nu numai ca mod de reperare relativ.
3.2.2.3. Excludere mutual
Introducerea resurselor virtuale ne permite s tratm problema excluderii mutuale la accesarea resurselor fizice
corespunztoare n cadrul mecanismelor de alocare. Aceast problem poate fi pus ntr-un alt mod cum o demonstreaz
exemplul urmtor.
Exemplu 3.3. Dou procese p i q trebuie fiecare s actualizeze valoarea unei variabile comune n (de exemplu, n este starea unui cont bancar
pentru care p i q efectueaz o depozitare). Aciunile respective se vor scrie n programele lui p i q:
procesul p: Ap : n=n+np procesul q:Aq : n=n+nq
S realizm decompoziia acestor aciuni n instrucii, notnd prin Rp i Rq registrele locale ale proceselor p i q respectiv:
procesul p procesul q
1. load Rp, n 1. load Rq, n
2. add Rp, np 2. add Rq, nq
3. sto Rp, n 3. sto Rq, n
Dac aceste aciuni sunt executate n ordinea 1, 1, 2, 2, 3, 3, efectul lor global este de executat n:=n+nq i nu n:=n+np+nq: una
din actualizri a fost pierdut i valoarea final a variabilei n este incorect. Pentru evitarea acestei incoerene aciunile Ap i Aq
trebuie s fie executate n excludere reciproc; se zice de asemenea c ele constituie pentru p i q seciuni critice. Este necesar s se
respecte condiia
sfrit(Ap) < nceput(Aq) sau sfrit(Aq) < nceput(Ap).
Aceast condiie de serializare, care are efectul de a face Ap i Aq s devin atomare, este identic condiiei deja ntlnite n 3.2.2.2
la accesarea unei resurse fizice critice.

Excluderea mutual const n extinderea pentru secvena de aciuni a proprietii de indivizibilitate a aciunilor
nivelului de baz (aciuni atomare). Posibilitatea executrii unor aciuni atomare se afl la baza mecanismelor care
realizeaz sincronizarea.

3.3. Sincronizarea proceselor


3.3.1. Exprimarea i implementarea restriciilor de succesiune
Introducem dou exemple, care ne vor ajuta la formularea restriciilor logice impuse de cooperare.
Exemplul 3.4. Procesul p transmite informaii procesului q scriind ntr-un segment a, consultat de q (se presupune c aceast transmitere are loc o
singur dat). Este necesar s se verifice condiia:

14.11.17 21:27 /conversion/tmp/scratch/371682329.doc p.6


sfrit(scriere(a)) < nceput(citire(a))
Aceast relaie exprim restricia, c citirea lui a de ctre q nu poate ncepe nainte de terminarea scrierii lui a de ctre p.
Exemplul 3.5. Rendez-vous. Fie N procese p1,..., pN. Definim n programul fiecrui proces un punct, numit rendez-vous, pe care procesul nu-l poate
trece nainte ca alte procese s ajung la punctul lor propriu de rendez-vous.
Dac programul procesului pi are forma
<debut_i>;
<rendez-vous>
<continuare_i>;
atunci restriciile de sincronizare se vor exprima dup cum urmeaz:
i, j [1..N]: sfrit(debut_i) < nceput(continuare_j)
Restriciile de sincronizare pot fi exprimate prin urmtoarele dou forme echivalente:
1. Se va impune o ordine de succesiune n timp logic pentru unele puncte ale traiectoriei temporale ale procesului,
2. Se va impune unor procese o condiie de autorizare a depirii acestor puncte ale traiectoriei lor temporale.
Punctele privilegiate astfel se vor numi puncte de sincronizare.
Expresia (2) arat, c restriciile de sincronizare pot fi satisfcute impunnd un proces s atepte s execute o
aciune pn cnd o oarecare condiie va fi satisfcut. Aceast noiune de ateptare nu poate fi exprimat cu ajutorul
instrumentarului introdus pn acum; pentru aceasta vom introduce o nou stare pentru un proces, stare n care procesul
se zice n ateptare sau blocat, prin opoziie strii activ, considerate pn acum n mod implicit. Un proces, care intr
n starea de ateptare, plecnd de la un punct observabil t, oprete progresarea ncepnd cu acest punct i stopeaz
executarea aciunilor. n momentul n care procesul revine n starea activ, el reia execuia sa i contextul su privat
restabilete starea pe care procesul o avea n punctul t (partea neprivat a contextului poate fi modificat de execuia
altor procese). Se numete blocare tranziia activateptare i deblocare tranziia invers. Modalitile de realizare a
blocrii i deblocrii sunt obiectul subparagrafului 3.3.3.
Vom utiliza noiunea de ateptare pentru specificarea sincronizrii proceselor. Aceast specificare se va produce n
dou etape:
1) definirea punctelor de sincronizare pentru fiecare proces,
2) asocierea unei condiii de depire fiecrui punct de sincronizare, condiie exprimat prin intermediul
variabilelor de stare a sistemului.
Vom ilustra acest mod de specificare pentru cele dou exemple precedente. Notm un punct de sincronizare prin ps,
iar condiia de depire asociat prin aut(ps). Dac aceast condiie are valoare TRUE, procesul n cauz este autorizat
s execute instruciunea etichetat cu ps.
Exemplul 3.6: var f : boolean init false
procesul p procesul q
scriere(a); <debut_q>;
f:=true;
<continuare_p> ps : citire(a)

aut(ps) : f:=true
A fost introdus variabila de stare f pentru a exprima condiia procesul p a terminat aciunea scriere(a).
Exemplul 3.7: var n : integer init 0;
procesul pi
<debut_i>;
n:=n+1
ps: <continuare_i>
aut(psi): n=N (i=1,...,N);
Variabila de stare n este n acest caz numrul procesului sosit n punctul de rendez-vous.

3.3.2. Probleme de realizare a sincronizrii


Vom ncerca s implementm sincronizarea specificat de condiiile de depire. Pentru aceasta trebuie s definim
un mecanism de ateptare i s introducem noiunea de evenimente memorizate. Un eveniment memorizat (e) este o
variabil, care poate lua dou valori: sosit i non_sosit, valoarea iniial este non-sosit. Asupra evenimentului
memorizat sunt posibile dou operaii, care sunt aciuni indivizibile:
e:=<valoare> -- atribuirea imediat a unei valori
ateptare(e).
Operaia ateptare(e), executat de un proces p, are urmtoarea specificaie:
if e = non_sosit then
starea(p) := blocat -- p este trecut n ateptarea lui e
endif
Cnd e ia valoarea sosit, toate procesele care ateptau e trec n starea activ.

14.11.17 21:27 /conversion/tmp/scratch/371682329.doc p.7


Vom ncerca acum s realizm cu ajutorul acestui mecanism sincronizarea pentru cele dou exemple.
Exemplul 3.8. var e : eveniment memorizat;
procesul p procesul q
scriere(a); <debut_q>;
e:=sosit; ateptare(e);
<continuare_p> citire(a)
Analiza acestui sistem (care nu conine alte variabile de stare, dect evenimentul memorizat e) poate fi fcut
enumernd traiectoriile temporale posibile. Aceast analiz arat, c sincronizarea este corect atunci cnd operaiile
asupra evenimentului memorizat sunt indivizibile.
Exemplul 3.9. var e : eveniment memorizat;
n : integer init 0;
procesul pi
<debuti>;
(A) n:=n+1;
(B) if n<N then
ateptare(e)
else
e:=sosit
endif
<continuarei>
O analiz mai atent, analogic celei din exemplul din 3.2.2.3, ne arat c acest program este incorect. Notnd un
registru local al procesului i prin Ri, aciunile (A) i (B) pot fi descompuse dup cum urmeaz:
(1) load Ri, n
(2) ai Ri, 1 -- adunare imediat
(3) ci Ri, N --comparare imediat
(4) br () etichet -- salt dac Ri N
<ateptare (e)>
...
etichet : ...
Presupunem, c toate proceselor sunt n ateptarea punctelor lor de rendez-vous, cu excepia a dou, notate prin pj i
pk. Dac pj i pk vor fi executate pe traiectoria temporal (1j, 1k, 2j,...), atunci fiecare va atribui lui n valoarea final N-1
i se va bloca, drept rezultat toate procesele se vor bloca pentru un timp indefinit.
Reieind din analiza de mai sus putem concluziona, c implementarea condiiilor de sincronizare nu poate fi corect
realizat numai cu ajutorul operaiilor de ateptare. Consultarea i modificarea variabilelor de stare, care intervin n
aceste condiii, trebuie s fie executate n regim de excludere reciproc. Observaia dat ne impune s introducem un
mecanism de sincronizare, care n mod automat ar asigura acest regim de funcionare.
3.3.3. Monitorul mecanism de sincronizare
3.3.3.1. Definiii
Un monitor este constituit dintr-o mulime de variabile de stare i o mulime de proceduri, care utilizeaz aceste
variabile. Unele dintre aceste proceduri, numite externe, sunt accesibile utilizatorilor monitorului; numele acestor
proceduri sunt numite puncte de intrare ale monitorului. Procesele, care utilizeaz monitorul pentru a se sincroniza, nu
au acces direct la variabilele de stare. Monitorul poate fi utilizat doar prin apelarea procedurilor sale externe; acestea
permit blocarea sau deblocarea proceselor conform specificaiilor problemei. Condiiile de blocare sau deblocare sunt
exprimate ca funcie ale variabilelor de stare, iar mecanismul de execuie a monitorului garanteaz manipularea acestor
variabile n regim de excludere mutual. n fine, un monitor conine un fragment de cod de iniializare, executat o
singur dat la crearea monitorului.
Blocarea i deblocarea proceselor se exprim, n procedurile monitorului, prin intermediul unor condiii. O condiie
este declarat ca i o variabil, dar nu are valoare: o condiie c poate fi utilizat doar prin intermediul a trei operaii
sau primitive, efectul crora este descris mai jos (prin p am notat procesul, care le execut)
c.ateptare : blocheaz procesul p i l plaseaz n ateptarea lui c
c.vid : funcie cu valori booleene (true, dac nu exist nici un proces n ateptarea lui c, altfel false)
c.semnalizare : if non c.vid then <deblocarea proceselor care sunt n ateptarea lui c> endif
Procesele, care sunt n ateptarea unei condiii c, sunt grupate ntr-un fir de ateptare asociat lui c. Putem spune, c o
condiie furnizeaz proceselor un instrument de desemnare a unui astfel fir de ateptare.

14.11.17 21:27 /conversion/tmp/scratch/371682329.doc p.8


Un proces deblocat de c.semnalizare i reia execuia de la instruciunea, care urmeaz imediat primitivei
c.ateptare, care-l blocase. Necesitatea asigurrii excluderii reciproce pentru variabilele de stare impune o restricie
suplimentar mecanismelor de deblocare: atunci cnd un proces p deblocheaz un proces q printr-o operaie de
semnalizare, p i q nu pot fi meninute simultan active. Se va impune blocarea lui p pn n momentul n care q, la
rndul su, se va bloca sau va prsi monitorul. Pentru evitarea unei blocri indefinite a lui p este necesar ca operaia de
transfer a comenzii de la p la q s fie atomar (indivizibil) i se va garanta, c un proces care a fost temporar blocat
este deblocat de operaia semnalizare nainte ca un nou proces s poat executa o procedur a monitorului.
Observaie. Problema excluderii mutuale , legat de primitiva semnalizare nu se va pune, dac aceasta este ultima operaie ntr-o
procedur a monitorului. Acest caz este foarte frecvent n practic, unele monitoare (de exemplu, Concurrent Pascal) respect n mod
obligator restricia dat.
3.3.3.2. Exemple de utilizare
Exemplul 3.10. sinc: monitor;
var fact: boolean;
term: condiie;
procedura terminare_scriere;
begin
fact:=true;
term.semnalizare
end
procedura debut_citire;
if non fact then
term.ateptare
endif
begin -- iniializare
fact := false
end
end sinc
Monitorul este utilizat dup cum urmeaz:
procesul p procesul q
scriere(a); <debut_q>
sinc.terminare_scriere; sinc.debut_citire;
<continuare_p> citire(a);
Exemplul 3.11. rendez-vous : monitor;
var n : integer;
toi_sosii : condiie;
procedura sosire;
begin
n := n+1;
if n < N then
toi_sosii.ateptare --nu au sosit toate procesele
endif
toi_sosii.semnalizare -- a sosit i ultimul
end
begin -- iniializare
n:=0
end
end rendez_vous
Programul procesului pi va fi de forma:
procesul pi
<debut i>
rendez_vous.sosire;
<continuare i>

14.11.17 21:27 /conversion/tmp/scratch/371682329.doc p.9


Este foarte important s nelegem funcionarea sincronizrii: procesul, care sosete ultimul la rendez_vous, execut
toi_sosii.semnalizare i deblocheaz unul dintre procesele, care sunt n starea ateptare. Acesta, la rndul su, execut
toi_sosii.semnalizare, deblocnd urmtorul proces i aa mai departe.

3.4. Implementarea sincronizrii


3.4.1. Probleme-tip
Experiena demonstreaz, c problemele de sincronizare logic ntlnite n practic pot fi reduse, n marea lor
majoritate, la combinaia unui numr mic de situaii elementare, schemele de soluionare ale crora sunt cunoscute.
Seciunile 3.4.2 3.4.5 sunt consacrate studierii acestor probleme-tip, utiliznd instrumentarul de baz, pus la dispoziie
de monitoare. Problemele-tip sunt urmtoarele:
accesarea de ctre o mulime de procese a unei resurse partajate comune,
comunicarea ntre procese,
gestionarea perifericelor i intrrilor-ieirilor tamponate,
sincronizare temporal.
3.4.2. Administrarea unei resurse partajate
Considerm o resurs (fizic sau logic) partajat de o mulime de procese. Utilizarea acestei resurse trebuie s
respecte nite reguli de utilizare, destinaia crora const n garantarea unor proprieti specificate sau restricii de
integritate. Aceste restricii sunt specificate pentru fiecare resurs. O modalitate de garantare a respectrii regulilor de
utilizare a unei resurse const n adoptarea urmtoarei scheme:
modul de folosire a resursei presupune utilizarea obligatorie a procedurilor de acces asociate resursei; orice
tentativ de utilizare, care nu respect acest mod este detectat automat,
procedurile de accesare sunt grupate ntr-un monitor, sau mai multe, programul cruia impune respectarea
restriciilor de integritate.
Cel mai simplu caz este acela al unei resurse pentru care singura restricie de integritate este de a fi utilizat n
excludere reciproc. Simpla grupare a procedurilor sale de acces ntr-un monitor unic garanteaz respectarea acestor
restricii.
3.4.2.1. Alocarea resurselor banalizate
Considerm o resurs pentru care exist un numr fix de N exemplare. Un proces poate accesa la cerere n uniti din
cele N, le poate utiliza i apoi elibera. O unitate, folosit de un proces, se numete alocat procesului, care o utilizeaz,
pentru toat perioada de la accesare pn la eliberare. Toate unitile sunt echivalente din punctul de vedere al
proceselor utilizatoare, vom mai zice c resursa este banalizat. Zonele-tampon din memoria principal sau pe disc,
unitile de band magnetic, etc. sunt exemple de resurse banalizate.
Vom admite urmtoarele reguli de utilizare:
o unitate poate fi alocat la un moment de timp dat doar unui singur proces,
o unitate poate fi alocat unui proces, care cere alocarea, doar dac ea este liber (nu este alocat nici unui
proces),
o operaie de eliberare este aplicat totdeauna ultimelor resurse, achiziionate de procesul care execut
eliberarea,
o cerere de alocare este blocant n caz de eec (numr insuficient de uniti libere).
Definim dou proceduri cerere i eliberare ale unui monitor. Utilizarea resursei are loc conform schemei de mai
jos.
ps:resurse.cerere(n); -- cerere pentru n uniti
-- ateptare n caz de eec
<utilizarea unitilor primite>
resurse.eliberare(n) -- eliberarea resurselor
Condiia de sincronizare se va scrie pentru orice proces:
aut(ps) : n nlibere
Prima form a unui monitor resurse se va obine utiliznd direct condiia de sincronizare:
resurse: monitor;
var nlibere : integer;
disp : condiie;

14.11.17 21:27 /conversion/tmp/scratch/371682329.doc p.10


procedura cerere(n);
begin
while n>nlibere do
disp.ateptare;
endwhile;
nlibere:=nlibere-n
end;
procedura eliberare(n);
begin
nlibere:=nlibere+n;
disp.semnalizare -- deblocare n lan
end;
begin -- iniializare
nlibere:=N
end
end resurse
Nu a fost fcut nici o ipotez despre ordinea proceselor n firul de ateptare al condiiei disp. Drept consecin, la
fiecare eliberare vor fi deblocate toate procesele. Este o soluie greoaie i poate fi foarte costisitoare, dac exist multe
procese. Este preferabil s se programeze n mod explicit administrarea firului de ateptare a cererilor, ceea ce oblig s
avem cte o condiie distinct pentru fiecare proces.
Pentru elaborarea programelor vom introduce un tip discret proces, variabilele cruia permit desemnarea proceselor.
resurse : monitor
type element : struct
lungime : integer
proc : proces
end;
var nlibere : integer;
disp :array[proces] of condiie;
<declaraia firului de ateptare f i procedurilor sale de accesare: introducere, extragere, primul>
procedura cerere(n);
begin
var e: element;
if n>nlibere then
e.lungime:=n;
e.proc:=p; -- p este procesul apelant
introducere(e,f); -- n ordinea de cretere a lungimii
disp[p].ateptare
endif;
nlibere:=nlibere-n
end;
procedura eliberare(n);
var e: element;
begin
nlibere:=nlibere+n;
while primul(f).lungime nlibere do
extragere(e,f); -- elementul extras = primul (f)
disp[e.proc].semnalizare -- e.proc = procesul deblocat
endwhile;
end;
begin -- iniializare
nlibere:=N;
f:=<vid>
end
end resurse

14.11.17 21:27 /conversion/tmp/scratch/371682329.doc p.11


Aceast soluie este mult mai compact i mai general, deoarece permite o mai bun separare a expresiei
sincronizrii (ceea ce rezult din structura monitorului) de politica de alocare (care este definit de procedurile de
gestionare a firului de ateptare introducere i extragere).
3.4.2.2. Modelul cititorului i scriitorului
S considerm un fiier manipulat de procese din dou clase diferite: cititori, care consult fiierul fr a modifica
coninutul lui i scriitori, care pot modifica acest coninut. Fie pentru un moment arbitrar de timp ncit i nscr numrul
de cititori i de scriitori, respectiv, care folosesc o procedur de acces la fiier. Cererea de asigurare a coerenei fiierului
ne impune s respectm urmtoarele restricii:
(nscr=0) i (ncit0) -- fiier n citire
sau (nscr =1) i (ncit=0) -- fiier n scriere
Fie fich un monitor care asigur respectarea acestor restricii. Vom impune urmtoarea form a acceselor la fiier:
proces cititor proces scriitor
fich.debut_citire; fich.debut_scriere;
<acces citire> <acces scriere>
fich.terminare_citire; fich.terminare_scriere;
Procedurile debut_citire, terminare_citire, debut_scriere, terminare_scriere trebuie s asigure respectarea
restriciilor de mai sus. Vom implementa aceste restricii autoriznd depirile; pentru aceasta este necesar s fie
precizate prioritile ntre cititori i scriitori.
Cu titlu de exemplu, presupunem c cititorii au prioritate n faa redactorilor (o scriere nu va fi autorizat, dac
exist cititori n ateptare). Definim urmtoarele variabile de stare:
scr = o scriere este n curs (valoare boolean)
nc = numrul de cititori n ateptare sau n curs de citire
n acest caz, condiiile de depire se vor exprima dup cum urmeaz:
aut(citire) : scr=false (nu exist scrieri curente)
aut(scriere): scr=false i nc=0 (nici scrieri nici citiri n curs, nici cititori n ateptarea)
Monitorul, care urmeaz traduce direct aceste condiii.
fich : monitor;
var scr : boolean;
nc : integer;
c_scr, c_cit : condiie;
procedura debut_citire;
begin
nc:=nc+1;
if scr then
c_cit.ateptare;
endif
end
procedura terminare_citire;
begin
nc:=nc-1;
if nc=0 then -- ultimul cititor a terminat
c_scr.semnalizare
endif
end
procedura debut_scriere;
begin
if scr or nc>0 then -- scriere sau citire n curs
c_scr.ateptare
endif;
scr:=true

14.11.17 21:27 /conversion/tmp/scratch/371682329.doc p.12


end
procedura terminare_scriere;
begin
scr:=false;
if nc>0 then -- prioritate cititorilor care ateapt
c_cit.semnalizare
else
c_scr.semnalizare
endif
end
begin -- iniializare
scr:=false;
nc:=0
end
end fich
Pot fi definite i programate i alte reguli de prioritate.
3.4.3. Comunicarea ntre procese
Procesele pot comunica prin accesarea unei mulimi de variabile comune. Acest mod de comunicare este slab
structurat i ineficace, deoarece el trebuie s asigure excluderea reciproc a variabilelor. Este utilizat doar n cazuri
speciale, cum ar fi un nucleu de sincronizare, unde excluderea mutual global este redus la secvene scurte i bine
protejate. Pentru cazuri generale sunt utilizate alte moduri de comunicare. Vom prezenta mai nti o schem de baz
pentru comunicarea prin mesaje - modelul productorului i consumatorului, realizat cu ajutorul monitoarelor (3.4.3.1).
O alt posibilitate, descris n 3.4.3.2, const n a considera operaiile de comunicare ca un fel de mecanisme primitive
de sincronizare. n 3.4.3.3 prezentm o aplicaie frecvent de comunicare modelul client-server.
3.4.3.1. Modelul productorului i consumatorului
O schem uzual de comunicare este cea n care un proces (productorul) trimite mesaje unui alt proces
(consumatorul), utiliznd un tampon n memoria comun. Mesajele sunt de lungime fix i capacitatea tamponului este
de N mesaje.
Specificaiile comunicaiei sunt urmtoarele:
un mesaj dat poate fi preluat doar o singur dat dup ce a fost depozitat n tampon,
un mesaj nu trebuie s poat fi pierdut; dac tamponul conine N mesaje nepreluate, nu pot fi depozitate aici
mesaje suplimentare,
o operaie imposibil (depozitare ntr-un tampon plin sau preluare dintr-un tampon vid) blocheaz procesul,
care ncearc s o execute.
Condiiile de depire pot fi exprimate dup cum urmeaz, notnd prin n numrul de mesaje din tampon, care nu au
fost nc preluate:
aut(depozitare) : n < N -- tamponul nu este plin
aut(preluare) : n > 0 -- tamponul nu este vid
Respectarea acestor restricii este asigurat de un monitor tampon, utilizat dup cum urmeaz:
proces productor proces consumator
... ...
produce(mesaj_emis); tampon.preluare(mesaj_recepionat);
tampon.depozitare(mesaj_emis); consumator(mesaj_recepionat);
Monitorul tampon poate fi elaborat, transcriind n mod direct autorizrile de depire:
tampon : monitor;
var n : 0..N;
non_plin, non_vid : condiie;
<declaraii ale procedurilor depozitare i preluare>
procedura depozitare(m:mesaj);
begin
if n=N then
non_plin.ateptare
endif;
n:=n+1;
introducere(m);

14.11.17 21:27 /conversion/tmp/scratch/371682329.doc p.13


non_vid.semnalizare
end
procedura preluare(var m:mesaj);
begin
if n=0 then
non_vid.ateptare
endif;
preluare(m);
n:=n-1;
non_plin.semnalizare
end;
begin -- iniializare
n:=0;
end
end tampon
Procedurile introducere(m) i preluare(m) definesc politica de gestionare a tamponului i reprezentarea intern a
mesajelor. Un caz frecvent este acela n care mesajele sunt reprezentate de elementele succesive ale unui tablou,
administrat n mod circular. n acest caz mesajele sunt preluate n ordinea depozitrii. Procedurile de gestionare a
tamponului se vor scrie astfel:
type mesaj : <descrierea formatului mesajelor>
ptr : 0..N-1;
var fa : array[ptr] of mesaj;
top, coad: ptr;
procedura intrare(m:mesaj);
begin
fa[coad]:=m;
coad:=coad+1 mod N
end;
procedura ieire(var m:mesaj);
begin
m:= fa[top];
top:=top+1 mod N
end;
<iniializarea se va completa cu top:=0; coad:=0;>
Aceast schem poate fi extins pentru mai muli productori i consumatori. Drept efect, procedurile monitorului
asigur accesarea exclusiv la mesaje ntre productori i consumtori. Totui, n cazul a mai muli consumatori schema
nu permite direcionarea unui mesaj ctre un consumator anumit: putem doar garanta, c un mesaj va fi preluat de un
consumator (i numai de unul singur) fr a specifica concret de care.
3.4.3.2. Primitive de comunicare
Schimbul de mesaje ntre procese, n afar de funcia de transmitere a informaiei, poate fi utilizat i pentru
ordonarea evenimentelor n cadrul unor procese distincte, deoarece emiterea unui mesaj precede ntotdeauna recepia sa.
Operaiile de schimb de mesaje pot fi definite ca nite mecanisme primitive i s le utilizm pentru sincronizarea
proceselor.
Primitivele de baz n comunicarea prin mesaje sunt:
emitere(mesaj,destinaie)
recepie(mesaj,origine)
Specificrile acestor primitive trebuie s precizeze:
natura i forma mesajelor,
modul de adresare a proceselor emitoare i destinatare,
modul de sincronizare a acestor procese,
tratarea erorilor.
1) Natura mesajelor
n conformitate cu nivelul de exprimare la care este definit mecanismul de comunicare, mesajele pot fi specificate de
un tip, analogic obiectelor unui limbaj sau prin lungimea fizic a lor. Lungimea poate fi constant sau variabil. Mai

14.11.17 21:27 /conversion/tmp/scratch/371682329.doc p.14


frecvent sunt utilizate mesajele de lungime constant, care pot fi create mai simplu, mesajele de lungime variabil vor fi
transmise prin referin, pasnd adresa fizic sau identificatorul informaiei transmise.
2) Modul de adresare
Procesele, care fac schimb de mesaje, se pot desemna reciproc prin numele lor (desemnare direct) sau pot utiliza
numele unui obiect intermediar ori cutie potal (desemnare indirect). Aceste nume sunt folosite ca parametri origine i
destinaie. Schema a doua faciliteaz modificarea dinamic a interconexiunilor proceselor sau chiar componentele unei
mulimi de procese, care comunic.
n cazul desemnrii directe parametrul origine a primitivei recepie poate fi interpretat n dou moduri:
fie ca dat: receptorul specific explicit c ateapt un mesaj de la un destinatar special (recepie selectiv),
fie ca rezultat: receptorul primete un mesaj care i-a fost adresat mpreun cu identitatea emitorului.
n cazul desemnrii indirecte asocierea proceselor cutiilor potale poate fi static sau dinamic. n ultimul caz, sunt
utilizate dou primitive conectare i deconectare pentru ataarea procesului la o cutie potal (n calitate de receptor) i
de abrogare a acestei atari, respectiv. n unele sisteme un receptor sau mai multe pot fi ataate unei cutii potale date;
cutiile potale supuse unor asemenea restricii sunt adesea numite pori. Este posibil i situaia invers cnd un proces
poate fi asociat la mai multe pori distincte. Dac asocierea ntre procesul receptor i poart este static, un nume de
poart specific fr ambiguitate un proces receptor ca i n metoda desemnrii directe.
3) Moduri de sincronizare
Pentru primitivele de comunicare pot fi specificate mai multe moduri de sincronizare. n caz general, operaia de
recepie blocheaz, n absena mesajului, receptorul. Unele sisteme pun la dispoziie primitive care dau posibilitatea s
se determine dac o cutie potal este vid, ceea ce permite evitarea blocrii. Pentru emitere sunt utilizate dou moduri
de sincronizare:
Schema productor-consumator, n care cutia potal este realizat printr-o zon tampon. Emiterea nu este
blocant, cu excepia cazului n care tamponul este plin. Folosirea tampoanelor de lungime variabil cu alocarea
dinamic a locaiunilor reduce probabilitatea blocrii emitorului.
Schema rendez-vous, n care emitorul este blocat pn la preluarea mesajului de ctre receptor. Aceast
schem poate fi considerat caz limit a precedentei cu lungimea nul a tamponului.
n fine, atunci cnd un proces este asociat n recepie la mai multe pori, poate fi definit un mod de sincronizare, zis
ateptare multipl, n care sosirea unui mesaj la o oarecare din aceste pori deblocheaz receptorul.
4) Tratarea erorilor
Scopul tratrii erorilor este de a evita blocrile infinite ale proceselor, care se pot produce n diverse circumstane:
Emiterea unui mesaj cu o destinaie (proces sau poart) inexistent. n acest caz primitiva nu este blocant;
eroarea este tratat prin emiterea unui cod de eroare sau printr-o deviere.
Distrugerea unui proces de la care alte procese ateapt un mesaj sau un rspuns: procesele n ateptare sunt
blocate i recepioneaz un cod de eroare.
Ultima situaie nu este totdeauna detectabil. O tehnic uzual const n stabilirea unui interval de timp maxim de
ateptare a unui mesaj i deblocarea procesului care ateapt la expirarea acestui interval (v. 3.4.5).
Vom ilustra prin cteva exemple reprezentative utilizarea acestor mecanisme de comunicare.
Exemplul 3.12. Sistemul de operare Thoth [8]. n acest sistem comunicarea folosete desemnarea direct i sincronizarea prin rendez-vous. Mesajele
sunt de lungime constant. Sunt utilizate patru primitive:
id:=send(message, id_dest)
emite procesului id_dest un mesaj; blocheaz emitorul pn la primirea unui rspuns, transmis n message. Aceast primitiv
indic identitatea procesului care a transmis rspunsul (sau nil, dac destinatarul nu exist).
id:=receive(message, id_orig)
recepioneaz un mesaj; procesul origine poate s nu fie specificat. Valoarea transmis este identitatea emitorului.
reply(message, id_orig, id_dest)
trimite un rspuns destinatarului specificat (care trebuie s-l atepte); nu este blocant; fr consecine, dac rspunsul nu era
ateptat.
forward(message, id_orig, id_dest)
aceast operaie non blocant este utilizat de un proces dup recepionarea unui mesaj trimis de ctre id_orig, pentru ca s impun
mesajul s ajung la id_dest, care are acum obligaia de a rspunde lui id_orig.

14.11.17 21:27 /conversion/tmp/scratch/371682329.doc p.15


Exemplul 3.13. Sistemul de operare Unix [9]. n sistemul Unix comunicarea ntre procese utilizeaz tampoane, numite pipes (tuburi), administrate
conform schemei productor-consumator. Mesajele transmise sunt caractere. Un pipe (tub) leag un emitor i un receptor,
conexiunea fiind stabilit dinamic.
Exemplul 3.14. Rendez-vous n limbajul de programare Ada [10, 11]. Limbajul Ada permite definirea proceselor. Forma sintactic a comunicrilor
ntre procese este apelarea procedurii, ns transmiterea parametrilor i a rezultatelor are loc conform principiului de transmitere a
mesajelor cu rendez-vous. Recepionarea poate fi condiionat (un apel este acceptat doar dac o condiie specificat este
satisfcut) i exist posibilitatea de ateptare multipl.
3.4.3.3. Aplicaii : relaia client-server
O aplicaie curent a comunicrilor ntre procese este relaia client-server. Un proces server are n arj
ndeplinirea unor servicii (executarea unui program predefinit) proceselor client. Pentru aceasta poate fi utilizat
urmtoarea schem:
procesul server procesul client
ciclu poart_serviciu.emitere(cerere)
poart_serviciu.recepionare(cerere)
<executare serviciu> ...
[poart_client.emitere(rezultat)] ...
endciclu [poart_client.recepionarere(rezultat)]
Secvenele din parantezele ptrate sunt facultative.
Procesul server este asociat unei pori, unde clienii i depun cererile, trimind cereri; el este blocat atta timp ct
nu exist cereri de servicii n ateptare.
Serviciul cerut poate conine trimiterea la client a rezultatului. n acest caz clientul trebuie s trimit serverului n
cererea sa numrul unei pori la care el se va bloca n ateptarea rezultatului.
Fr a modifica schema de mai sus putem introduce mai multe procese server echivalente, oricare dintre ele fiind n
stare s satisfac o cerere a unui serviciu. Aceste servere n recepie vor fi asociate la una i aceeai cutie potal.
Modelul din 3.4.2.1 (alocarea resurselor banalizate) i modelul client-server de mai sus sunt reprezentative pentru
dou scheme de obinere a unui serviciu cu ajutorul proceselor ntr-un sistem de operare: apelarea de procedur ntr-un
monitor sau activarea uni proces server ciclic prin emiterea mesajelor. Alegerea ntre aceste dou scheme este dictat de
considerente de eficacitate (schema serverului este preferat, atunci cnd exist paralelism real ntre client i server) sau
de uniformitate a structurii.
3.4.4. Administrarea intrrilor-ieirilor
Vom arta cum poate fi integrat administrarea intrrilor-ieirilor n mecanismul monitoarelor. Prezentm mai nti
gestionarea unui periferic izolat, apoi, ca exemplu de aplicaie, principiul unei gestionri tamponate a intrrilor-ieirilor.
3.4.4.1. Administrarea unui periferic
Fiecrui periferic i este asociat un monitor procedurile externe ale cruia permit executarea intrrilor-ieirilor la
acest periferic. Acest monitor are urmtoarea form general (pentru un sistem mono-utilizator):
perif: monitor;
var ..., sfr_schimb_i,...: condiie;
<declaraiile variabilelor de stare ale perifericului>
...
procedura schimb_i(<parametri>);
begin
<mascarea ntreruperilor>;
if starea preg then
<tratare eroare(perifericul nu este gata)>
endif;
lansare_transfer_i(parametri);
sfr_schimb_i.ateptare; -- ntrerupere demascat
if starea ok then -- n timpul ateptrii
<tratare eroare(incident de transfer)>
endif;
<demascare ntreruperi>
end;
...
begin

14.11.17 21:27 /conversion/tmp/scratch/371682329.doc p.16


<iniializare>
end
end perif
Procedura lansare_transfer_i pregtete programul pentru schimbul cerut (construirea programului canalului sau al
ADM - Acces Direct la Memorie, innd cont de parametrii schimbului) i lanseaz execuia sa (instruciunea SIO
Start Input Output). Procesele apelante ateapt sfritul transferului datorit condiiei sfr_schimb_i. Sosirea unei
ntreruperi, care marcheaz sfritul schimbului de tip i provoac n mod automat executarea urmtoarei secvene:
if sfr_schimb_i.vid then <tratarea eroare ntrerupere care nu este ateptat>
else sfr_schimb_i.semnalizare
endif
Pentru un proces care execut o intrare-ieire apelnd o procedur de schimb a acestui monitor, totul se petrece ca i
cum schimbul este sincron: la returul din procedur, informaia a fost efectiv transferat (sau o eroare a fost detectat i
semnalizat). Mecanismul de blocare evit ateptarea activ i procesorul poate fi utilizat n timpul transferului de un alt
proces.
3.4.4.2. Buferizarea imprimrii
Putem elabora acum programul administrrii tamponate a imprimrii, descrise n 3.1. Avem nevoie de trei tampoane
tm1 i tm2 de capacitate N1 i N2 n memoria central i unul pe disc, td, de lungime Ndisc. Pentru simplitate
presupunem, c transferurile se fac cu blocuri constante egale cu o linie. Fiecare tampon este comandat de un monitor
cu aceeai structur care are rolul de a asigura excluderea mutual i sincronizarea condiiilor tampon plin i tampon
vid.
Aceste tampoane, numite tampon1, tampon2 i tampon_disc au aceeai structur cu tamponul descris n 3.4.3,
nlocuind N cu N1, N2 i Ndisc, respectiv. Definind tm1, tm2 i td ca tablouri de elemente de lungimea unei linii i
pointerii top i coad locali fiecrui monitor, procedurile de depozitare i preluare pot fi:
<pentru tamponul 1> <pentru tamponul 2>
procedura intrare(l:linie); procedura intrare(l:linie);
tm1[coad] := l; tm2[coad] := l;
coad := coad+1 mod N1 coad := coad+1 mod N2
procedura ieire(var l:linie); procedura ieire(var l:linie);
l := tm1[top]; l := tm2[top];
top := top+1 mod N1 top := top+1 mod N2
n monitorul tampon_disc operaiile de depozitare i preluare sunt intrri-ieiri, care utilizeaz monitorul de
gestionare a discului:
procedura intrare(l:linie);
disc.scriere(l,td[coad]);
coad := coad+1 mod Ndisc
procedura ieire(var l:linie);
disc.scriere(l,td[top]);
top := top+1 mod Ndisc
Programul de intrare-ieire este realizat prin cooperarea a patru procese programul crora este prezentat schematic
mai jos (trei procese ale sistemului de operare i procesul utilizator). Pentru a simplifica expunerea au fost omise
secvenele de tratare a erorilor i am admis, c sistemul funcioneaz n regim permanent fr limitarea numrului de
linii la imprimare. Programele folosesc trei monitoare de gestionare a perifericelor (tampon1, tampon2 i tampon_disc)
i dou monitoare de gestionare a perifericelor (impr i disc), construite n baza modelului perif, prezentat n 3.4.4.1
proces imprimare linie proces scriere_disc proces citire_disc
ciclu ciclu ciclu
tampon2.preluare(l); tampon1.preluare(l); tampon_disc.citire(l);
impr.scriere(l); tampon_disc.scriere(l); tampon2.depozitare(l);
endciclu endciclu endciclu
Imprimarea unei linii este cerut de procedura:
procedura scriere_linie(l:linie);
tampon1.depozitare(l)

14.11.17 21:27 /conversion/tmp/scratch/371682329.doc p.17


Putem constata, c programele de mai sus sunt mult mai simple dect cele care folosesc direct ntreruperile.
Structura modular, introdus de monitoare permite separarea total a gestionrii tampoanelor de cea a perifericelor.
Schimbarea capacitii unui tampon modific doar monitorul care comand acest tampon; nlocuirea unui periferic cu
un altul implic rescrierea doar a monitorului, care comand acest periferic.
3.4.5. Sincronizare temporal
Sincronizarea temporal face ca timpul s intervin nu numai ca mijloc de ordonare a evenimentelor, dar i ca
msur de durat absolut. Acest mod de sincronizare este utilizat n aplicaiile de timp real, care conin interaciuni cu
organe externe (comanda proceselor industriale, de exemplu). Sincronizarea temporal solicit folosirea unui ceas,
realizat prin intermediul unui oscilator cu quartz, care emite impulsuri la intervale regulate. Aceste impulsuri pot fi
utilizate pentru a declana o ntrerupere la fiecare impuls sau pentru a decrementa n mod automat coninutul unui
registru contor, o ntrerupere este declanat atunci cnd coninutul acestui registru atinge valoare 0.
Vom utiliza a doua metod de funcionare. Unitatea de timp folosit este perioada ceasului.
Pentru rezolvarea problemelor principale de sincronizare temporal poate fi folosit primitiva suspendare(t), care
are ca efect blocarea procesului apelant pentru o durat (fizic) egal cu t uniti de timp. Prezentm principiul de
realizare a acestei primitive cu ajutorul unui ceas.
Pentru rezolvarea acestei probleme vom fi nevoii s ntreinem:
valoarea absolut a timpului (ora absolut), care msoar la orice moment timpul trecut de la o instan
iniial,
un registru, adic o list a proceselor care ateapt deblocarea, ordonat conform timpului absolut de deblocare.
Toate procesele, care apeleaz primitiva suspendare(t) sunt inserate n registru n poziia, care corespunde orei sale
absolute de deblocare.
Numim ora_de_baz ora absolut a ultimei nnoiri a ceasului, adic a ultimei iniializri a contorului; fie t_at
ultima valoare ncrcat. Ora absolut exact este dat la fiecare moment de timp de relaia
ora_exact = ora_de_baz + t_at - contor
(t_at - contor este timpul care s-a scurs dup ultima nnoire a contorului). De la o ntrerupere de ceas (la trecerea
contorului prin 0), dup ultima nnoire s-a scurs un timp egal cu t_at; ora_de_baz poate, deci, fi iniializat conform
relaiei
ora_de_baz := ora_de_baz + t_at
Variabila ora_de_baz, odat iniializat, va fi corect ntreinut cu condiia ca registrul s nu fie nicicnd vid; n caz
general aceast condiie va fi asigurat introducnd un proces, numit paznic:
procesul paznic
ciclu
suspendare(tmax)
endciclu
n care tmax este un interval foarte mare de timp, paznicul rmnnd pentru toat perioada de lucru n coada registrului.
Mecanismele descrise sunt realizate ntr-un monitor numit ceas, care are dou intrri: procedura suspendare (apelat
prin ceas.suspendare(t)) i procedura tratare_ntrerupere, pentru tratarea ntreruperii de ceas (trecerea contorului prin
zero). Registrul este realizat cu ajutorul unui fir de ateptare, care conine descriptorii proceselor. Un descriptor este
format din numele procesului i timpul absolut de deblocare; firul de ateptare este ordonat n ordinea creterii timpului
deblocrii.
Presupunem, c ntreruperea de ceas activeaz un proces, unica activitate a cruia const n apelarea procedurii
ceas.tratare_ntrerupere.
ceas: monitor;
type descriptor: struct
i : proces;
ora_debl : integer

Problema ar fi uor de rezolvat, dac fiecare proces ar dispune de un ceas propriu: ar fi suficient s se introduc n contor valoarea t i s se atepte
ntreruperea la trecerea prin zero. Realizarea primitivei suspendare cu ajutorul unui ceas unic comun presupune ataarea fiecrui proces a unui ceas
virtual.

14.11.17 21:27 /conversion/tmp/scratch/371682329.doc p.18


end;
var contor, t_at;
ora_de_baz, ora_exact: integer
deblocare : array[proces] of condiie;
<declaraiile firului de ateptare i procedurilor sale de acces>
procedura suspendare(durata:integer);
var proc: descriptor;
begin
ora_exact:=ora_de_baz+t_at-contor;
proc.ora_deblocrii:=ora_exact+durata -- ora absolut a deblocrii
proc.i := p; -- p este procesul apelant
intrare(proc,fir) -- ordonare de ora_deblocrii
if proc=primul(fir) then
t_at:=contor:=durata;
ora_de_baz:=ora_exact;
endif;
deblocare[p].ateptare
end;
procedura tratare_ntrerupere; -- contor n zero
var proc: descriptor;
delta_t:integer;
begin
ieire(proc,fir); -- primul din firul de ateptare
ora_de_baz := ora_exact+t_at;
if vid(fir) then
delta_t := tmax
else
delta_t := primul(fir).ora_deblocrii ora_de_baz;
endif;
t_at := contor := delta_t; -- deblocarea viitoare
deblocare[proc.i].semnalizare
end;
begin
ora_de_baz := <ora_iniial>;
contor := t_at := tmax;
intrare(paznic,fir)
end
end ceas
Metoda de mai sus (ntreinerea unui registru i a orei absolute) este utilizat pentru realizarea programelor de
simulare discret. Un atare program realizeaz un model n care diferite procese, executate n regim pseudo-paralel,
reprezint activiti care se deruleaz n paralelism real. Timpul utilizat este un timp simulat, adic o variabil, care
reprezint trecerea timpului fizic, respectnd proporionalitatea intervalelor timpului simulat cu intervalele
corespunztoare ale timpului fizic.

3.5. Gestionarea dinamic a proceselor


Doar n sistemele cele mai simple procesele sunt n numr constant i create odat pentru totdeauna la iniializarea
sistemului. n sistemele concurente, mai ales n cele interactive, procesele sunt comandate dinamic. Astfel, n Multics,
un proces nou este creat odat cu admiterea unui nou utilizator; n Unix, la executarea fiecrei comenzi. Primitivele de
creare i distrugere a proceselor pot fi puse n arja sistemului de operare sau la dispoziia utilizatorilor. Crearea unui
proces presupune alocarea resurselor i iniializarea contextului, elementele cruia au fost specificate n 3.2.1.
Distrugerea unui proces elibereaz toate resursele care i-au fost alocate.
Primele primitive, propuse pentru gestionarea dinamic a proceselor, au fost fork i join. Istoric i cronologic, aceste
operaii au fost introduse pentru organizarea executrii paralele a programelor pe un sistem multiprocesoral, noiunea de
proces nefiind nc clar. Vom descrie cteva variante ale acestor primitive.
Fie P o procedur. Instruciunea
id := fork(p),

14.11.17 21:27 /conversion/tmp/scratch/371682329.doc p.19


executat de un proces p (printe), creaz un proces nou q (fiul), care va fi executat paralel cu p. Primitiva fork prezint
ca rezultat identificatorul lui q (sau nil, dac crearea este imposibil). Contextul iniial al lui q este o copie a lui p, mai
puin contorul ordinal, care este fixat la prima instruciune a lui p. Procesul fiu se termin cu o primitiv, numit exit sau
quit, care provoac dispariia sa. Dup ce fork creaz un proces fiu q, primitiva join q permite procesului printe s
fixeze un punct de rendez-vous cu acest fiu. Executarea lui join q blocheaz procesul printe pn cnd q nu va executa
exit. Primitivele fork i join au avantajele i dezavantajele instruciunii go to din programarea secvenial.
Exemplul 3.15. n sistemul de operare Unix crearea unui proces poate fi realizat de ctre interpretorul limbajului de comand (shell) sau cu ajutorul
instruciunii fork() de un program. Ultima situaie este prezentat schematic n fig.3.4.

procesul 1 procesul 2

copie

date date

stiv stiv

procesul fiu
procesul printe
Fig.4.4. Crearea proceselor cu ajutorul instruciunii fork

Efectul instruciunii fork():


duplicarea procesului printe;
returnarea valorii pid (numrului procesului fiu) n procesul printe;
returnarea valorii 0 n procesul fiu:
procesul printe procesul fiu

if (fork() == 0) if (fork() == 0)
codul procesului fiu codul procesului fiu
else else
codul procesului printe codul procesului printe

returnarea pid al procesului fiu ( 0) returnare 0


Altfel spus, n Unix primitiva fork (fr parametri) creeaz un proces al crui spaiu de lucru este o copie a spaiului de lucru a
creatorului, inclusiv i contorul ordinal. Diferena poate fi determinat consultnd valoarea returnat de primitiv (0 pentru fiu;
identificatorul fiului sau nil pentru printe). O primitiv wait permite printelui s atepte terminarea execuiei unuia dintre
programele fiu (fr a putea alege care anume, dac exist mai multe). Un proces termin execuia sa cu primitiva exit. Primitiva
exec(p) permite unui proces s schimbe contextul, apelnd o procedur specificat de p.
La lansarea Unix-ului sunt create dou procese: procesul numit Swaper, care administreaz memoria, cu pid=0 i procesul Init cu
pid=1, care creeaz toate celelalte procese.
Ilustrm folosirea primitivelor fork i exec:
...
id := fork();
if id = 0 then -- eu sunt fiul
exec(p) -- programul fiului
else -- eu sunt printele
if id = -1 then -- nil : creare imposibil
<tratare eroare>
else
<programul printelui>
endif
endif
Primitiva wait este utilizat dup cum urmeaz:
id := wait(cod) -- blocare pn la terminarea programului unuia dintre fii
... -- id = numrul programului fiu terminat, cod = cauza terminrii

14.11.17 21:27 /conversion/tmp/scratch/371682329.doc p.20


3.6. Sincronizarea n Windows
Platforma pe 32 de bii pune la dispoziia programatorului instrumente evoluate pentru multiprogramare, att la
nivelul unei mulimi de lucrri, ct i a unei lucrri singulare. Poate s apar ntrebarea CND s fie utilizat
multiprogramarea n cadrul unei singure aplicaii. Rspunsul este foarte simplu: atunci cnd dorim ca mai multe
fragmente de cod s fie executate simultan (pseudosimultan, dac exist mai multe fragmente dect procesoare). De
exemplu, dac dorim ca unele activiti s fie ndeplinite n regim de fond sau programul s continue s reacioneze la
unele evenimente exterioare n timpul ndeplinirii unor calcule foarte costisitoare. Pot fi aduse i alte exemple.
3.6.1. Procese i fire
Numim proces n Windows o instan (un exemplar) a programului, ncrcat n memoria operativ. Aceast instan
poate crea fire (thread) - secvene de instruciuni, care urmeaz a fi executate. Este important s se neleag c n
Windows anume firele sunt executate (nu procesele!), fiecrui proces fiindu-i asociat minimum un fir, numit firul
principal al aplicaiei.
Deoarece n realitate exist mult mai multe fire dect procesoare fizice, firele vor fi executate secvenial, timpul de
procesor repartizndu-se ntre fire. Dar viteza mare de execuie i frecvena mare de comutare a firelor las impresia
unei execuii paralele a acestora.
Fr comentarii suplimentare subliniem, c strile elementare ale unui fir sunt aceleai ca i n cazul proceselor: ales
(exe), eligibil (ready) i blocat (wait). Starea blocat este asociat ateptrii unui anume eveniment. Cnd evenimentul se
produce firul este trecut n mod automat n starea eligibil. De exemplu, dac un fir execut anumite calcule, iar un alt fir
este obligat s atepte rezultatele pentru a le salva pe disc, al doilea fir ar putea utiliza un ciclu de tipul "while(!
isCalcFinished ) continue;". Este ns simplu s ne convingem, c n timpul execuiei acestui ciclu procesorul este
ocupat 100% - este cazul ateptrii active. Astfel de cicluri trebuie evitate prin utilizarea mecanismelor de sincronizare.
n cadrul sistemului de operare Windows exist dou tipuri de fire fire interactive, care execut un ciclu propriu de
prelucrare a mesajelor (de exemplu, firul principal al unei aplicaii) i fire de lucru, care sunt funcii simple. n ultimul
caz execuia firului se ncheie atunci cnd calculele, generate de funcia respectiv, iau sfrit.
Merit atenie i modalitatea organizrii ordinii de execuie a firelor. Algoritmul FIFO este departe de a fi cel mai
optimal. n Windows toate firele sunt ordonate conform prioritilor. Prioritatea unui fir este un numr ntreg de la 0 la
31 i este determinat de prioritatea procesului, care a generat firul i prioritatea relativ a firului. n acest mod se
ajunge la o flexibilitate maxim, fiecrui fir punndu-i-se la dispoziie n caz ideal exact atta timp de procesor, ct
are nevoie.
Prioritatea firului poate fi modificat dinamic. Firele interactive, care au prioritatea Normal, sunt executate n mod
deosebit de ctre sistem, prioritatea acestor fire fiind majorat, atunci cnd procesul, care le-a generat, se afl n planul
central (foreground). n rezultat, aplicaia curent reacioneaz mai repede la cererile utilizatorului.
3.6.2. Necesitatea sincronizrii
Cnd un proces este creat n mod automat este creat firul principal al acestuia. Acest fir poate crea n timpul
execuiei alte fire, care la fel pot crea fire noi i aa mai departe. Timpul de procesor fiind repartizat ntre fire, fiecare fir
lucreaz n mod independent.
Toate firele unui proces mpart resursele comune, de exemplu, spaiul de adrese al memoriei operative sau fiierele
deschise. Aceste resurse aparin ntregului proces, deci i fiecrui fir. Fiecare fir poate s utilizeze aceste resurse fr
nici un fel de restricii. n realitate, din cauza multitaskingului controlat (preemptive multitasking - la orice moment de
timp sistemul poate ntrerupe execuia unui fir i transmite controlul unui alt fir), se poate ntmpla ca un fir s nu fi
terminat nc lucrul cu o resurs comun oarecare, iar sistemul s treac la un alt fir, care utilizeaz aceeai resurs.
Rezultatele pot fi imprevizibile. Asemenea conflicte se pot produce i n cazul unor fire, care aparin chiar unor procese
diferite. Problema poate s apar ntotdeauna cnd dou sau mai multe fire folosesc o resurs comun. Este necesar un
mecanism de coordonare a lucrului firelor cu resurse comune. n Windows acest mecanism se numete sincronizarea
firelor (thread synchronization).
3.6.3. Structura mecanismului de sincronizare n Windows
Mecanismul de sincronizare este un set de obiecte ale sistemului de operare Windows, create i gestionate program,
comune pentru toate firele sistemului (unele pentru firele unui singur proces) i utilizate pentru coordonarea accesului la
resurse. n calitate de resurse pot fi toate obiectele, care pot fi accesate de dou i mai multe fire un fiier pe disc, un
port, un articol al unei baze de date, o variabil global a unui program, accesibil firelor unui singur procesor, un obiect
al dispozitivului interfeei grafice (Graphic Device Interface), etc.
De obicei, sunt utilizate mecanismele (obiectele) de sincronizare, introduse mai sus: excluderea mutual (mutex),
secia critic (critical section), eveniment memorizat (event) i semaforul (semaphore), fiecare realiznd metoda proprie
de sincronizare. n calitate de obiecte sincronizate pot fi chiar procesele sau firele (cnd un fir ateapt terminarea
execuiei unui proces sau a unui alt fir), fiierele, dispozitivele de comunicaie, etc.
Sensul mecanismelor de sincronizare const n faptul, c fiecare poate s fie n starea set. Pentru fiecare mecanism
de sincronizare aceast stare poate s aib sens propriu. Firele pot s testeze starea curent a mecanismului de
sincronizare i/sau s atepte modificarea acestei stri, coordonndu-i n acest fel aciunile proprii. Este foarte

14.11.17 21:27 /conversion/tmp/scratch/371682329.doc p.21


important s se sublinieze, c atunci cnd un fir lucreaz cu mecanismele de sincronizare (le creeaz, le modific starea)
sistemul nu ntrerupe execuia firului, pn nu va fi terminat aceast aciune, adic toate operaiile finite din
mecanismele de sincronizare sunt atomare (nu pot fi ntrerupte).
Menionm de asemenea, c nu exist nici o legtur real ntre mecanismele de sincronizare i resurse.
Mecanismele de sincronizare nu pot interzice accesul nedorit la o resurs, ele doar indic firului momentul cnd acesta
poate accesa resursa, sau cnd acesta trebuie s atepte (de exemplu, un semafor la o intersecie doar indic cnd este
permis trecerea. Cineva poate trece pe rou, dar consecinele pot fi grave).
3.6.4. Administrarea obiectelor de sincronizare n Windows
Crearea unui obiect de sincronizare se produce prin apelarea unei funcii speciale din WinAPI de tipul Create (de
exemplu, CreateMutex). Acest apel returneaz descriptorul obiectului (handle), care poate fi folosit de toate firele
procesului dat. Un obiect de sincronizare poate fi accesat i dintr-un alt proces, dac acest proces a motenit descriptorul
obiectului dat, sau folosind funcia de deschidere a unui obiect (Open). Obiectului, dac el nu este destinat doar
pentru uz intern (n interiorul unui singur proces), n mod obligator i se acord un nume unic. Nu poate fi creat un
eveniment memorizat i un semafor cu acelai nume.
Folosind descriptorul poate fi determinat starea curent a obiectului cu ajutorul funciilor de ateptare. De exemplu,
funcia WaitForSingleObject(x, y) cu doi parametri (primul este descriptorul obiectului, iar al doilea timpul de
ateptare n ms) returneaz WAIT_OBJECT_0, dac obiectul se afl n starea set (adic nu aparine nici unui fir i
poate fi utilizat pentru sincronizare), WAIT_TIMEOUT dac a expirat timpul de ateptare i WAIT_ABANDONED,
dac obiectul de sincronizare nu a fost eliberat nainte ca firul, care-l comanda, s se fi terminat. Dac timpul de
ateptare este egal cu 0, atunci funcia returneaz rezultatul imediat, n caz contrar, ateapt intervalul de timp indicat. n
cazul n care starea obiectului de sincronizare va deveni set pn la expirarea acestui timp, funcia returneaz
WAIT_OBJECT_0, altfel - WAIT_TIMEOUT.
Dac n parametrul timp este indicat constanta simbolic INFINITE, funcia va atepta pn cnd starea obiectului
va deveni set, fr vre-o restricie.
Starea mai multor obiecte poate fi aflat cu ajutorul funciei WaitForMultipleObjects. Pentru ncheierea lucrului cu
un obiect de sincronizare i eliberarea descriptorului se apeleaz funcia CloseHandle. Este important de tiut, c
apelarea unei funcii de ateptarea blocheaz firul curent, adic atta timp ct un fir se afl n starea de ateptare el nu
are acces la procesor.
3.6.4.1. Excluderea mutual
Cum a fost menionat deja, mecanismele de excludere mutual (mutex-ele, de la MUTual EXclusion) permit
coordonarea accesului la o resurs partajat. Starea set a obiectului corespunde momentului de timp n care obiectul nu
aparine nici unui fir i poate fi utilizat, iar starea reset momentului cnd un fir oarecare controleaz deja mutex-ul.
Accesarea va fi permis doar dup eliberare.
Pentru a lega mutex-ul de firul curent trebuie apelat una din funciile de ateptare. Firul, cruia i aparine mutex-ul,
l poate ocupa de mai multe ori, fr autoblocare, ns mai apoi acesta va trebui eliberat tot de attea ori cu ajutorul
funciei ReleaseMutex.
3.6.4.2. Evenimentele
Obiectele-evenimente sunt utilizate pentru a informa firele, care sunt n ateptare, despre producerea unui
eveniment. n Windows exist dou tipuri de evenimente cu resetare manual i automat. Resetarea manual se
execut cu funcia ResetEvent. Aceste evenimente sunt folosite pentru informarea mai multor fire, iar evenimentele cu
resetare automat sunt utilizate pentru informarea unui anumit fir, celelalte rmnnd n ateptare.
Funcia CreateEvent creaz un obiect-eveniment, funcia SetEvent seteaz evenimentul n starea set, iar funcia
ResetEvent reseteaz evenimentul. Funcia PulseEvent seteaz evenimentul, iar dup semnalizarea firelor, care erau
n ateptare (toate n cazul resetrii manuale i doar unul la resetarea automat), reseteaz obiectul. Dac nu exist fire
n ateptare, PulseEvent doar reseteaz obiectul, fr semnalizare.
3.6.4.3. Semafoarele
Un obiect-semafor este n ultim instan un mutex cu contor. Acest obiect permite s fie ocupat de un numr
anume de fire, dup care ocuparea va fi posibil numai dac unul din fire va elibera semaforul. Semafoarele sunt
utilizate pentru a limita numrul de fire, care lucreaz simultan cu resursa. La iniializare se specific numrul maxim
de fire, la fiecare ocupare valoarea contorului semaforului scade.
3.6.4.4. Seciunile critice
Obiectul-seciune critic permite programatorului s evidenieze un fragment de cod n care firul obine acces la o
resurs partajat, prentmpinnd utilizarea resursei de mai muli utilizatori. Pentru a utiliza resursa firul va intra mai
nti n seciunea critic (apelarea funciei EnterCriticalSection). Dac intrarea a avut loc, nici un alt fir nu va avea
acces la aceeai seciune critic, execuia acestuia fiind suspendat. Reluarea se va produce n momentul n care primul

14.11.17 21:27 /conversion/tmp/scratch/371682329.doc p.22


fir prsete seciunea critic (funcia LeaveCriticalSection). Diferena de mutex const n faptul c seciunea critic
este utilizat numai pentru firele unui singur proces.
Cu ajutorul funciei TryEnterCriticalSection se poate stabili, dac seciunea critic este liber. Utiliznd aceast
funcie, un proces, fiind n ateptarea resursei, poate s nu se blocheze, ndeplinind operaii utile.
3.6.4.5. Protejarea accesrii variabilelor
Exist o serie de funcii, care permit lucrul cu variabilele globale ale tuturor firelor, fr a ne preocupa de
sincronizare, deoarece aceste funcii singure rezolv problema sincronizrii. Aceste funcii sunt InterlockedIncrement,
InterlockedDecrement, InterlockedExchange, InterlockedExchangeAdd i InterlockedCompareExchange. De
exemplu, funcia InterlockedIncrement incrementeaz valoarea unei variabile pe 32 bii cu o unitate.
3.6.4.6. Sincronizarea n Microsoft Fundation Classes
Biblioteca MFC conine clase speciale pentru sincronizarea firelor (CMutex, CEvent, CCriticalSection i
CSemaphore). Aceste clase corespund obiectelor de sincronizare WinAPI i sunt derivate de la clasa CSyncObject.
Pentru utilizarea acestor clase trebuie consultai constructorii i metodele lor Lock i Unlock. n principiu, aceste
clase sunt doar un fel de ambalaj pentru obiectele de sincronizare.
O alt modalitate de utilizare a acestor clase const n crearea aa numitelor clase thread-safe. O clas thread-safe
reprezint o anumit resurs n program. Tot lucrul cu resursa este realizat numai prin intermediul acestei clase, care
conine toate metodele necesare. Clasa este proiectat n aa mod, ca metodele ei s rezolve problema sincronizrii,
adic n cadrul aplicaiei s apar ca o simpl clas. Obiectul de sincronizare MFC este inclus n aceast clas n calitate
de membru privat i toate funciile clasei, care realizeaz accesarea resursei, i coordoneaz lucrul cu acest membru.
Utiliznd funciile Lock i Unlock clasele de sincronizare MFC pot fi utilizate direct, iar n mod indirect prin
funciile CSingleLock i CmultiLock.
3.6.5. Exemplu de sincronizare n Windows
Prezentm un exemplu simplu de lucru cu obiectul de sincronizare mutex [22]. Pentru simplitate a fost utilizat o
aplicaie Win32 de consol, dei nu este obligator.

#include <windows.h>
#include <iostream.h>

void main()
{
DWORD res;

// crem obiectul excludere mutual


HANDLE mutex = CreateMutex(NULL, FALSE, "NUME_APLICATIE-MUTEX01");
// dac obiectul exist deja, CreateMutex va returna descriptorul obiectul existent,
// iar GetLastError va returna ERROR_ALREADY_EXISTS

// timp de 20 s ncercm s ocupm obiectul


cout<<"ncerc s ocup obiectul...\n"; cout.flush();
res = WaitForSingleObject(mutex,20000);
if (res == WAIT_OBJECT_0) // dac ocupare s-a terminat cu succes
{
// ateptm 10 s
cout<<"L-am prins! Ateptare 10 secunde...\n"; cout.flush();
Sleep(10000);

// eliberm obiectul
cout<<"Acum eliberm obiectul\n"; cout.flush();
ReleaseMutex(mutex);
}

// nchidem descriptorul
CloseHandle(mutex);
}
Pentru a controla modul de funcionare a mecanismului de excludere mutual se vor lansa dou instane ale acestei
aplicaii. Prima instan va ocupa obiectul imediat i-l va elibera doar peste 10 secunde. Numai dup aceasta instana a
doua va reui s ocupe obiectul. n acest exemplu obiectul de sincronizare este folosit pentru sincronizarea proceselor,
din care cauz n mod obligatoriu trebuie s aib nume.

14.11.17 21:27 /conversion/tmp/scratch/371682329.doc p.23


3.6.6. Utilizarea seciunilor critice n Windows
n acest caz seciunile critice sunt utilizate pentru a permite la un moment de timp dat accesul la unele date
importante unui singur fir al aplicaiei, celelalte fire fiind blocate. De exemplu, fie variabila m_pObject i cteva fire,
care apeleaz metodele obiectului referit de m_pObject. Presupunem c aceast variabil din timp n timp i poate
schimba valoarea, valoarea 0 nu este interzis. Fie urmtorul fragment de cod:

// Firul #1
void Proc1()
{
if (m_pObject)
m_pObject->SomeMethod();
}

// Firul #2
void Proc2(IObject *pNewObject)
{
if (m_pObject)
delete m_pObject;
m_pObject = pNewObject;
}

n acest exemplu exist pericolul potenial de apelare m_pObject->SomeMethod() dup ce obiectul a fost distrus
cu ajutorul delete m_pObject, deoarece n sistemele de operare cu multitasking controlat execuia oricrui fir poate fi
ntrerupt n cel mai neconvenabil (pentru firul dat) moment i s nceap execuia unui alt fir. Pentru exemplul nostru
momentul nedorit este atunci cnd firul #1 a testat deja m_pObject, dar nu a reuit s apeleze SomeMethod().
Execuia firului #1 a fost ntrerupt i a nceput execuia firului #2. Iar firul #2 reuise deja s apeleze destructorul
obiectului. Ce se va ntmpla atunci cnd firului #1 i se va acorda din nou timp de procesor i va fi apelat
SomeMethod() al unui obiect deja inexistent? Greu de presupus.
Aici ne vin n ajutor seciunile critice. S modificm exemplul de mai sus.

// Firul #1
void Proc1()
{
::EnterCriticalSection(&m_lockObject);
if (m_pObject)
m_pObject->SomeMethod();
::LeaveCriticalSection(&m_lockObject);
}

// Firul #2
void Proc2(IObject *pNewObject)
{
::EnterCriticalSection(&m_lockObject);
if (m_pObject)
delete m_pObject;
m_pObject = pNewObject;
::LeaveCriticalSection(&m_lockObject);
}

Fragmentul de cod inclus ntre ::EnterCriticalSection() i ::LeaveCriticalSection() cu una i aceeai


seciune critic n calitate de parametru nu va fi executat nici o dat n mod paralel. Aceasta nseamn, c dac firul #1 a
reuit s acapareze seciunea critic m_lockObject, ncercarea firului #2 s intre n aceeai seciune critic va
conduce la blocarea acestuia pn n momentul cnd firul #1 va elibera m_lockObject prin apelul
::LeaveCriticalSection(). Invers, dac firul #2 a accesat seciunea critic naintea firului #1, acesta din urm va fi
nevoit s atepte, nainte de a ncepe lucrul cu m_pObject.
Menionm, c seciunile critice nu sunt obiecte ale nucleului sistemului de operare. Practic, tot lucrul cu seciunile
critice are loc in procesul care le-a creat. Din aceasta rezult, c seciunile critice pot fi utilizate numai pentru
sincronizare n cadrul unui proces. S cercetm mai aprofundat o seciune critic.

14.11.17 21:27 /conversion/tmp/scratch/371682329.doc p.24


3.6.6.1. Structura RTL_CRITICAL_SECTION
Este definit dup cum urmeaz:

typedef struct _RTL_CRITICAL_SECTION


{
PRTL_CRITICAL_SECTION_DEBUG DebugInfo; // Folosit de sistemul de operare
LONG LockCount; // Contorul de utilizri
LONG RecursionCount; // Contorul accesrii repetate din firul utilizatorului
HANDLE OwningThread; // ID firului utilizatorului (unic)
HANDLE LockSemaphore; // Obiectul nucleului folosit pentru ateptare
ULONG_PTR SpinCount; // Numrul de cicluri goale naintea apelrii nucleului
}
RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;

Cmpul LockCount este incrementat cu o unitate la fiecare apelare ::EnterCriticalSection() i decrementat cu


unu la fiecare apel ::LeaveCriticalSection(). Acesta este primul (adesea i unicul) control pentru testarea seciunii
critice. Dac dup incrementare n acest cmp avem 0, aceasta nseamn c pn la acest moment nu au avut loc apeluri
impare ::EnterCriticalSection() din alte fire. n acest caz datele pzite de aceast seciune critic pot fi utilizate
n regim monopol. Adic, dac seciunea critic este folosit intensiv de un singur fir, atunci
::EnterCriticalSection() se transform practic n ++LockCount, iar ::LeaveCriticalSection() n--LockCount.
Aceasta nseamn, c folosirea a mai multor mii de seciuni critice ntr-un singur proces nu va conduce la creterea
substanial a utilizrii nici a resurselor de sistem, nici a timpului de procesor. Ca i concluzie: nu se va face economie
pe contul seciunilor critice. Mult totuna nu vom putea economisi.
n cmpul RecursionCount este pstrat numrul de apeluri repetate ::EnterCriticalSection() din unul i acelai
fir. Dac se va apela ::EnterCriticalSection() din unul i acelai fir de mai multe ori, toate apelurile vor avea
succes. Adic urmtorul cod nu se va opri pentru totdeauna n cel de-al doilea apel ::EnterCriticalSection(), ci va
merge pn la capt.
// Firul #1
void Proc1()
{
::EnterCriticalSection(&m_lock);
// ...
Proc2()
// ...
::LeaveCriticalSection(&m_lock);
}

// nc Firul #1
void Proc2()
{
::EnterCriticalSection(&m_lock);
// ...
::LeaveCriticalSection(&m_lock);
}
ntr-adevr, seciunile critice sunt destinate s protejeze datele la accesarea din cteva fire. Utilizarea multipl a
uneia i aceeai seciuni critice de un singur fir nu va genera eroare, ceea ce este normal. Trebuie doar s avem grij ca
numrul de apeluri ::EnterCriticalSection() i ::LeaveCriticalSection() s coincid i totul va fi n regul.
Cmpul OwningThread conine 0 n cazul seciunilor critice libere sau identificatorul unic al firului-posesor. Acest
cmp este testat, dac la apelul ::EnterCriticalSection() cmpul LockCount, dup incrementarea cu o unitate, a
devenit mai mare ca 0. Dac OwningThread coiincide cu identificatorul unic al firului curent, atunci valoarea lui
RecursionCount crete cu o unitate i ::EnterCriticalSection() este returnat imediat. n caz contrar
::EnterCriticalSection() va atepta pn firul, care posed seciunea critic, va apela ::LeaveCriticalSection() de un
numr necesar de ori. Cmpul LockSemaphore este folosit, dac este necesar s se atepte pn seciunea critic este
eliberat. Dac LockCount este mai mare ca 0 i OwningThread nu coiincide cu identificatorul unic al firului curent,
atunci firul blocat creaz un obiect al nucleului eveniment i apeleaz ::WaitForSingleObject(LockSemaphore).
Firul posesor, dup decrementarea cmpului RecursionCount, testeaz acest cmp i dac valoarea lui este 0, iar
LockCount este mai mare ca 0, constat c exist minimum un fir n ateptarea momentului cnd LockSemaphore se
va afla n starea sosit. Pentru aceasta firul-posesor apeleaz ::SetEvent() i un fir oarecare (doar unul) dintre cele
blocate este deblocat i are acces la datele critice.

14.11.17 21:27 /conversion/tmp/scratch/371682329.doc p.25


Windows NT/2000 genereaz o excepie, dac ncercarea de a crea un eveniment a euat. Aceasta este just att
pentru funciile ::Enter/LeaveCriticalSection(), ct i pentru ::InitializeCriticalSectionAndSpinCount() cu bitul cel
mai semnificativ al parametrului SpinCount setat. n cazul sistemului de operare WindowsXP creatorii nucleului au
procedat un pic altfel. Aici, funciile ::Enter/LeaveCriticalSection() n loc s genereze o excepie, dac nu pot crea un
eveniment propriu, vor folosi un obiect global, unic pentru toi, creat n avans. n aa mod, n caz de deficien
catastrofal a resurselor de sistem, programul sub comanda lui WindowsXP chiopteaz un timp oarecare mai
departe. ntr-adevr, este foarte complicat s scrii programe, care ar fi n stare s continue lucrul dup ce
::EnterCriticalSection() a generat o excepie. De obicei, chiar dac programatorul a prezis o astfel de situaie, nu
se ajunge mai departe de generarea unui mesaj de eroare i stoparea forat a execuiei programului. Drept rezultat,
WindowsXP ignoreaz bitul cel mai semnificativ al cmpului LockCount.
n sfrit, cmpul LockCount. Acest cmp este folosit doar n sistemele multiprocesorale. n sistemele
monoprocesorale, dac seciunea critic este ocupat de un fir oarecare, putem doar comuta comanda la aceast seciune
i trece n ateptarea evenimentului nostru. n sistemele multiprocesorale exist o alternativ: s executm de cteva ori
un ciclu vid, testnd de fiecare dat dac seciunea critic nu a fost eliberat (ateptare activ). Dac dup un numr de
SpinCount ori seciunea critic nu a fost eliberat, trecem n starea blocat. Aceast modalitate este mult mai eficient
dect comutarea la planificatorul nucleului i napoi. n WindowsNT/2000 bitul cel mai semnificativ al acestui cmp
indic dac obiectul nucleului, variabila handle a cruia se afl n cmpul LockSemaphore, trebuie creat anticipat.
Dac pentru aceasta nu dispunem de resurse de sistem suficiente, sistemul genereaz o excepie i programul i poate
micora posibilitile funcionale sau chiar termina execuia.
3.6.6.2. Funcii API pentru seciunile critice
Descriem mai jos cteva funcii din API pentru lucrul cu seciunile critice.
Mai nti funciile BOOL InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection) i BOOL
InitializeCriticalSectionAndSpinCount(LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount).
Completeaz cmpurile structurii lpCriticalSection adresate. Dup apelare seciunea critic este gata de lucru. Iat
pseudocodul funciei RtlInitializeCriticalSection din ntdll.dll

VOID RtlInitializeCriticalSection(LPRTL_CRITICAL_SECTION pcs)


{
RtlInitializeCriticalSectionAndSpinCount(pcs, 0)
}

VOID RtlInitializeCriticalSectionAndSpinCount(LPRTL_CRITICAL_SECTION pcs,


DWORD dwSpinCount)
{
pcs->DebugInfo = NULL;
pcs->LockCount = -1;
pcs->RecursionCount = 0;
pcs->OwningThread = 0;
pcs->LockSemaphore = NULL;
pcs->SpinCount = dwSpinCount;
if (0x80000000 & dwSpinCount)
_CriticalSectionGetEvent(pcs);
}

Funcia DWORD SetCriticalSectionSpinCount(LPCRITICAL_SECTION lpCriticalSection, DWORD


dwSpinCount) seteaz valoarea cmpului SpinCount i returneaz valoarea precedent a acestuia. Amintim, c bitul
cel mai semnificativ este responsabil de legarea evenimentului, folosit pentru ateptarea accesului la seciunea critic
dat. Pseudocodul funciei RtlSetCriticalSectionSpinCount din ntdll.dll este listat mai jos.

DWORD RtlSetCriticalSectionSpinCount(LPRTL_CRITICAL_SECTION pcs, DWORD dwSpinCount)


{
DWORD dwRet = pcs->SpinCount;
pcs->SpinCount = dwSpinCount;
return dwRet;
}

VOID DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection) elibereaz resursele, ocupate de


seciunea critic. Are urmtorul pseudocod:
VOID RtlDeleteCriticalSection(LPRTL_CRITICAL_SECTION pcs)

14.11.17 21:27 /conversion/tmp/scratch/371682329.doc p.26


{
pcs->DebugInfo = NULL;
pcs->LockCount = -1;
pcs->RecursionCount = 0;
pcs->OwningThread = 0;
if (pcs->LockSemaphore)
{
::CloseHandle(pcs->LockSemaphore);
pcs->LockSemaphore = NULL;
}
}

VOID EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection), BOOL


TryEnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection) permit intrarea n seciunea critic. Dac
seciunea critic este ocupat de un alt fir, atunci ::EnterCriticalSection() va atepta pn aceasta va fi eliberat, iar
::TryEnterCriticalSection() va returna valoarea FALSE. Listingurile din ntdll.dll sunt:

VOID RtlEnterCriticalSection(LPRTL_CRITICAL_SECTION pcs)


{
if (::InterlockedIncrement(&pcs->LockCount))
{
if (pcs->OwningThread == (HANDLE)::GetCurrentThreadId())
{
pcs->RecursionCount++;
return;
}

RtlpWaitForCriticalSection(pcs);
}

pcs->OwningThread = (HANDLE)::GetCurrentThreadId();
pcs->RecursionCount = 1;
}

BOOL RtlTryEnterCriticalSection(LPRTL_CRITICAL_SECTION pcs)


{
if (-1L == ::InterlockedCompareExchange(&pcs->LockCount, 0, -1))
{
pcs->OwningThread = (HANDLE)::GetCurrentThreadId();
pcs->RecursionCount = 1;
}
else if (pcs->OwningThread == (HANDLE)::GetCurrentThreadId())
{
::InterlockedIncrement(&pcs->LockCount);
pcs->RecursionCount++;
}
else
return FALSE;

return TRUE;
}

VOID LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection) elibereaz seciunea critic.


Pseudocodul este urmtorul:
VOID RtlLeaveCriticalSectionDbg(LPRTL_CRITICAL_SECTION pcs)
{
if (--pcs->RecursionCount)
::InterlockedDecrement(&pcs->LockCount);
else if (::InterlockedDecrement(&pcs->LockCount) >= 0)
RtlpUnWaitCriticalSection(pcs);

14.11.17 21:27 /conversion/tmp/scratch/371682329.doc p.27


}
3.6.6.3. Clase de seciuni critice
Pentru o utilizare corect a seciunilor critice prezentm mai jos codul claselor seciunilor critice:
class CLock
{
friend class CScopeLock;
CRITICAL_SECTION m_CS;
public:
void Init() { ::InitializeCriticalSection(&m_CS); }
void Term() { ::DeleteCriticalSection(&m_CS); }

void Lock() { ::EnterCriticalSection(&m_CS); }


BOOL TryLock() { return ::TryEnterCriticalSection(&m_CS); }
void Unlock() { ::LeaveCriticalSection(&m_CS); }
};

class CAutoLock : public CLock


{
public:
CAutoLock() { Init(); }
~CAutoLock() { Term(); }
};

class CScopeLock
{
LPCRITICAL_SECTION m_pCS;
public:
CScopeLock(LPCRITICAL_SECTION pCS) : m_pCS(pCS) { Lock(); }
CScopeLock(CLock& lock) : m_pCS(&lock.m_CS) { Lock(); }
~CScopeLock() { Unlock(); }
void Lock() { ::EnterCriticalSection(m_pCS); }
void Unlock() { ::LeaveCriticalSection(m_pCS); }
};

Clasele CLock i CAutoLock sunt utilizate, de obicei, pentru sincronizarea accesrii variabilelor clasei, iar
CScopeLock este destinat, n special, pentru a fi utilizat n proceduri. Compilatorul singur va avea grij s apeleze
::LeaveCriticalSection() prin intermediul destructorului. Urmeaz un exemplu de folosire a CScopeLock.

CAutoLock m_lockObject;
CObject *m_pObject;
void Proc1()
{
CScopeLock lock(m_ lockObject); // apelarea lock.Lock();
if (!m_pObject)
return; // apelarea lock.Unlock();
m_pObject->SomeMethod();

// apelarea lock.Unlock();
}
3.6.6.4. Depanarea seciunilor critice
Depanarea seciunilor critice este o ocupaie foarte interesant, dar i dificil. Poi cuta ore i chiar zile n ir cauza
apariiei unei probleme. Erorile, legate de seciunile critice sunt de dou tipuri: de realizare i de arhitectur. Erorile de
realizare pot fi depistate relativ uor i, de regul, sunt generate de utilizarea incorect (lipsa perechii) a apelurilor
::EnterCriticalSection() i ::LeaveCriticalSection(). Urmeaz un fragment de cod n care este omis apelul
::EnterCriticalSection().
// n procedur se presupune, c m_lockObject.Lock(); a fost deja apelat
void Pool()
{
for (int i = 0; i < m_vectSinks.size(); i++)

14.11.17 21:27 /conversion/tmp/scratch/371682329.doc p.28


{
m_lockObject.Unlock();
m_vectSinks[i]->DoSomething();
m_lockObject.Lock();
}
}
Apelul ::LeaveCriticalSection() fr ::EnterCriticalSection() va conduce la faptul c chiar primul apel
::EnterCriticalSection() va stopa execuia firului pentru totdeauna.
n fragmentul de cod de mai jos lipsete apelul ::LeaveCriticalSection():
void Proc()
{
m_lockObject.Lock();
if (!m_pObject)
return;
// ...
m_lockObject.Unlock();
}
n acest exemplu are sens s fie utilizat o clas de tipul CSopeLock. Se mai poate ntmpla ca
::EnterCriticalSection() s fie apelat fr iniializarea seciunii critice cu ajutorul ::InitializeCriticalSection() (de
exemplu, n proiectele scrise cu ajutorul lui ATL). n versiunea debug totul poate lucra foarte bine, iar n versiunea
release moare. Aceasta are loc din cauza aa-zisului CRT (_ATL_MIN_CRT) minimal, care nu apeleaz
constructorii obiectelor statice (Q166480, Q165076). n versiunea ATL 7.0 aceast problem a fost rezolvat. Pot
s apar probleme, dac atunci cnd este folosit o clas de tipul CScopeLock a fost omis identificatorul variabilei, de
exemplu, CScopeLock (m_lock). Compilatorul apeleaz constructorul CScopeLock i imediat distruge acest obiect
fr nume (n conformitate cu standardul!). Adic, imediat dup apelarea metodei Lock() are loc apelarea metodei
Unlock() i sincronizarea nu se produce.
Dintre erorile de arhitectur cea mai frecvent este mbriarea fatal (deadlock, v.5.1.3.3), cnd dou fire
ncearc s acceseze dou i mai multe seciuni critice. Prezentm un exemplu pentru dou fire.
void Proc1()
// Firul #1
{
::EnterCriticalSection(&m_lock1);
// ...
::EnterCriticalSection(&m_lock2);
// ...
::LeaveCriticalSection(&m_lock2);
// ...
::LeaveCriticalSection(&m_lock1);
}

// Firul #2
void Proc2()
{
::EnterCriticalSection(&m_lock2);
// ...
::EnterCriticalSection(&m_lock1);
// ...
::LeaveCriticalSection(&m_lock1);
// ...
::LeaveCriticalSection(&m_lock2);
}
Pot s apar probleme i n cazul copierii unor seciuni critice. Este greu de presupus c codul de mai jos a fost scris
de un programator sntos:

CRITICAL_SECTION sec1;
CRITICAL_SECTION sec2;
// ...
sec1 = sec2;
Din atribuirea de mai sus este dificil s obii foloase. Dar fragmentul urmtor poate fi adesea ntlnit:

14.11.17 21:27 /conversion/tmp/scratch/371682329.doc p.29


struct SData
{
CLock m_lock;
DWORD m_dwSmth;
} m_data;

void Proc1(SData& data)


{
m_data = data;
}
i totul ar fi OK, dac structura SData ar avea on constructor de copiere, de exemplu:

SData(const SData data)


{
CScopeLock lock(data.m_lock);
m_dwSmth = data.m_dwSmth;
}
Presupunem c programatorul a considerat, c este suficient s se ndeplineasc o simpl copiere a cmpurilor i, n
rezultat, variabila m_lock a fost copiat, iar anume n acest moment ea fusese accesat dintr-un alt fir i valoarea
cmpului LockCount este 0. Dup apelul ::LeaveCriticalSection() din acel fir valoarea cmpului LockCount pentru
variabila iniial m_lock a fost decrementat cu o unitate, pentru variabila copiat rmnnd fr schimbare. Ca
rezultat, orice apel ::EnterCriticalSection() nu se va ntoarce niciodat n acest fir. Va rmne pentru totdeauna n
ateptare. Pot exista situaii mult mai complicate.
Fie un obiect care apeleaz metodele unui alt obiect, obiectele aflndu-se n fire diferite. Apelurile se fac n mod
sincron, adic obiectul #1 transmite execuia firului obiectului #2, apeleaz metoda i se va comuta napoi la firul su.
Execuia firului #1 va fi suspendat pentru toat perioada de execuie a firului obiectului #2. Presupunem acum, c
obiectul #2 apeleaz o metod a obiectului #1 din firul su. Controlul va fi ntors obiectului #1, dar din firul obiectului
#2. Dac obiectul #1 apelase metoda obiectului #2, intrnd ntr-o seciune critic oarecare, atunci la apelarea metodei
obiectului #1 acesta se va bloca pe sine nsui la intrarea repetat n aceeai seciune critic. Fragmentul de cod care
urmeaz vine s exemplifice aceast situaie.
// Firul #1
void IObject1::Proc1()
{
// Intrm n seciunea critic a obiectului #1
m_lockObject.Lock();
// Apelm metoda obiectului #2, are loc comutarea la firul obiectului #2
m_pObject2->SomeMethod();
// Aici nimerim numai dup ntoarcerea din m_pObject2->SomeMethod()
m_lockObject.Unlock();
}

// Firul #2
void IObject2::SomeMethod()
{
// Apelm metoda obiectului #1 din firul obiectului #2
m_pObject1->Proc2();
}

// Firul #2
void IObject1::Proc2()
{
// ncercm s intrm n seciunea critic a obiectului #1
m_lockObject.Lock();
// Aici nu vom ajunge niciodat
m_lockObject.Unlock();
}
Dac n acest exemplu nu ar fi avut loc comutarea firelor, toate apelurile ar fi avut loc n firul obiectului #1 i nu am
fi avut probleme. Exemple de acest gen stau la baza tehnologiei compartimentului COM (apartments). Nu sunt
recomandate apelurile obiectelor, dac au avut loc intrri n seciunile critice. Primul exemplu din acest subparagraf va
fi rescris astfel:

14.11.17 21:27 /conversion/tmp/scratch/371682329.doc p.30


// Firul #1
void Proc1()
{
m_lockObject.Lock();
CComPtr<IObject> pObject(m_pObject); // apelarea pObject->AddRef();
m_lockObject.Unlock();
if (pObject)
pObject->SomeMethod();
}

// Firul #2
void Proc2(IObject *pNewObject)
{
m_lockObject.Lock();
m_pObject = pNewobject;
m_lockObject.Unlock();
}
Accesul la obiect a rmas ca i mai nainte sincronizat, dar apelul SomeMethod() are loc n afara seciunii critice.
Situaia a fost aproape rezolvat. Mai exist o problem mic. S cercetm mai atent Proc2():

void Proc2(IObject *pNewObject)


{
m_lockObject.Lock();
if (m_pObject.p)
m_pObject.p->Release();
m_pObject.p = pNewobject;
if (m_pObject.p)
m_pObject.p->AddRef();
m_lockObject.Unlock();
}

Este evident, c apelurile m_pObject.p->AddRef() i m_pObject.p->Release() au loc n interiorul seciunii


critice. i dac apelarea metodei AddRef() nu genereaz, de obicei probleme, apelarea metodei Release() poate fi
ultimul apel al Release() i obiectul se va autodistruge. n metoda FinalRelease() a obiectului #2 poate fi orice, de
exemplu, eliberarea unor obiecte, care se afl n alte compartimente. Dar aceasta din nou va conduce la comutarea
firelor i poate genera autoblocarea obiectului #1 ca i n exemplul de mai sus. Pentru a prentmpina aceasta vom folosi
aceeai tehnic ca i n Proc1().
// Firul #2
void Proc2(IObject *pNewObject)
{
CComPtr<IObject> pPrevObject;
m_lockObject.Lock();
pPrevObject.Attach(m_pObject.Detach());
m_pObject = pNewobject;
m_lockObject.Unlock();
}
Potenial, acum ultimul apel IObject2::Release() va fi executat dup prsirea seciunii critice. Iar atribuirea unei
valori noi este sincronizat ca i mai nainte cu apelul IObject2::SomeMethod() din firul #1.
Concluzii:
seciunile critice sunt executate relativ repede i nu cer multe resurse de sistem;
pentru sincronizarea accesrii a mai multor variabile independente este mai bine s fie utilizate cteva
seciuni critice (nu una pentru toate variabilele);
codul unei seciuni critice va fi redus la minimum;
nu este recomandat s fie apelate metode ale unor obiecte strine dintr-o seciune critic.

3.7. Exerciii la capitolul 3


Exerciiul 3.1. S se exprime n termeni de procese executarea programelor n corutine (v.2.1). Precizai contextul i
modul de sincronizare a acestor procese.

14.11.17 21:27 /conversion/tmp/scratch/371682329.doc p.31


Exerciiul 3.2. n programele cititorului i redactorului, prezentate n 3.4.2.2, cititorii pot ocupa resursa permanent,
blocnd pentru un timp redactorii.
1) Propunei o soluie cu prioritate pentru redactori n care accesarea resursei ar fi interzis unor noi cititori, dac
exist un redactor n ateptare.
2) Soluia de mai sus prezint un risc de ateptare pentru un timp nedefinit a cititorilor. Specificai un mod de
funcionare care ar trata ntr-o manier echitabil cititorii i redactorii i elaborai programul monitorului
respectiv.
Exerciiul 3.3. Buticul unui brbier este organizat conform schemei de mai jos:

Ua de intrare i cea de comunicare dintre sala de ateptare i salonul brbierului nu permit intrarea simultan a mai
multor clieni (doar unul singur poate trece prin fiecare ua la un moment de timp dat). Aceste ui au un mecanism
culisant, care face ca una din ele s fie nchis, atunci cnd cealalt este deschis. Brbierul invit urmtorul client,
atunci cnd termin deservirea clientului curent. Dac sala de ateptare este goal el doarme. Dac un client gsete
brbierul dormind, l trezete, altfel i ateapt rndul n sala de ateptare.
Reprezentnd brbierul i clienii prin procese, programai funcionarea acestui sistem cu ajutorul unui monitor.
Exerciiul 3.4. O linie de cale ferat, care leag dou orae A i B, conine o poriune cu un singur drum. Vom
reprezenta trenurile prin procese conform schemei de mai jos:
trenurile A B trenurile B A
... ...
sens_unic.intrare_vest sens_unic.intrare_est
<traversare sens unic> <traversare sens unic>
sens_unic.ieire_est sens_unic.ieire_vest
... ...

B
A
Rolul monitorului sens_unic este s garanteze c toate trenurile, angajate la un moment de timp dat pe poriunea cu
un singur drum, circul n aceeai direcie.
1) S se elaboreze programul monitorului sens_unic, presupunnd c numrul trenurilor prezente pe poriunea cu
un singur drum, nu este limitat.
2) Aceeai problema, dar pentru un numr limitat N de trenuri.
3) Examinai n ambele cazuri riscul de blocare i mijloacele de evitare ale acestora.
Exerciiul 3.5. Cinci filozofi stau la o mas conform schemei de mai jos.

Filosoful 0

Filosoful 4 Filosoful 1

Filosoful 3 Filosoful 2

14.11.17 21:27 /conversion/tmp/scratch/371682329.doc p.32


Fiecare, schematic prezentat printr-un proces pi, (i=0..4), se comport conform programului:
ciclu
gndire -- nu necesit vre-o resurs
mncare -- sunt necesare dou (ambele) beioare (pentru orez)
endciclu
Fiecare filozof poate utiliza doar cele dou furculie, plasate de o parte i alta a farfuriei sale, la terminare
ntorcndu-le n poziia iniial. Durata celor dou faze este arbitrar, dar finit.
1) S se arate, c alocarea furculielor poate conduce la blocarea sistemului (blocarea a dou sau mai multe
procese).
2) S se elaboreze un monitor furculie, care ar realiza alocarea furculielor fr blocare. Apelarea procedurii
mncare va fi nlocuit n programul procesului pi prin secvena
furculie.luare(i)
mncare
furculie.eliberare(i)
Apelarea procedurii luare l blocheaz pe apelant pentru perioada de timp n care cele dou furculie adiacente
farfuriei sale sunt utilizate de ctre vecinii si. Apelarea procedurii eliberare elibereaz dou furculie.
3) Examinai posibilitatea de blocare indefinit a unui proces i propunei soluii pentru a o evita.
Rezolvarea punctului 1. Blocare reciproc
Filozoful i
gndire();
P(furculia i);
P(furculia (i+1) mod 5);
mncare();
V(furculia i);
V(furculia (i+1) mod 5);
Dac fiecare filozof va lua n acelai moment de timp furculia din partea sa dreapt va avea loc o blocare reciproc.
Rezolvarea punctului 2.
Filozoful i luare_furculi(i) ntoarcer_furculi(i)
gndire(); P(mutex); P(mutex);
luare_furculi(i); stare[i] = FOAME; stare[i] = GNDIRE;
mncare(); test(i); test(STNGA);
ntoarcere_furculi(i); V(mutex); test(DREAPTA);
P(s[i]); V(mutex);
test(i)
if(stare[i] = FOAME && stare[DREAPTA] != MNCARE && stare[STNGA] != MNCARE) then
stare[i] = MNCARE;
V(s[i]);
Exerciiul 3.6. Specificai i elaborai cu ajutorul unui monitor dou primitive emitere i recepie, care realizeaz
comunicarea ntre dou procese via un tampon de capacitatea unui singur mesaj cu sincronizare prin metoda rendez-
vous.
Exerciiul 3.7. Programul de mai jos reprezint dou procese, care funcioneaz conform schemei productor-
consumator i sincronizate prin ateptare activ.
productor consumator
ciclu ciclu
test_c: if n=0 then
generare(mesaj);
test_p: if n=N then go to test_c
go to test_p endif;
endif; n:=n-1;
n:=n+1; ieire(mesaj);
intrare(mesaj); consumare(mesaj)
endciclu endciclu

14.11.17 21:27 /conversion/tmp/scratch/371682329.doc p.33


Procedurile intrare i ieire sunt cele din 3.4.3.1.
S se discute validitatea acestor programe i s se aduc, eventual, modificri necesare, presupunnd:
1) c operaiile de consultare, incrementare i decrementare a unei variabile n memorie sunt indivizibile,
2) c indivizibile sunt doar ncrcarea, ordonarea, incrementare i decrementarea unui registru; operaiile de
comparare, incrementare i decrementare a unei variabile necesit ncrcarea sa ntr-un registru.
Exerciiul 3.8. Programul de mai jos reprezint dou procese, care funcioneaz conform schemei productor-
consumator i sincronizate prin ateptare activ.
productor consumator
ciclu ciclu
generare(mesaj); test_c: if NC-NP0 then
test_p: if NP-NC N then go to test_c
go to test_p endif;
endif; ieire(mesaj);
intrare(mesaj); consumare(mesaj)
NP:=NP+1; NC:=NC+1
endciclu endciclu
S se discute validitatea acestor programe. Ce reprezint variabilele ntregi NP i NC i care sunt valorile lor
iniiale?
Exerciiul 3.9. Un sistem const din n cupluri de procese comunicante dou cte dou conform schemei productor-
consumator. Tampoanele sunt de lungime variabil i administrate dinamic. Atunci cnd un productor are nevoie de o
caset pentru a depozita un mesaj, el cere aceasta unei rezerve de casete, o umple i o pune n firul de ateptare al
tamponului su. Cnd un consunator a preluat un mesaj dintr-o caset, el restituie caseta rezervei de casete. Iniial
rezerva conine N casete, fiecare fiind identificat printr-o adres adr[0..N-1]. Elaborai monitorul gestionrii rezervei
de casete n urmtoarele dou ipoteze:
1) Toate cuplurile de procese sunt tratate identic.
2) Prioritatea cuplului este cu att mai mare cu ct mai puine tampoane cuplul ocup (aceast politic vizeaz
restabilirea echitii pentru cazul unor procese care deruleaz cu viteze diferite).
Exerciiul 3.10. S se programeze cu ajutorul monitoarelor problema de culegere a rezultatelor de msurare propus
n exerciiul 2.2.
Exerciiul 3.11. Modificai monitorul gestionrii unui periferic asociindu-i un monitor de gard, astfel ca tratarea
unei erori s fie declanat, dac transferul nu a fost terminat dup un interval dat de timp.
Exerciiul 3.12. Pentru realizarea unui server de imprimare se cere s se utilizeze metoda de buferizare, descris n
3.4.4. Un client se poate adresa acestui server printr-o comand imprimare(f), n care f este un nume de fiier.
Presupunem c serverul dispune de informaiile necesare citirii unei serii de linii, care formeaz fiierul f.
1) Elaborai programul serverului imprimantei.
2) Modificai acest program astfel nct clientului s i se permit:
anularea unei imprimri cerute, nainte ca ea s fi avut loc,
prevenirea clientului despre terminarea imprimrii fiierului prin depozitarea unui mesaj ntr-o cutie
potal specificat.
3) Modificai programul serverului pentru a permite mai multor imprimante s funcioneze n paralel.
Exerciiul 3.13. Vrem s construim un sistem de tratare a lucrrilor organizate pe loturi, analogic exerciiului 9.15.
Lucrrile sunt citite secvenial de un cititor de cartele, rezultatele sunt listate la o imprimant.
Sistemul este construit din trei procese: citire, care comand cititorul, execuie, care realizeaz execuia unei lucrri
i imprimare, care comand imprimarea. Procesul imprimare este de forma:
ciclu
<citire cartel>
<tratare cartel>
endciclu
programul tratare cartel poate conine apelri <imprimare linie>.

14.11.17 21:27 /conversion/tmp/scratch/371682329.doc p.34


1) Presupunem mai nti c toate intrrile-ieirile sunt tamponate n memoria central. Realizai cu ajutorul
monitoarelor cooperarea celor trei procese.
2) Modificai programul precedent pentru a putea trata cazul erorilor n cursul execuiei (presupunem, c procesul
citire poate detecta prima cartel a unei lucrri).
3) Modificai programul precedent pentru a integra aici administrarea consolei operatorului (cu specificaiile
exerciiului 9.15).
4) Modificai programele precedente pentru a introduce administrarea tamponat a intrrilor i ieirilor (exerciiul
precedent).
Exerciiul 3.14. Clienii unui server sunt mprii n dou clase, numite 1 i 2. Cererile clienilor din clasa 1 sunt
prioritare: serverul va trate o cerere din clasa 2 doar n lipsa de cereri de clasa 1.Cererile din aceeai clasa vor fi tratate
n ordinea sosirii. Un cmp al cererii identific prioritatea.
1) Programai sincronizarea clienilor i a serverului utiliznd monitoare. S se foloseasc un fir de ateptare
pentru fiecare clas de cereri i un proces dispecer pentru alimentarea acestor fire.
2) Schema de mai sus conine un risc de blocare. Modificai specificaiile i realizarea pentru a elimina acest risc,
pstrnd tratarea privilegiat a cererilor din clasa 1.

14.11.17 21:27 /conversion/tmp/scratch/371682329.doc p.35

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

  • Lucrare de Laborator NR. 2: Sisteme Multimedia
    Lucrare de Laborator NR. 2: Sisteme Multimedia
    Document4 pagini
    Lucrare de Laborator NR. 2: Sisteme Multimedia
    fr4nky95
    Încă nu există evaluări
  • Cap 1
    Cap 1
    Document21 pagini
    Cap 1
    DumitruGuba
    Încă nu există evaluări
  • PSI ParteI V 30 09 2017
    PSI ParteI V 30 09 2017
    Document200 pagini
    PSI ParteI V 30 09 2017
    Chicu Roman
    Încă nu există evaluări
  • Laboratorul6 BDC
    Laboratorul6 BDC
    Document6 pagini
    Laboratorul6 BDC
    fr4nky95
    Încă nu există evaluări
  • Lab 7 C
    Lab 7 C
    Document5 pagini
    Lab 7 C
    fr4nky95
    Încă nu există evaluări
  • Cap 6
    Cap 6
    Document25 pagini
    Cap 6
    fr4nky95
    Încă nu există evaluări
  • PR Lab2-3
    PR Lab2-3
    Document5 pagini
    PR Lab2-3
    fr4nky95
    Încă nu există evaluări
  • PR Lab2-3
    PR Lab2-3
    Document5 pagini
    PR Lab2-3
    fr4nky95
    Încă nu există evaluări
  • SM12
    SM12
    Document6 pagini
    SM12
    fr4nky95
    Încă nu există evaluări
  • Cap 5
    Cap 5
    Document20 pagini
    Cap 5
    fr4nky95
    Încă nu există evaluări
  • Untitled 10
    Untitled 10
    Document5 pagini
    Untitled 10
    fr4nky95
    Încă nu există evaluări
  • Metode Numerice Lab 1
    Metode Numerice Lab 1
    Document6 pagini
    Metode Numerice Lab 1
    fr4nky95
    Încă nu există evaluări
  • AMSI Lab6
    AMSI Lab6
    Document6 pagini
    AMSI Lab6
    fr4nky95
    Încă nu există evaluări
  • 612
    612
    Document73 pagini
    612
    verginarojnita
    Încă nu există evaluări
  • Cap 4
    Cap 4
    Document18 pagini
    Cap 4
    fr4nky95
    Încă nu există evaluări
  • Cap. 2
    Cap. 2
    Document26 pagini
    Cap. 2
    Andrei Carp
    Încă nu există evaluări
  • Partea 5 Mec Cuantica Tiparim
    Partea 5 Mec Cuantica Tiparim
    Document38 pagini
    Partea 5 Mec Cuantica Tiparim
    fr4nky95
    Încă nu există evaluări
  • Oceanul Pacific
    Oceanul Pacific
    Document6 pagini
    Oceanul Pacific
    fr4nky95
    Încă nu există evaluări
  • Laborator 1 Conditii
    Laborator 1 Conditii
    Document7 pagini
    Laborator 1 Conditii
    fr4nky95
    Încă nu există evaluări
  • Programa FCIM Nu Tiparim PDF
    Programa FCIM Nu Tiparim PDF
    Document24 pagini
    Programa FCIM Nu Tiparim PDF
    fr4nky95
    Încă nu există evaluări
  • ACSO Cuprins
    ACSO Cuprins
    Document9 pagini
    ACSO Cuprins
    DumitruGuba
    Încă nu există evaluări
  • Economia Curs2 1
    Economia Curs2 1
    Document10 pagini
    Economia Curs2 1
    fr4nky95
    Încă nu există evaluări
  • Mecanica CIM 2017 Nu Tiparim PDF
    Mecanica CIM 2017 Nu Tiparim PDF
    Document37 pagini
    Mecanica CIM 2017 Nu Tiparim PDF
    Dorin Gribincea
    Încă nu există evaluări
  • 4 Oscilatii Si Unde - Tiparim PDF
    4 Oscilatii Si Unde - Tiparim PDF
    Document55 pagini
    4 Oscilatii Si Unde - Tiparim PDF
    fr4nky95
    Încă nu există evaluări
  • Tema 12
    Tema 12
    Document1 pagină
    Tema 12
    fr4nky95
    Încă nu există evaluări
  • Oceanul Pacific
    Oceanul Pacific
    Document6 pagini
    Oceanul Pacific
    fr4nky95
    Încă nu există evaluări
  • Economia Curs2 1
    Economia Curs2 1
    Document9 pagini
    Economia Curs2 1
    fr4nky95
    Încă nu există evaluări
  • Economia Ramurii Conspect
    Economia Ramurii Conspect
    Document32 pagini
    Economia Ramurii Conspect
    fr4nky95
    Încă nu există evaluări
  • Lab 2
    Lab 2
    Document2 pagini
    Lab 2
    fr4nky95
    Încă nu există evaluări