Sunteți pe pagina 1din 223

Sisteme de operare

Cursul 6 - Comunicarea ntre procese


Dr
agulici Dumitru Daniel
Facultatea de matematic
a si informatic
a,
Universitatea Bucuresti

2008

Instrumente de comunicare intre procese


Conditii de curs
a
Regiuni critice
Dezactivarea ntreruperilor
Variabile z
avor
Alternarea strict
a
Solutia lui Peterson
Instructiunea TSL
Sleep si Wakeup
Semafoare
Mutex
Monitoare
Transfer de mesaje
Bariere
Probleme clasice ale comunicarii interprocese
Problema filozofilor care mananc
a
Problema cititorilor si scriitorilor
Problema frizerului somnoros
Cazul UNIX/Linux
Semnale
IPC
IPC - segmente de memorie partajat
a
IPC - vectori de semafoare
IPC - cozi de mesaje
Fisiere tub

Cuprins

Comunicarea intre procese


Probleme importante legate de comunicarea ntre procese:
- cum poate un proces s
a trimit
a informatia c
atre altul;
- evitarea situatiei c
and procesele si afecteaz
a reciproc, ntr-un mod nedorit,
executarea activit
atilor critice (de ex. dou
a procese ncearc
a n acelasi timp s
a
aloce ultimul MB de memorie);
- serializarea corect
a atunci c
and exist
a anumite dependente: dac
a procesul A
produce datele si procesul B le tip
areste, B va trebui s
a astepte ca A s
a
produc
a date nainte de a ncepe s
a tip
arireasc
a.
Ultimile dou
a probleme se reg
asesc si la thread-uri. Prima problem
a (trimiterea
informatiei) este usor de rezolvat n cazul thread-urilor unui acelasi proces,
deoarece ele partajaz
a un spatiu comun de adrese; dac
a thread-urile sunt din
procese diferite, abordarea este aceeasi ca n cazul proceselor.
In cele ce umeaz
a vom trata cazul proceselor, dar not
am c
a aceleasi probleme
si solutii exist
a si n cazul thread-urilor.

Instrumente de comunicare intre procese


Conditii de curs
a
Regiuni critice
Dezactivarea ntreruperilor
Variabile z
avor
Alternarea strict
a
Solutia lui Peterson
Instructiunea TSL
Sleep si Wakeup
Semafoare
Mutex
Monitoare
Transfer de mesaje
Bariere
Probleme clasice ale comunicarii interprocese
Problema filozofilor care mananc
a
Problema cititorilor si scriitorilor
Problema frizerului somnoros
Cazul UNIX/Linux
Semnale
IPC
IPC - segmente de memorie partajat
a
IPC - vectori de semafoare
IPC - cozi de mesaje
Fisiere tub

Cuprins

Conditii de cursa

Conditii de curs
a (race conditions) = situatii n care dou
a sau mai multe
procese citesc sau scriu date partajate si rezultatul final depinde de cine se
execut
a exact c
and.
Detectarea greselilor n programare care contin conditii de curs
a este dificil
arezultatele celor mai multe teste de executie sunt bune, dar odat
a la foarte
mult timp ceva ciudat si inexplicabil se nt
ampl
a.

Conditii de cursa
Exemplu:
out=4

in=7

?
fis1

?
fis2

fis3

Presupunem c
a printarea fisierelor se face folosind:
- un tabel organizat ca o coad
a, cu intrari pentru fisierele ce urmeaz
a a fi
printate; el are intr
arile numerotate 0, ..., N-1;
- dou
a variabile in si out, retin
and prima pozitie ocupat
a, respectiv prima
pozitie liber
a; ele sunt accesibile tuturor proceselor (de ex. se afl
a ntr-un fisier
cu dou
a cuvinte);
- un proces care periodic verific
a dac
a exist
a fisiere n coad
a (in 6= out) si dac
a
da, printeaz
a fisierul de pe pozitia out si incrementeaz
a out cu 1 modulo N
(out=(out+1)%N);
Presupunem ca N=100 iar la momentul curent in=7, out=4.

Conditii de cursa
Exemplu:
out=4

in=7

?
fis1

?
fis2

fis3

Presupunem c
a avem dou
a procese (de ex. comenzi de printare de la doi
utilizatori) care urm
aresc s
a trimit
a la printare c
ate un fisier:
proces A: citeste(&urmatorul,in);
insereaza(fis_a,tabel[urmatorul]);
scrie((urmatorul+1)%N,in);
proces B: citeste(&urmatorul,in);
insereaza(fis_b,tabel[urmatorul]);
scrie((urmatorul+1)%N,in);

Conditii de cursa
Exemplu:
out=4

in=7

?
fis1

?
fis2

fis3

Executate n paralelism intercalat, planificatorul poate decide urm


atoarea
secvent
a de executare a celor dou
a procese:
proces A
proces B
--------------------------------------------------------------------------citeste(&urmatorul,in);
|
(urmatorul=7)
|
--------------------------------------------------------------------------|
citeste(&urmatorul,in);
|
(urmatorul=7)
--------------------------------------------------------------------------insereaza(fis_a,tabel[urmatorul]);
|
scrie((urmatorul+1)%N,in);
|
(tabel[7]=fis_a, in=8)
|
--------------------------------------------------------------------------|
insereaza(fis_b,tabel[urmatorul]);
|
scrie((urmatorul+1)%N,in);
|
(tabel[7]=fis_b, in=8)
---------------------------------------------------------------------------

Conditii de cursa
Exemplu:
out=4

in=7

?
fis1

?
fis2

fis3

out=4

in=8

?
fis1

?
fis2

fis3

fis b

Consecint
a: fisierul fis a nu se mai printeaz
a (utilizatorul care a dat comanda
asteapt
a degeaba l
ang
a imprimant
a).

Instrumente de comunicare intre procese


Conditii de curs
a
Regiuni critice
Dezactivarea ntreruperilor
Variabile z
avor
Alternarea strict
a
Solutia lui Peterson
Instructiunea TSL
Sleep si Wakeup
Semafoare
Mutex
Monitoare
Transfer de mesaje
Bariere
Probleme clasice ale comunicarii interprocese
Problema filozofilor care mananc
a
Problema cititorilor si scriitorilor
Problema frizerului somnoros
Cazul UNIX/Linux
Semnale
IPC
IPC - segmente de memorie partajat
a
IPC - vectori de semafoare
IPC - cozi de mesaje
Fisiere tub

Cuprins

Regiuni critice
Regiune critic
a (critical region) sau sectiune critic
a (critical section) =
parte din program care acceseaz
a o resurs
a partajat
a (structur
a de date sau
dispozitiv).
Excluderea mutual
a (mutual exclusion) = mpiedicarea folosirii simultane de
c
atre mai multe procese a unei resurse partajate.
Dac
a am putea face ca oricare dou
a procese ce partajaz
a anumite resurse s
a nu
fie niciodat
a n acelasi timp n regiunile lor critice, am putea evita cursele.
Desi aceast
a cerint
a evit
a conditiile de curs
a, ea nu este suficient
a pentru ca
procesele paralele s
a coopereze corect si eficient folosind datele partajate.
Pentru a avea o solutie bun
a, trebuie ndeplinite patru conditii:
1. Oricare dou
a procese nu se pot afla simultan n regiunile lor critice.
2. Nici un fel de presupunere nu se poate face asupra vitezelor sau num
arului
de procesoare.
3. Nici un proces care ruleaz
a n afara regiunii sale critice nu poate bloca alt
proces s
a intre n regiunea sa critic
a.
4. Nici un proces nu trebuie s
a astepte la infinit pentru a intra n regiunea sa
critic
a.

Regiuni critice
Exemplu de comportament pe care ni-l dorim:
A intr
a n regiunea critic
a
Proces A

A iese din regiunea critic


a


B intr
a
n regiunea
critic
a

B ncearc
a
s
a intre n
regiunea critic
a

Proces B


|
T1

T2
Timp

{z
B blocat

B iese din
regiunea
critic
a

}
T3

T4

Regiuni critice

In cele ce urmeaz
a prezent
am c
ateva metode de realizare a excluderii mutuale,
a.. mai multe procese ce acceseaz
a o resurs
a partajat
a sa nu se poata afla
simultan n regiunea lor critic
a.

Instrumente de comunicare intre procese


Conditii de curs
a
Regiuni critice
Dezactivarea ntreruperilor
Variabile z
avor
Alternarea strict
a
Solutia lui Peterson
Instructiunea TSL
Sleep si Wakeup
Semafoare
Mutex
Monitoare
Transfer de mesaje
Bariere
Probleme clasice ale comunicarii interprocese
Problema filozofilor care mananc
a
Problema cititorilor si scriitorilor
Problema frizerului somnoros
Cazul UNIX/Linux
Semnale
IPC
IPC - segmente de memorie partajat
a
IPC - vectori de semafoare
IPC - cozi de mesaje
Fisiere tub

Cuprins

Dezactivarea ntreruperilor

Metoda: procesul dezactiveaz


a ntreruperile imediat ce a intrat n regiunea
critic
a si le reactiveaz
a imediat nainte de a iesi din ea.
Astfel, pe parcursul regiunii critice procesul nu mai poate fi ntrerupt de nucleu
pentru a comuta procesorul la alt proces (comutarea ntre procese este
rezultatul ntreruperilor de ceas sau de alt tip, iar acestea sunt dezactivate) deci nici un alt proces nu l mai poate incomoda.

Dezactivarea ntreruperilor

Avantaje/dezavantaje:
- nu este bine sa dai proceselor utilizator puterea de a dezactiva ntreruperile;
dac
a nu le mai reactiveaz
a?
- dac
a sistemul are mai multe procesoare, dezactivarea ntreruperilor afecteaz
a
doar procesorul care a executat instructiunea de dezactivare; celelalte
procesoare vor rula n continuare procese, care ar putea accesa resursele
partajate;
- pentru nucleul SO este convenabil s
a dezactiveze ntreruperile c
at timp
execut
a c
ateva instructiuni de actualizare a unor variabile sau liste; dac
a de ex.
ar ap
area o ntrerupere c
at timp lista proceselor gata de executie este ntr-o
stare inconsistent
a, ar putea ap
area conditii de curs
a.
Concluzie: metoda este adesea util
a n cadrul nucleului SO, dar nu este
adecvat
a ca mecanism general de excludere mutual
a pentru procesele utilizator.

Instrumente de comunicare intre procese


Conditii de curs
a
Regiuni critice
Dezactivarea ntreruperilor
Variabile z
avor
Alternarea strict
a
Solutia lui Peterson
Instructiunea TSL
Sleep si Wakeup
Semafoare
Mutex
Monitoare
Transfer de mesaje
Bariere
Probleme clasice ale comunicarii interprocese
Problema filozofilor care mananc
a
Problema cititorilor si scriitorilor
Problema frizerului somnoros
Cazul UNIX/Linux
Semnale
IPC
IPC - segmente de memorie partajat
a
IPC - vectori de semafoare
IPC - cozi de mesaje
Fisiere tub

Cuprins

Variabile zavor

Metoda: se foloseste o variabil


a partajata (z
avor) av
and valoarea initial
a 0;
c
and un proces vrea s
a intre n regiunea critic
a, verific
a zavorul; dac
a este 0, l
face 1, intr
a n regiunea critic
a, iar la sf
arsit l face 0; dac
a este deja 1,
asteapt
a s
a devin
a 0.
Deci z
avor = 0 nseamn
a c
a nici un proces nu este n regiunea critic
a, z
avor =
1 nseamn
a c
a exist
a un proces aflat n regiunea critic
a.
Aceasta metod
a contine aceeasi gresala fatal
a ca n exemplul anterior cu
printarea fisierelor: de ex. un proces poate observa c
a z
avor = 0, intr
a pe
ramura ce duce c
atre regiunea critic
a, si nainte de a apuca s
a fac
a z
avor = 1,
planificatorul l ntrerupe, comut
a la alt proces, care observ
and c
a z
avor = 0
(nc
a) intr
a pe ramura ce duce c
atre regiunea critic
a, etc. - astfel ambele
procese se vor putea afla simultan n regiunea critic
a.

Urm
atoarele metode de obtinere a excluderii mutuale sunt corecte si se bazeaz
a
pe asteptarea ocupat
a.
In esent
a, ele fac urm
atorul lucru: c
and un proces vrea s
a intre n regiunea sa
critic
a, verific
a dac
a accesul i este permis; dac
a nu, asteapt
a s
a i fie permis,
execut
and o bucl
a str
ans
a.

Instrumente de comunicare intre procese


Conditii de curs
a
Regiuni critice
Dezactivarea ntreruperilor
Variabile z
avor
Alternarea strict
a
Solutia lui Peterson
Instructiunea TSL
Sleep si Wakeup
Semafoare
Mutex
Monitoare
Transfer de mesaje
Bariere
Probleme clasice ale comunicarii interprocese
Problema filozofilor care mananc
a
Problema cititorilor si scriitorilor
Problema frizerului somnoros
Cazul UNIX/Linux
Semnale
IPC
IPC - segmente de memorie partajat
a
IPC - vectori de semafoare
IPC - cozi de mesaje
Fisiere tub

Cuprins

Alternarea stricta

Metoda foloseste o variabil


a partajat
a care retine al cui este r
andul, iar fiecare
proces naintea regiunii critice asteapta s
a aibe valoarea potrivit
a, intr
a n
regiunea critic
a, iar la iesire i schimb
a valoarea pentru a-i da voie procesului
cel
alalt.

Alternarea stricta
Exemplu (atentie la ; de dup
a instructiunile while interioare !):
while(TRUE){
while(turn!=0); /* loop */
critical_region();
turn=1;
noncritical_region();
}
procesul (a)

while(TRUE){
while(turn!=1); /* loop */
critical_region();
turn=0;
noncritical_region();
}
procesul (b)

Metoda evit
a cursele, dar nu garanteaz
a respectarea conditiei 3 de mai nainte
(anume, nici un proces care ruleaz
a n afara regiunii lui critice nu poate bloca
alte procese).

Alternarea stricta
Exemplu (atentie la ; de dup
a instructiunile while interioare !):
while(TRUE){
while(turn!=0); /* loop */
critical_region();
turn=1;
noncritical_region();
}
procesul (a)

while(TRUE){
while(turn!=1); /* loop */
critical_region();
turn=0;
noncritical_region();
}
procesul (b)

De exemplu, mai sus este posibil urm


atorul scenariu:
- (b) face turn=0, apoi intr
a n noncritical region(), care presupunem
c
a dureaz
a mult;
- ntre timp (a) (av
and turn=0), efectueaz
a repede critical region(),
turn=1, noncritical region(), apoi ajunge la while(turn!=1);
- ntruc
at (b) nc
a ruleaz
a noncritical region(), dureaz
a mult p
an
a va
face turn=0, asa c
a (a) va astepta mult p
an
a s
a intre iar n
critical region().
Deci (b), aflat n regiunea necritic
a, l mpiedic
a pe (a) s
a intre n regiunea
critic
a.

Alternarea stricta

Astfel, metoda nu este util


a c
and unul dintre procese este mult mai lent dec
at
celalalt.
De asemenea, metoda impune proceselor s
a alterneze strict efectuarea
regiunilor critice; aceasta poate cauza limit
ari - de exemplu, n exemplul
anterior cu printarea fisierelor, alternarea strict
a ar mpiedica un proces s
a
nregistreze consecutiv dou
a fisiere la printare.
Verificarea n continuu a unei variabile p
an
a c
and are o anumit
a valoare s.n.
asteptare ocupat
a (busy waiting); ea trebuie n general evitat
a, deoarece
iroseste timp pe procesor; este util
a doar c
and probabilitatea ca asteptarea s
a
fie scurt
a este rezonabil
a.
Un z
avor care foloseste asteptarea ocupat
a se numeste spin lock.

Instrumente de comunicare intre procese


Conditii de curs
a
Regiuni critice
Dezactivarea ntreruperilor
Variabile z
avor
Alternarea strict
a
Solutia lui Peterson
Instructiunea TSL
Sleep si Wakeup
Semafoare
Mutex
Monitoare
Transfer de mesaje
Bariere
Probleme clasice ale comunicarii interprocese
Problema filozofilor care mananc
a
Problema cititorilor si scriitorilor
Problema frizerului somnoros
Cazul UNIX/Linux
Semnale
IPC
IPC - segmente de memorie partajat
a
IPC - vectori de semafoare
IPC - cozi de mesaje
Fisiere tub

Cuprins

Solutia lui Peterson

Metode de obtinere a excluderii mutuale f


ar
a alternare strict
a au obtinut
T.Dekker (matematician olandez), apoi (mai simplu) G.L.Peterson (n 1981).
Algoritmul lui Peterson este format din dou
a proceduri ce trebuie executate n
fiecare proces la intrarea, respectiv iesirea din regiunea critic
a si este ilustrat
mai jos (fiecare proces apeleaz
a procedurile cu num
arul s
au de ordine, 0 sau 1):

Solutia lui Peterson


/* date partajate */
#define FALSE 0
#define TRUE 1
int turn;
/* al cui e randul ? */
int interested[2]={FALSE,FALSE}; /* toate valorile sunt initial 0 (FALSE) */
/* proceduri prezente in fiecare proces */
void enter_region(int process){
/* procesul este 0 sau 1 */
int other;
/* numarul celuilalt proces */
other=1-process;
interested[process]=TRUE;
/* arata ca esti interesat */
turn=process;
/* te inscrii la rand */
while(turn==process && interested[other]==TRUE); /* cicleaza instr.vida */
}
void leave_region(int process){
/* process: cine pleaca */
interested[process]=FALSE;
/* indica plecarea din regiunea critica */
}

Obs: nainte de a intra n regiunea critic


a, un proces process si manifest
a
interesul set
and interested[process]=TRUE, iar dup
a iesirea din regiunea
critic
a seteaz
a interseted[process]=FALSE; turn retine ultimul proces
interesat; un proces interesat intr
a n regiunea critic
a doar dac
a cel
alalt nu este
interesat (n particular nu este n sectiunea sa critic
a), sau este interesat dar s-a
anuntat n turn mai t
arziu.

Instrumente de comunicare intre procese


Conditii de curs
a
Regiuni critice
Dezactivarea ntreruperilor
Variabile z
avor
Alternarea strict
a
Solutia lui Peterson
Instructiunea TSL
Sleep si Wakeup
Semafoare
Mutex
Monitoare
Transfer de mesaje
Bariere
Probleme clasice ale comunicarii interprocese
Problema filozofilor care mananc
a
Problema cititorilor si scriitorilor
Problema frizerului somnoros
Cazul UNIX/Linux
Semnale
IPC
IPC - segmente de memorie partajat
a
IPC - vectori de semafoare
IPC - cozi de mesaje
Fisiere tub

Cuprins

Instructiunea TSL
Multe calculatoare (mai ales cele cu mai multe procesoare) au o instructiune
hardware TSL (Test and Set Lock - testeaz
a si seteaz
a z
avor):
TSL RX,LOCK

care citeste n registrul RX continutul z


avorului LOCK (word aflat n
memorie), apoi salveaz
a o valoare nenul
a n z
avor; citirea si salvarea sunt
indivizibile (nici un procesor nu poate accesa word-ul din memorie p
an
a nu se
termin
a instructiunea).
Procesorul care execut
a TSL blocheaza magistrala memoriei pentru a
interzice altor procesoare s
a acceseze memoria p
ana c
and se termin
a
instructiunea.
Metoda de obtinere a excluderii mutuale bazat
a pe TSL foloseste o variabil
a
partajat
a LOCK pentru a coordona accesul la resursele partajate; c
and LOCK
este 0, orice proces o poate seta la 1 cu TSL, apoi s
a acceseze resursele
partajate, apoi s
a o reseteze la 0 cu move; dac
a un proces vrea s
a acceseze
resursele partajate dar LOCK este 1, execut
a asteptare ocupat
a p
an
a devine 0.

Instructiunea TSL
Exemplu (ntr-un limbaj assembler fictiv dar tipic):
enter_region:
TSL REGISTER,LOCK
CMP REGISTER,#0
JNE enter_region
RET

|
|
|
|

leave_region:
MOVE LOCK,#0
RET

| salveaza 0 in zavor
| return catre apelant

copiaza zavorul in registru, apoi seteaza zavorul la 1


zavorul a fost 0 ?
daca nu a fost 0, cicleaza (asteptare ocupata)
return catre apelant; se intra in regiunea critica

Fiecare proces, nainte de sectiunea critic


a va apela enter_region(), la
revenire va executa (cu z
avorul setat la 1) regiunea critic
a, apoi va apela
leave_region().
Faptul c
a instructiunea TSL execut
a citirea si modificarea z
avorului n mod
hardware indivizibil, elimin
a posibilitatea c
a ntre cele dou
a operatii sistemul s
a
comute la alt proces care s
a consulte / modifice si el z
avorul - deci nu apar
conditii de curs
a.

At
at solutia lui Peterson c
at si cea care foloseste TSL sunt corecte, dar necesit
a
asteptarea ocupat
a - procesele asteapta permisiunea de a intra n regiunea
critic
a cicl
and ntr-o bucl
a strans
a.
Pe lang
a faptul c
a iroseseste timp pe procesor, asteptarea ocupat
a poate avea
efecte neasteptate. De ex. presupunem c
a avem doua procese: H cu prioritate
mare si L cu prioritate mic
a, iar regulile de planificare sunt a.. H s
a fie executat
de fiecare dat
a c
and este n starea de gata de executie (ready). Atunci, dac
a la
un moment dat L este n regiunea critic
a iar H devine ready (de exemplu a
terminat o operatie de I/O), planificatorul comut
a la H, care ncepe asteptarea
ocupat
a, si atunci L nu va termina niciodat
a sectiunea critic
a (deoarece n
timpul ciclului de asteptare H nu ajunge niciodat
a sleeping, si astfel procesorul
este alocat numai lui H), iar H va astepta la nesf
arsit.
Aceast
a situatie se numeste uneori problema inversiunii de prioritate (priority
inversion problem).
In continuare vom prezenta c
ateva primitive de comunicare interprocese care
fac ca procesul s
a se blocheze n loc s
a consume timp procesor, atunci c
and nu
i se permite intrarea n regiunea critic
a.

Instrumente de comunicare intre procese


Conditii de curs
a
Regiuni critice
Dezactivarea ntreruperilor
Variabile z
avor
Alternarea strict
a
Solutia lui Peterson
Instructiunea TSL
Sleep si Wakeup
Semafoare
Mutex
Monitoare
Transfer de mesaje
Bariere
Probleme clasice ale comunicarii interprocese
Problema filozofilor care mananc
a
Problema cititorilor si scriitorilor
Problema frizerului somnoros
Cazul UNIX/Linux
Semnale
IPC
IPC - segmente de memorie partajat
a
IPC - vectori de semafoare
IPC - cozi de mesaje
Fisiere tub

Cuprins

Sleep si Wakeup

Unele dintre cele mai simple primitive de comunicare ntre procese sunt
perechea sleep si wakeup.
sleep este un apel sistem ce blocheaz
a procesul apelant (n starea
suspendat), p
an
a c
and un alt proces l trezeste.
wakeup trezeste un proces (primit ca parametru) din suspendarea produs
a de
un sleep.
Alternativ, at
at sleep c
at si wakeup au fiecare c
ate un parametru const
and
ntr-o adres
a de memorie utilizat
a pentru a mperechea apelurile de sleep cu
cele de wakeup.

Pb. producator-consumator cu sleep/wakeup

Aplicatie la problema producator-consumator (producer-consumer)


(cunoscut
a si ca problema zonei tampon finite (bounded-buffer)): dou
a
procese partajaz
a o zon
a tampon cu lungime fix
a; unul (producatorul)
introduce date n el, cel
alalt (consumatorul) le extrage. Problema se poate
generaliza la m producatori si n consumatori.
Dac
a producatorul vrea s
a introduc
a un element iar zona e plin
a, sau dac
a
consumatorul vrea sa extrag
a un element iar zona e goal
a, apar probleme; n
fiecare caz procesul trebuie s
a se blocheze p
an
a cand celalalt creaz
a conditiile
necesare, apoi s
a fie trezit de acesta. De asemenea, pot ap
area conditii de
curs
a similare celor din exemplul de mai devreme cu printarea fisierelor.

Pb. producator-consumator cu sleep/wakeup


O rezolvare INCORECTA ce utilizeaz
a sleep si wakeup este urm
atoarea:
/* date partajate */
#define N 100 /* numarul de locatii din zona tampon */
int count=0;
/* numarul de elemente din zona tampon */
/* procesul producator */
void producer(void){
int item;
while(TRUE){
item=produce_item();
if(count==N)sleep();
insert_item(item);
count=count+1;
if(count==1)wakeup(consumer);
}
}

/* procesul consumator */
void consumer(void){
int item;
while(TRUE){
if(count==0)sleep();
item=remove_item();
count=count-1;
if(count==N-1)wakeup(producer);
consume_item(item);
}
}

Apelurile sleep si wakeup nu sunt n biblioteca standard C, dar n


UNIX/Linux se pot implementa prin trimiterea cu apelul kill(), sau
autotrimiterea cu apelul raise() a semnalelor SIGSTOP si SIGCONT de
suspendare / trezire a procesului (a se vedea cursul 5).

Pb. producator-consumator cu sleep/wakeup


O rezolvare INCORECTA ce utilizeaz
a sleep si wakeup este urm
atoarea:
/* date partajate */
#define N 100 /* numarul de locatii din zona tampon */
int count=0;
/* numarul de elemente din zona tampon */
/* procesul producator */
void producer(void){
int item;
while(TRUE){
item=produce_item();
if(count==N)sleep();
insert_item(item);
count=count+1;
if(count==1)wakeup(consumer);
}
}

/* procesul consumator */
void consumer(void){
int item;
while(TRUE){
if(count==0)sleep();
item=remove_item();
count=count-1;
if(count==N-1)wakeup(producer);
consume_item(item);
}
}

Functia produce item() genereaz


a urm
atorul element, insert item()
introduce un elemet n zona tampon, remove item() scoate un element din
zona tampon, consume item() efectueaz
a o prelucrare oarecare, de ex.
tipareste un element.

Pb. producator-consumator cu sleep/wakeup


O rezolvare INCORECTA ce utilizeaz
a sleep si wakeup este urm
atoarea:
/* date partajate */
#define N 100 /* numarul de locatii din zona tampon */
int count=0;
/* numarul de elemente din zona tampon */
/* procesul producator */
void producer(void){
int item;
while(TRUE){
item=produce_item();
if(count==N)sleep();
insert_item(item);
count=count+1;
if(count==1)wakeup(consumer);
}
}

/* procesul consumator */
void consumer(void){
int item;
while(TRUE){
if(count==0)sleep();
item=remove_item();
count=count-1;
if(count==N-1)wakeup(producer);
consume_item(item);
}
}

C
and producatorul detecteaz
a n final count==1, nseamn
a c
a nainte de
inserarea item-ului curent zona tampon era goal
a, deci consumatorul care o
golise intrase n sleep, si atunci (av
and deja un element n zona tampon) l
trezeste cu wakeup(consumer). El mai poate eventual s
a insereze c
ateva
elemente nainte ca planificatorul s
a comute pe consumator.

Pb. producator-consumator cu sleep/wakeup


O rezolvare INCORECTA ce utilizeaz
a sleep si wakeup este urm
atoarea:
/* date partajate */
#define N 100 /* numarul de locatii din zona tampon */
int count=0;
/* numarul de elemente din zona tampon */
/* procesul producator */
void producer(void){
int item;
while(TRUE){
item=produce_item();
if(count==N)sleep();
insert_item(item);
count=count+1;
if(count==1)wakeup(consumer);
}
}

/* procesul consumator */
void consumer(void){
int item;
while(TRUE){
if(count==0)sleep();
item=remove_item();
count=count-1;
if(count==N-1)wakeup(producer);
consume_item(item);
}
}

Similar, c
and consumatorul detecteaz
a count==N-1, nseamn
a c
a nainte de
consumarea item-ului curent zona tampon era plin
a, deci producatorul care o
umpluse intrase n sleep si atunci l trezeste.

Pb. producator-consumator cu sleep/wakeup


/* producator */
while(TRUE){
item=produce_item();
if(count==N)sleep();
insert_item(item);
count=count+1;
if(count==1)wakeup(consumer);
}

/* consumator */
while(TRUE){
if(count==0)sleep();
item=remove_item();
count=count-1;
if(count==N-1)wakeup(producer);
consume_item(item);
}

In rezolvarea anterioar
a conditia de curs
a poate ap
area deoarece accesul la
count nu este restrictionat. Putem avea de ex. urm
atorul scenariu:
- consumatorul detecteaz
a count==0 si se nscrie pe ramura DA; nainte
de a intra n sleep, planificatorul comut
a la producator;
- producatorul insereaz
a un element, incremeneteaz
a count, si constat
and c
a
count==1 trimite semnalul de trezire c
atre consumator; planificatorul comut
a
la consumator;
- consumatorul primeste semnalul de trezire, dar nefiind logic suspendat (nu a
ajuns nc
a la sleep), semnalul se pierde; apoi consumatorul ajunge si intr
a n
sleep;
- dup
a un timp producatorul umple zona tampon si intr
a si el n sleep.
Astfel, ambele procese ram
an blocate definitiv.

Pb. producator-consumator cu sleep/wakeup

Esenta problemei este ca un semnal de trezire venit c


and procesul nu era
suspendat logic este pierdut. Am putea ad
auga un bit de asteptare a trezirii
(wakeup waiting bit), care devine 1 c
and procesul primeste un semnal de
trezire si nu este suspendat logic; ulterior, dac
a procesul ncearc
a un sleep si
bitul este 1, apelul sleep nu va suspenda procesul ci doar va face bitul 0.
Aceasta rezolv
a problema n cazul a dou
a procese, dar pentru mai multe
procese (si deci mai multe semnale ce pot veni c
and nu sunt asteptate) un
singur bit nu este suficient; am putea ad
auga mai multi biti, dar problema de
principiu r
am
ane. Pentru rezolvarea ei au fost introduse semafoarele ...

Instrumente de comunicare intre procese


Conditii de curs
a
Regiuni critice
Dezactivarea ntreruperilor
Variabile z
avor
Alternarea strict
a
Solutia lui Peterson
Instructiunea TSL
Sleep si Wakeup
Semafoare
Mutex
Monitoare
Transfer de mesaje
Bariere
Probleme clasice ale comunicarii interprocese
Problema filozofilor care mananc
a
Problema cititorilor si scriitorilor
Problema frizerului somnoros
Cazul UNIX/Linux
Semnale
IPC
IPC - segmente de memorie partajat
a
IPC - vectori de semafoare
IPC - cozi de mesaje
Fisiere tub

Cuprins

Semafoare

Semaforul a fost introdus de E.W.Dijkstra n 1965, ca o variabil


a ntreag
a
utilizat
a pentru num
ararea semnalelor de trezire venite ce asteapt
a s
a fie
folosite; valorile sale sunt 0.
Dijkstra a propus dou
a operatii down si up (ce generalizeaz
a sleep si
wakeup) definite astfel:
- down asupra unui semafor verific
a dac
a semaforul are valoarea > 0;
dac
a da, o decrementeaz
a cu 1 (deci foloseste unul dintre semnalele de
trezire), iar procesul apelant continu
a (nu se blocheaz
a);
dac
a nu, procesul apelant se blocheaz
a (n starea sleeping) (f
ar
a a ncheia
deocamdat
a operatia down);
verificarea valorii, decrementarea ei si eventuala blocare a procesului sunt
efectuate ca o singur
a actiune atomic
a;

Semafoare

Semaforul a fost introdus de E.W.Dijkstra n 1965, ca o variabil


a ntreag
a
utilizat
a pentru num
ararea semnalelor de trezire venite ce asteapt
a s
a fie
folosite; valorile sale sunt 0.
Dijkstra a propus dou
a operatii down si up (ce generalizeaz
a sleep si
wakeup) definite astfel:
- up asupra unui semafor incrementeaz
a valoarea sa cu 1, iar dac
a unul sau
mai multe procese erau blocate n asteptarea ncheierii unei operatii down la
acel semafor, unul dintre ele este ales de sistem (de ex. aleator) si i se permite
s
a ncheie operatia down (el va decrementa semaforul la loc);
astfel, dup
a un up pe un semafor ce avea procese n asteptare la el,
semaforul r
am
ane tot 0 dar vor fi cu 1 mai putine procese n asteptare la el;
incrementarea semaforului si eventuala trezire a unui proces se desf
asoar
a de
asemenea atomic;
nici un proces nu se va bloca efectu
and up (asa cum nainte nici un proces
nu se bloca efectu
and wakeup).

Semafoare

Semaforul a fost introdus de E.W.Dijkstra n 1965, ca o variabil


a ntreag
a
utilizat
a pentru num
ararea semnalelor de trezire venite ce asteapt
a s
a fie
folosite; valorile sale sunt 0.
Dijkstra a propus dou
a operatii down si up (ce generalizeaz
a sleep si
wakeup) definite astfel:
- c
and o operatie atomic
a asupra unui semafor a nceput, se garanteaz
a c
a nici
un proces nu poate accesa semaforul p
an
a c
and operatia nu se ncheie sau nu
se blocheaz
a; atomicitatea este esential
a pentru rezolvarea problemelor de
sincronizare si pentru evitarea conditiilor de curs
a.

Semafoare

Uneori n loc de down si up se folosesc denumirile P, respectiv V


(denumiri introduse de Dijkstra n lucrarea sa original
a, cu semnificatie n limba
olandez
a).

Semafoare

In mod normal, operatiile up si down sunt implementate ca apeluri sistem.


Implementarea lor ntr-un mod atomic este esential
a; n acest scop se
dezactiveaz
a pentru scurt timp ntreruperile, c
ata vreme se testeaz
a semaforul,
se actualizeaz
a si, dac
a este necesar, se trece procesul n adormire - toate
aceste actiuni necesit
a putine instructiuni si astfel nu apar probleme cauzate de
dezactivarea ntreruperilor.
Dac
a sunt folosite mai multe procesoare, fiecare semafor trebuie protejat de o
variabil
a z
avor folosind instructiunea TSL, pentru a permite accesul unui
singur procesor la semafor la un moment dat. Astfel apare o asteptare ocupat
a,
dar ea este foarte scurt
a (operatiile asupra semaforului dureaz
a putin).

Pb. producator-consumator cu semafoare


O rezolvare a problemei producator-consumator cu semafoare este:
/* date partajate */
#define N 100
typedef int semaphore;
semaphore mutex=1;
semaphore empty=N;
semaphore full=0;

/*
/*
/*
/*
/*

/* procesul producator */
void producer(void){
int item;
while(TRUE){
item=produce_item();
down(&empty);
down(&mutex);
insert_item(item);
up(&mutex);
up(&full);
}
}

numarul de locatii din zona tampon */


semafoarele sunt un tip special de int */
controleaza accesul la zona critica */
numara locatiile libere din zona tampon */
numara locatiile ocupate din zona tampon */
/* procesul consumator */
void consumer(void){
int item;
while(TRUE){
down(&full);
down(&mutex);
item=remove_item();
up(&mutex);
up(&empty);
consume_item(item);
}
}

Pb. producator-consumator cu semafoare

Cu down(&empty) / up(&empty) se decrementeaz


a / incrementeaz
a
num
arul locurilor libere, cu down(&full)/up(&full) se decrementeaz
a/
incrementeaz
a num
arul locurilor ocupate, iar cu down(&mutex) /
up(&mute) se marcheaz
a intrarea / iesirea din regiunea critic
a.

Semafoare

Semafoarele cu valoarea initial


a 1 care sunt folosite de dou
a sau mai multe
procese pentru a asigura accesul unuia singur n regiunea lui critic
a la un
moment dat se numesc semafoare binare (binary semaphores).
Dac
a fiecare proces efectueaz
a un down asupra semaforului binar imediat
nainte de a intra n regiunea lui critic
a si un up asupra lui imediat dup
a
iesirea din regiunea critic
a, excluderea mutual
a este garantat
a.

Semafoare

In exemplul anterior am folosit semafoarele n dou


a moduri diferite:
- mutex a fost folosit pentru excluderea mutual
a - el garaneaz
a c
a doar un
singur proces va accesa zona tampon si variabilele asociate acestuia la un
moment dat;
- full si empty au fost folosite pentru sincronizare - ele garanteaz
a c
a
anumite secvente de operatii pot sau nu pot ap
area; n cazul nostru, ele asigur
a
c
a producatorul nu se mai execut
a dac
a zona tampon este plin
a, iar
consumatorul nu se mai execut
a dac
a zona tampon este goal
a.

Instrumente de comunicare intre procese


Conditii de curs
a
Regiuni critice
Dezactivarea ntreruperilor
Variabile z
avor
Alternarea strict
a
Solutia lui Peterson
Instructiunea TSL
Sleep si Wakeup
Semafoare
Mutex
Monitoare
Transfer de mesaje
Bariere
Probleme clasice ale comunicarii interprocese
Problema filozofilor care mananc
a
Problema cititorilor si scriitorilor
Problema frizerului somnoros
Cazul UNIX/Linux
Semnale
IPC
IPC - segmente de memorie partajat
a
IPC - vectori de semafoare
IPC - cozi de mesaje
Fisiere tub

Cuprins

Mutex
Un mutex este o variabil
a cu dou
a st
ari posibile: deschis/nchis si n legatur
a
cu care sunt definite dou
a operatii (proceduri):
- mutex lock: dac
a mutex-ul este deschis, el se nchide, iar apelantul
continu
a (nu se blocheaz
a);
daca mutex-ul este nchis, apelantul este blocat (la mutex-ul respectiv);
a mutex-ul este deschis, nu face nimic;
- mutex unlock: dac
dac
a mutex-ul este nchis, l deschide, apoi alege aleator pe unul din cei care
erau blocati la el (dac
a exist
a) si l trezeste - acesta finalizeaz
a nchiderea, a..
n final mutex-ul r
am
ane tot nchis; dac
a nu era nimeni blocat la mutex, acesta
r
am
ane deschis;
a (nu se
n ambele cazuri, cel care a apelat mutex unlock continu
blocheaz
a).
Mutex-urile sunt versiuni simplificate de semafoare, pe care le folosim atunci
cand nu vrem s
a num
ar
am ci doar s
a obtinem excluderea mutual
a; st
arile
deschis / nchis semnific
a permisiunea / interzicerea de a intra n regiunea
critic
a; nainte de a intra n regiunea critic
a se execut
a mutex lock asupra
unui mutex, iar dup
a iesirea din regiunea critic
a se excut
a mutex unlok
asupra acestuia.

Mutex
Mutex-urile sunt usor si eficient de implementat si de aceea sunt folosite mai
ales la excluderea mutual
a a thread-urilor implementate n spatiul utilizator (a
se vedea cursul 5).
Dac
a este disponibil
a o instructiune TSL, mutex-urile se pot implementa n
spatiul utilizator astfel:
mutex_lock:
TSL REGISTER,MUTEX
CMP REGISTER,#0
JZE ok
CALL thread_yield
JMP mutex_lock
ok: RET

|
|
|
|
|
|

mutex_unlock:
MOVE mutex,#0
RET

| pune 0 in MUTEX
| revenire in apelant

copiaza MUTEX in REGISTRU si seteaza MUTEX la 1


MUTEX a fost 0 ?
daca MUTEX a fost 0 (deci deschis), iesire
MUTEX a fost ocupat, cedeaza procesorul
(la reprimirea procesorului) incearca iar
revenire in apelant (care va intra in reg. critica)

Mutex
Procedurile sunt asem
anatoare cu enter region si leave region din exemplul
anterior de excludere mutual
a bazat pe TSL, dar nu face asteptare ocupat
a
p
an
a c
and planificatorul comut
a pe alt proces care elibereaz
a zavorul ci
cedeaz
a procesorul voluntar (thread yield).
In cazul thread-urilor implementate n spatiul utilizator, c
and thread-urile nu
sunt ntrerupte de nucleu (care nu stie de existenta lor) acestea nu pot face
asteptare ocupat
a, deoarece ar cicla la infinit - asa cum am spus n cursul 5, ele
trebuie s
a renunte voluntar la procesor (iar executivul procesului gazd
a va
planifica alt thread sau tot acelasi).
Cum thread yield este doar un apel c
atre executivul procesului, operatiile
a apeluri sistem, si astfel thread-urile
mutex lock si mutex unlock nu necest
implementate n spatiul utilizator se pot sincroniza n ntregime n spatiul
utilizator.

Mutex
In toate metodele prezentate p
an
a acum (algoritmul lui Peterson, semafoarele,
etc.) este necesar ca anumite locatii din memorie folosite de metoda (ex.
semafoarele) s
a fie partajate ntre procese / thread-uri.
Pentru a putea fi partajate ntre mai multe procese, locatiile respective nu se
pot afla n spatiul de adrese al unui proces (spatiile de adrese ale proceselor
sunt n mod normal disjuncte). Ele trebuie stocate n nucleul SO sau, n
sistemele care permit acest lucru (ex. UNIX/Linux) plasate n zone speciale de
memorie partajat
a care desi fizic sunt unice, logic pot fi privite de mai multe
procese ca fiind n spatiul lor de adrese.
C
and mai multe procese pot avea zone comune de memorie partajat
a, diferenta
fat
a de thread-uri este mai neclar
a, dar totusi exist
a: desi acum au o parte din
spatiul de adrese comun, procesele au n continuare fisiere deschise diferite,
alarme programate diferite, alte caracteristici specifice la nivel de proces diferite
(thread-urile unui acelasi proces le partajaza si pe acestea); n plus, comutarea
ntre ele f
acut
a de nucleu este n continuare mai lent
a dec
at comutarea
thread-urilor implementate n spatiul utilizator si f
acut
a de procesul gazd
a.

Instrumente de comunicare intre procese


Conditii de curs
a
Regiuni critice
Dezactivarea ntreruperilor
Variabile z
avor
Alternarea strict
a
Solutia lui Peterson
Instructiunea TSL
Sleep si Wakeup
Semafoare
Mutex
Monitoare
Transfer de mesaje
Bariere
Probleme clasice ale comunicarii interprocese
Problema filozofilor care mananc
a
Problema cititorilor si scriitorilor
Problema frizerului somnoros
Cazul UNIX/Linux
Semnale
IPC
IPC - segmente de memorie partajat
a
IPC - vectori de semafoare
IPC - cozi de mesaje
Fisiere tub

Cuprins

Monitoare
Monitorul este o primitiv
a de sincronizare de nivel nalt (introdus
a de Hoare
(1974) si Brinch Hansen (1975)), const
and ntr-un pachet special de proceduri
si structuri de date, cu proprietatea c
a:
- procesele pot apela procedurile unui monitor, dar nu pot accesa direct
structurile sale de date;
- ntr-un monitor doar un proces poate fi activ la un moment dat (dac
a ntre
timp alt proces ncearc
a, el este blocat automat p
an
a monitorul e liber).
Monitoarele sunt concepte ale limbajelor de programare, tratate de compilator de exemplu compilatorul v
az
and c
a o procedur
a este definit
a ntr-un monitor,
trateaz
a apelurile ei altfel. Este sarcina compilatorului, nu a programatorului,
s
a implementeze excluderea mutual
a asupra procedurilor din monitor
(compilatorul adaug
a automat codul necesar - de exemplu foloseste un semafor
binar sau un mutex).
Monitoarele ne scutesc de dificult
atile explicit
arii tuturor pasilor elementari ai
unui algoritm de excludere mutual
a - de exemplu, n rezolvarea problemei
producator-consumator cu semafoare prezentat
a mai devreme, dac
a cele dou
a
operatii down din codul producatorului erau inversate, ap
areau conditii de
curs
a (exercitiu!). Ele ne ofera c
ateva proceduri cuprinz
atoare cu care putem
implementa algoritmul n mai putini pasi. Pur si simplu vom implementa
regiunile critice ca proceduri de monitor.

Monitoare

Desi monitoarele ne ofer


a excluderea mutual
a, ele trebuie mbog
atite si cu un
instrument de sincronizarea a proceselor - s
a putem bloca procesul apelant n
asteptarea unor semnaliz
ari din partea altor procese privind anumite
evenimente.
De exemplu, n problema producator-consumator putem transforma testele de
zona tampon plin
a si goala n proceduri de monitor, dar cum se blocheaza
producatorul dac
a zona tampon e plin
a?

Monitoare
Solutia const
a n ad
augarea la monitor a unor variabile de conditie (condition
variables) mpreun
a cu urm
atoarele operatii asupra lor, efectuabile din
procedurile monitorului:
- wait: blocheaz
a procesul apelant, la variabila conditie respectiv
a;
monitorul devine astfel liber si alt proces poate intra n el;
- signal: planificatorul alege unul dintre procesele blocate la variabila conditie
respectiv
a si l trezeste;
variabila conditie nu acumuleaz
a semnaliz
ari ca semafoarele, deci dac
a nu
exist
a procese blocate la ea, semnalizarea cu signal se pierde.
Pentru a evita situatia c
and si procesul care a f
acut signal si procesul trezit
r
am
an simultan active n cadrul monitorului, s-au propus mai multe solutii:
- (Hoare): procesul trezit continu
a, procesul care a facut signal se blocheaz
a
(e mai complicat);
- (B.Hansen): se specific
a necesitatea ca dup
a un signal procesul s
a
p
ar
aseasc
a imediat monitorul - deci folosirea lui signal s
a fie permis
a doar la
sf
arsitul procedurilor monitorului (e mai simplu); aceast
a solutie va fi adoptat
a
n cele ce urmeaz
a;
- procesul care a facut signal continu
a, dar numai dup
a ce acesta va p
ar
asi
monitorul procesul trezit isi va continua executia.

Monitoare
Rezolvarea problemei produc
ator-consumator cu monitoare, n limbajul Pidgin
Pascal (un limbaj imaginar):
monitor ProducerConsumer
condition full, empty;
integer count;
procedure insert(item:integer);
begin
if count=N then wait(full);
insert_item(item);
count=count+1;
if count=1 then signal(empty);
end;
function remove:integer;
begin
if count=0 then wait(empty);
remove=remove_item;
count=count-1;
if count=N-1 then signal(full);
end;
count=0;
end monitor;

procedure producer;
begin
while true do
begin
item=produce_item;
ProducerConsumer.insert(item);
end
end;
procedure consumer;
begin
while true do
begin
item=ProducerConsumer.remove;
consume_item(item)
end
end;

Monitoare

Operatiile wait si signal seam


an
a cu sleep si wakeup, dar pentru c
a se
fac n cadrul monitorului, evit
a cursa care ap
area la primele c
and ntre
momentul detect
arii zonei tampon pline / goale de c
atre producator /
consumator si intrarea lor n sleep procesele puteau fi comutate ajung
andu-se
la inconsistente.

Monitoare

Subliniem c
a monitoarele, fiind concepte ale limbajelor de programare tratate
de compilator, nu pot fi folosite dec
at n limbajele ce ofer
a suport nativ pentru
ele (Java, C# si alte limbjaje ce folosesc .NET, Ada, Ruby, Pascal-FC, etc).
In plus, desi functionalitatea lor este integrat
a n programul utilizator de c
atre
compilator, si sistemul gazd
a trebuie s
a ndeplineasc
a anumite cerinte, de ex.
s
a contin
a mecanismele folosite de compilator pentru implementarea
monitoarelor: semafoare, TSL, etc.
O rezolvare a problemei producator-consumator cu monitoare n limbajul Java
este descrisa n lucrarea lui A.S. Tanenbaum Sisteme de operare moderne.

Instrumente de comunicare intre procese


Conditii de curs
a
Regiuni critice
Dezactivarea ntreruperilor
Variabile z
avor
Alternarea strict
a
Solutia lui Peterson
Instructiunea TSL
Sleep si Wakeup
Semafoare
Mutex
Monitoare
Transfer de mesaje
Bariere
Probleme clasice ale comunicarii interprocese
Problema filozofilor care mananc
a
Problema cititorilor si scriitorilor
Problema frizerului somnoros
Cazul UNIX/Linux
Semnale
IPC
IPC - segmente de memorie partajat
a
IPC - vectori de semafoare
IPC - cozi de mesaje
Fisiere tub

Cuprins

Transfer de mesaje

Transferul de mesaje (message passing) este o metod


a de comunicare ntre
procese care foloseste dou
a primitive send si receive, implementate ca
apeluri sistem (asem
an
ator semafoarelor si spre deosebire de monitoare)
definite astfel:
- send(destination,&message): trimite mesajul la destinatia destination;
- receive(source,&message): preia un mesaj provenit de la sursa source
(care poate fi nespecificat
a si atunci e vorba de orice surs
a); dac
a nici un mesaj
nu este disponibil, apelantul se blocheaz
a p
an
a la primirea unui mesaj (se poate
alege si s
a se revin
a din apel imediat, cu cod de eroare).
(parametrul message este de fapt o adres
a din spatiul de adrese propriu, de
unde se ia / unde se pune mesajul).

Transfer de mesaje
Exist
a mai multe variante de implementare a transferului de mesaje:
- Implementarea n SO a unei structuri de date speciale numit
a casut
a postal
a
(mail box) av
and un num
ar fix de locuri de mesaj si un identificator unic - el
este indicat ca surs
a/destinatie la send si receive; casuta postal
a stocheaz
a
temporar mesajele trimise si nc
a nepreluate; c
and un proces ncearc
a s
a trimit
a
un mesaj ntr-o c
asut
a postal
a plin
a, se blocheaza p
an
a se elibereaz
a un loc.
Aceast
a implementare seaman
a cu mecanismul semafoarelor, numai c
a un
mesaj ad
augat unei casute postale poate contine mai mult
a informatie dec
at
valoarea +/- 1 ad
augat
a unui semafor; n plus, semaforul nu are o capacitate
maxim
a fixat
a.
- O abordare opus
a c
asutelor postale este eliminarea stoc
arii temporare a
mesajelor iar dac
a un proces face send si destinatarul nu este deja n
receive, el se blocheaz
a (p
an
a ce destinatarul face receive si preia mesajul).
Astfel, primul proces care ajunge la un send sau receive se blocheaz
a p
an
a
c
and celalalt ajunge la operatia dual
a, dup
a care si transfer
a mesajul si
repornesc simultan.
Aceasta este o strategie de sincronizare ntre procese (s.n rendezvous). Este
mai usor de implementat dec
at stocarea temporar
a a mesajelor, dar emit
atorul
si receptorul vor fi fortati s
a p
astreze ritmul celui mai lent.

Transfer de mesaje
Transferul de mesaje este folosit si pentru comunicarea ntre procese aflate pe
masini diferite legate ntr-o retea. In acest caz se pot pierde mesaje si trebuie
implementat un protocol de comunicare ntre procese. De exemplu:
- la fiecare mesaj primit, receptorul trimite emit
atorului un mesaj de
confirmare pozitiv
a (acknowledgement); dac
a emit
atorul nu l primeste
ntr-un anumit interval de timp (mesajul de confirmare pozitiv
a se poate pierde
si el), retrimite mesajul;
- pentru a nu confunda mesajele noi cu cele vechi retransmise si pentru a
reconstitui ordinea logic
a a mesajelor, ele se pot asocia cu numere succesive
dintr-o secvent
a.
De asemenea, ntr-o retea se pune problema unui mod de a numi procesele, a..
specificarea sursei / destinatiei s
a fie neambigu
a si a unui mod de autentificare
(authentification) a.. un client s
a fie sigur c
a el comunic
a cu adev
aratul server
si nu cu un impostor.

Transfer de mesaje

Prezent
am n continuare o rezolvare a problemei producator-consumator cu
transfer de mesaje, av
and la baz
a urm
atoarele idei:
- at
at producatorul c
at si consumatorul au c
ate o c
asut
a postal
a de capacitate
N; n ea sunt stocate mesajele primite si nc
a nepreluate;
- mesajele pot fi pline sau goale; produc
atorul preia mesaje goale si le retrimite
pline, consumatorul invers (deci n sistem vor fi mereu exact N mesaje); initial
consumatorul trimite producatorului N mesaje goale;
- dac
a producatorul sau consumatorul apeleaz
a receive iar c
asuta lor postal
a
este goal
a, se blocheaz
a p
an
a vine un mesaj; datorit
a num
arului fix de mesaje
din sistem, nici unul dintre ei nu risc
a s
a se blocheze n send.

Transfer de mesaje
Rezolvarea problemei produc
ator-consumator cu transfer de mesaje:
#define N 100
void producer(void){
int item;
message m;
while(TRUE){
item=produce_item();
receive(consumer,&m);
build_message(&m,item);
send(consumer,&m);
}
}

void consumer(void){
int item,i;
message m;
for(i=0;i<N;++i) send(producer,&m);
while(TRUE){
receive(producer,&m);
item=extract_item(&m);
send(producer,&m);
consume_item(item);
}
}

Instrumente de comunicare intre procese


Conditii de curs
a
Regiuni critice
Dezactivarea ntreruperilor
Variabile z
avor
Alternarea strict
a
Solutia lui Peterson
Instructiunea TSL
Sleep si Wakeup
Semafoare
Mutex
Monitoare
Transfer de mesaje
Bariere
Probleme clasice ale comunicarii interprocese
Problema filozofilor care mananc
a
Problema cititorilor si scriitorilor
Problema frizerului somnoros
Cazul UNIX/Linux
Semnale
IPC
IPC - segmente de memorie partajat
a
IPC - vectori de semafoare
IPC - cozi de mesaje
Fisiere tub

Cuprins

Bariere
Barierele sunt un mecanism de sincronizare folosit atunci c
and mai multe
procese execut
a activit
ati mp
artite n etape si nici un proces nu are voie s
a
treac
a la o etap
a nou
a p
an
a c
and toate celelalte procese nu sunt si ele gata s
a
treac
a la etapa respectiv
a.
In acest scop, ntre etape sunt plasate bariere - practic, apeluri ale unei
primitive barrier (implementat
a n general prin intermediul unei functii de
bibliotec
a), care blocheaz
a procesul apelant p
an
a ce si celelalte procese ajung
n punctul respectiv - din acel moment vor reporni toate.
Exemplu: avem de calculat elementele unui sir recursiv de matrici; fiecare
matrice se calculeaz
a pe baza precedentei: An =f(An1 ); matricile sunt mari,
iar pentru eficient
a se folosesc procese paralele care calculeaz
a p
arti din
matricea curent
a An ; aceste procese trebuie s
a se astepte ntre ele la sf
arsitul
fiec
arei etape n, deoarece nici o parte din An+1 nu poate fi calculat
a p
an
a ce
An nu este n ntregime calculat
a.

Instrumente de comunicare intre procese


Conditii de curs
a
Regiuni critice
Dezactivarea ntreruperilor
Variabile z
avor
Alternarea strict
a
Solutia lui Peterson
Instructiunea TSL
Sleep si Wakeup
Semafoare
Mutex
Monitoare
Transfer de mesaje
Bariere
Probleme clasice ale comunicarii interprocese
Problema filozofilor care mananc
a
Problema cititorilor si scriitorilor
Problema frizerului somnoros
Cazul UNIX/Linux
Semnale
IPC
IPC - segmente de memorie partajat
a
IPC - vectori de semafoare
IPC - cozi de mesaje
Fisiere tub

Cuprins

Instrumente de comunicare intre procese


Conditii de curs
a
Regiuni critice
Dezactivarea ntreruperilor
Variabile z
avor
Alternarea strict
a
Solutia lui Peterson
Instructiunea TSL
Sleep si Wakeup
Semafoare
Mutex
Monitoare
Transfer de mesaje
Bariere
Probleme clasice ale comunicarii interprocese
Problema filozofilor care mananc
a
Problema cititorilor si scriitorilor
Problema frizerului somnoros
Cazul UNIX/Linux
Semnale
IPC
IPC - segmente de memorie partajat
a
IPC - vectori de semafoare
IPC - cozi de mesaje
Fisiere tub

Cuprins

Problema filozofilor care mananca


Problema filozofilor care mananc
a (dining philosophers problem) este o
problem
a de sincronizare propus
a si rezolvat
a de Edsger Dijkstra n 1965,
const
and n urmatoarele:
- la o mas
a circular
a stau 5 filozofi; n fata fiec
aruia este o farfurie cu spaghetti;
ntre oricare doi este o furculit
a; fiecare are nevoie de ambele furculite al
aturate
pentru a m
anca (uneori problema este formulat
a cu filozofi chinezi av
and n
fat
a c
ate o farfurie cu orez si ntre oricare doi c
ate un b
at si av
and nevoie
fiecare de ambele bete al
aturate pentru a putea manca);
- viata unui filozof const
a n perioade de hr
anire si perioade de g
andire care
alterneaz
a; c
and unui filozof i este foame, ncearc
a s
a ia furculitele al
aturate,
c
ate una odat
a (nu conteaz
a ordinea), apoi mananc
a o vreme, apoi elibereaz
a
furculitele, si iar g
andeste.
- cerinta: un program pentru fiecare filozof care face ce trebuie, f
ar
a s
a se
mpotmoleasc
a.

'$
h
R
R
h
R
h
*h h
Y
*
Y
*
Y
6
&%
6
6

Problema filozofilor care mananca

Dificult
ati de implementare:
- putem scrie programul a.. dac
a filozofului i se face foame s
a astepte furculita
st
ang
a, s
a o ia, apoi s
a o astepte pe cea dreapt
a, s
a o ia, apoi s
a man
ance,
apoi s
a elibereze furculitele; atunci, dac
a tuturor li se face foame simultan, vor
lua furculita st
ang
a (e liber
a), apoi vor astepta la nesf
arsit pe cea dreapt
a
(vecinul nu o elibereaz
a p
an
a nu mananc
a); astfel se ajunge la interblocare
(deadlock);

Problema filozofilor care mananca

Dificult
ati de implementare:
- putem modifica programul a.. dup
a preluarea furculitei st
angi filozoful s
a
verifice dac
a cea dreapt
a este disponibil
a, iar dac
a nu s
a o pun
a jos pe cea
st
ang
a, apoi s
a asteapte un interval de timp fixat, apoi s
a ncerce din nou;
atunci, dac
a toti filozofii ncep actiunea simultan, vor lua furculita st
ang
a,
v
az
and c
a nu e liber
a cea dreapt
a o vor pune jos, apoi dup
a acel interval de
timp iar iau furculita st
ang
a, etc; astfel se ajunge la livelock, caz particular de
infometare (starvation) - procesele, n asteptarea resursei, si schimb
a starea la
infinit f
ar
a a progresa;

Problema filozofilor care mananca

Dificult
ati de implementare:
- putem modifica programele a.. fiecare filozof, dup
a esuarea prelu
arii furculitei
drepte si eliberarea celei st
angi, s
a astepte un interval de timp aleator (nu fixat)
p
ana s
a ncerce din nou; atunci sansa s
a nu se schimbe nimic este din ce n ce
mai mic
a odat
a cu trecerea timpului (n aplicatiile unde nu este o problem
a
dac
a ncerc
am din nou mai t
arziu solutia este folosit
a, de ex. n reteaua locala
Ethernet dac
a dou
a calculatoare trimit un pachet n acelasi timp, fiecare
asteapt
a un interval de timp aleator si ncearc
a din nou); vrem ns
a o solutie
sigur
a;

Problema filozofilor care mananca

Dificult
ati de implementare:
- am putea proteja toate actiunile unui filozof legate de m
ancare (preluarea
furculitei st
angi, a celei drepte, m
ancatul, eliberarea furculitelor) cu un mutex;
atunci nu mai apare interblocarea sau infometarea, solutia este corect
a dar nu
si performant
a, deoarece doar un filozof ar putea m
anca la un moment dat,
desi ar trebui s
a poata doi (deoarece exist
a 5 furculite).

Problema filozofilor care mananca


Solutia urm
atoare este corect
a si asigur
a paralelismul maxim pentru un num
ar
arbitrar de filozofi.
Se foloseste un vector state cu st
arile fiec
arui filozof: THINKING, HUNGRY
(doreste s
a m
an
ance si ncearc
a s
a preia furculitele), EATING.
Un filozof poate trece n starea EATING doar dac
a nici unul din vecinii s
ai nu
este n starea EATING.
Se foloseste un semafor mutex pentru a proteja regiunile critice (ele sunt
cuprinse ntre down(&mutex) si up(&mutex)).
De asemenea, se foloseste un vector s de semafoare, c
ate unul pentru fiecare
filozof, a.. orice filozof nfometat s
a se poat
a bloca dac
a vreun vecin al s
au
m
an
anc
a.
Fiecare proces-filozof execut
a procedura philosopher() ca pe un cod
principal, restul fiind proceduri obisnuite (nu procese separate).

Problema filozofilor care mananca


/* date partajate */
#define N 5
#define LEFT (i+N-1)%N
#define RIGHT (i+1)%N
#define THINKING 0
#define HUNGRY
1
#define EATING
2

/*
/*
/*
/*
/*
/*

numarul de filozofi */
indexul vecinului stang al lui i */
indexul vecinului drept al lui i */
filozoful gandeste */
filozoful incearca sa ia furculitele
filozoful mananca */

typedef int semaphore;


int state[N]={0,0,0,0,0};
semaphore mutex=1;
semaphore s[N]={0,0,0,0,0};

/*
/*
/*
/*

semafoarele sunt un tip special de intreg */


vector pentru a retine starea fiecaruia */
excludere mutuala pentru regiunile critice */
cate un semafor pentru fiecare filozof */

/* programul unui filozof */


void philosopher(int i){
/*
while(TRUE){
/*
think();
/*
take_forks();
/*
eat();
/*
put_forks();
/*
}
}

*/

i: numarul filozofului, de la 0 la N-1 */


repeta la infinit */
filozoful gandeste */
preia 2 furculite sau blocheaza-te */
filozoful mananca */
depune ambele furculite */

Problema filozofilor care mananca


void take_forks(int i){
down(&mutex);
state[i]=HUNGRY;
test(i);
up(&mutex);
down(&s[i]);
}

/*
/*
/*
/*
/*
/*

i: numarul filozofului, de la 0 la N-1 */


intra in regiunea critica */
inregistreaza ca filozofului i ii e foame */
incearca sa obtina 2 furculite */
iese din regiunea critica */
blocare daca furculitele n-au fost obtinute*/

void put_forks(int i){


down(&mutex);
state[i]=THINKING;
test(LEFT);
test(RIGHT);
up(&mutex);
}

/*
/*
/*
/*
/*
/*

i: numarul filozofului, de la 0 la N-1 */


intra in regiunea critica */
filozoful a terminat de mancat */
vezi daca vecinul stang poate manca acum */
vezi daca vecinul drept poate manca acum */
iesire din regiunea critica */

void test(int i){


/* i: numarul filozofului, de la 0 la N-1 */
if(state[i]==HUNGRY&&state[LEFT]!=EATING&&state[RIGHT]!=EATING){
state[i]=EATING;
up(&s[i]);
}
}

Problema filozofilor care mananca

Obs: Functia test() este tentativa de a m


anca; apelul ei, ca si schimb
arile
de stare se fac n zon
a de excludere mutual
a, a.. c
and un proces face aceste
operatii, celelalte procese au o stare fixat
a - astfel nu pot ap
area inconsistente.

Instrumente de comunicare intre procese


Conditii de curs
a
Regiuni critice
Dezactivarea ntreruperilor
Variabile z
avor
Alternarea strict
a
Solutia lui Peterson
Instructiunea TSL
Sleep si Wakeup
Semafoare
Mutex
Monitoare
Transfer de mesaje
Bariere
Probleme clasice ale comunicarii interprocese
Problema filozofilor care mananc
a
Problema cititorilor si scriitorilor
Problema frizerului somnoros
Cazul UNIX/Linux
Semnale
IPC
IPC - segmente de memorie partajat
a
IPC - vectori de semafoare
IPC - cozi de mesaje
Fisiere tub

Cuprins

Problema cititorilor si scriitorilor

Problema filozofilor care m


an
anc
a modeleaz
a procese aflate n competitie
pentru obtinerea accesului exclusiv la un num
ar mic de resurse, ca dispozitivele
de I/O.
Problema cititorilor si scriitorilor modeleaz
a accesul la o baza de date.
Problema cititorilor si scriitorilor (Readers-writers problem, Courtois et al.,
1971):
- consider
am o baz
a de date, cu mai multe procese concurente, unele care
citesc, altele care scriu n ea;
- este permis
a citirea simultan
a de c
atre mai multe procese, dar c
at timp un
proces scrie, nici un alt proces nu poate citi sau scrie.
Cum putem programa cititorii si scriitorii ?

typedef int semaphore;


semaphore mutex=1;
semaphore db=1;
int rc=0;
void reader(void){
while(TRUE){
down(&mutex);
rc=rc+1;
if(rc==1)down(&db);
up(&mutex);
read_data_base();
down(&mutex);
rc=rc-1;
if(rc==0)up(&db);
up(&mutex);
use_data_read();
}
}
void writer(void){
while(TRUE){
think_up_data();
down(&db);
write_data_base();
up(&db);
}
}

Pb. cititorilor si scriitorilor


/* controleaza accesul la "rc" */
/* controleaza accesul la baza de date */
/* nr. celor care citesc sau vor s-o faca */
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*

repeta la infinit */
obtine acces exclusiv la rc */
un cititor in plus */
daca acesta este primul cititor ... */
elibereaza accesul exclusiv la "rc" */
acceseaza datele */
obtine acces exclusiv la rc */
un cititor mai putin acum */
daca acesta este ultimul cititor ... */
elibereaza accesul exclusiv la "rc" */
nu este in regiunea critica */

/*
/*
/*
/*
/*

repeta la infinit */
nu este in regiunea critica */
obtine acces exclusiv */
actualizeaza datele */
elibereaza accesul exclusiv */

Problema cititorilor si scriitorilor

Obs: Primul cititor care intr


a n baza de date face down(db); n continuare
alti cititori pot intra imediat (deoarece rc nu mai este 1 deci nu mai fac
down(&db)) - ei doar incrementeaz
a rc; n schimb dac
a vine un scriitor, el
este blocat n down(db) (pe care-l face neconditionat); pe masur
a ce cititorii
pleac
a, decrementeaz
a rc, iar ultimul (deoarece rc este 0) face si
up(&db), permit
and astfel unui scriitor blocat, dac
a exist
a, s
a intre.
Dac
a un scriitor acceseaz
a baza de date, face down(&db) neconditionat si
astfel orice alt cititor sau scriitor care va ncerca accesul va fi blocat p
ana c
and
scriitorul face up(&db).

Problema cititorilor si scriitorilor

Deficienta a solutiei de mai sus: dac


a o citire dureaz
a 5 secunde iar cititorii
(re)vin la fiecare 2 secunde, odat
a ce a intrat un prim cititor vor fi n
permanent
a cititori activi n baza de date, a.. dac
a ntre timp vine un scriitor el
este blocat vesnic.
O solutie: rescrierea programului a.. la sosirea unui cititor, dac
a era un scriitor
n asteptare, cititorul este blocat n spatele scriitorului (n loc s
a fie acceptat
imediat) - astfel, un scriitor va trebui s
a astepte doar iesirea cititorilor activi n
momentul sosirii lui (exercitiu !); metoda ns
a micsoreaz
a concurenta, deci are
o performant
a mai slab
a.
Courtois et al. prezint
a o solutie care d
a prioritate scriitorilor.

Instrumente de comunicare intre procese


Conditii de curs
a
Regiuni critice
Dezactivarea ntreruperilor
Variabile z
avor
Alternarea strict
a
Solutia lui Peterson
Instructiunea TSL
Sleep si Wakeup
Semafoare
Mutex
Monitoare
Transfer de mesaje
Bariere
Probleme clasice ale comunicarii interprocese
Problema filozofilor care mananc
a
Problema cititorilor si scriitorilor
Problema frizerului somnoros
Cazul UNIX/Linux
Semnale
IPC
IPC - segmente de memorie partajat
a
IPC - vectori de semafoare
IPC - cozi de mesaje
Fisiere tub

Cuprins

Problema frizerului somnoros

Problema frizerului somnoros (The Sleeping Barber Problem) (atribuit


a
adesea lui Edsger Dijkstra, 1965):
- ntr-o frizerie, exist
a un frizer, un scaun de tuns si n scaune de asteptare;
- dac
a nu sunt clienti, frizerul doarme pe scaunul de tuns;
- dac
a soseste un client c
and frizerul doarme, l trezeste iar frizerul ncepe s
a-l
tund
a; dac
a soseste un client c
and frizerul tunde, fie st
a pe un scaun de
asteptare, dac
a exist
a scaune goale, fie p
ar
aseste frizeria, dac
a nu exist
a scaune
goale.
Cerinta const
a n a programa frizerul si clientii f
ar
a a intra n conditii de curs
a.

Problema frizerului somnoros


O implementare incorect
a poate conduce la interblocare sau infometare, de ex:
- frizerul poate astepta la nesf
arsit un client iar clientul asteapt
a la nesf
arsit
frizerul (interblocare);
- clientii pot s
a nu decid
a abordarea frizerului ntr-un mod ordonat, a.. unii
clienti pot s
a nu aibe sansa de a se tunde desi au asteptat (nfometare).
In formularea dat
a, problema implic
a un singur frizer (the single sleeping
barber problem); ea se poate generaliza consider
and mai multi frizeri ce
trebuie coordonati printre clientii n asteptare (multiple sleeping barbers
problem).
Problema este similar
a multor situatii de formare de cozi, de ex. serviciul de
relatii cu clientii format din mai multe persoane si care dispune de un sistem de
apel n asteptare computerizat pentru retinerea unui num
ar limitat de apeluri
primite.

Problema frizerului somnoros

Solutia urm
atoare foloseste:
- un semafor customers, care num
ar
a clientii n asteptare (se exclude
clientul de pe scaunul de tuns, care nu asteapt
a);
- un semafor barbers, care numar
a frizerii ce nu fac nimic, n asteptarea
clientilor (0 sau 1);
- un semafor mutex, folosit pentru excluderea mutual
a;
- o variabi
a waiting, care num
ar
a si ea clientii n asteptare; ea este de fapt o
copie a lui customers si este necesar
a deoarece nu putem citi valoarea
curent
a a unui semafor.
Frizerul execut
a procedura barber(), iar fiecare client, c
and vine, execut
a
procedura customer():

Problema frizerului somnoros

/* date partajate */
#define CHAIRS 5
typedef int semaphore;
semaphore customers=0;
semaphore barbers=0;
semaphore mutex=1;
int waiting=0;

/* nr. de scaune pentru clientii care asteapta */


/*
/*
/*
/*

nr. de
nr. de
pentru
nr. de

clienti care asteapta */


frizeri care asteapta clientii */
excluderea mutuala */
clienti care asteapta */

Problema frizerului somnoros


/* procedura executata de frizer */
void barber(void){
while(TRUE){
down(&customers);
/* dormi, daca nr. de clienti este 0 */
down(&mutex);
/* obtine acces la "waiting" */
waiting=waiting-1;
/* decrementeaza nr. de clienti care asteapta */
up(&barbers);
/* un frizer gata sa tunda */
up(&mutex);
/* elibereaza "waiting" */
cut_hair();
/* tunde (in afara regiunii critice) */
}
}
/* procedura executata de fiecare client */
void customer(void){
down(&mutex);
/* intra in regiunea critica */
if(waiting<CHAIRS){
/* daca nu sunt scaune libere, pleaca */
waiting=waiting+1;
/* incrementeaza nr. de clienti care asteapta */
up(&customers);
/* trezeste frizer daca e nevoie */
up(&mutex);
/* elibereaza accesul la "waiting" */
down(&barbers);
/* dormi, daca nr. de frizeri liberi este 0 */
get_haircut();
/* ia loc si fii tuns */
} else {
up(&mutex);
/* frizeria este plina, nu astepta */
}
}

Problema frizerului somnoros

Obs1: n codul clientului nu exist


a bucl
a, deoarece fiecare se tunde doar o dat
a;
n codul frizerului exist
a bucl
a, pentru a prelua urm
atorul client.
Obs2: solutia de mai sus este preluat
a din lucrarea lui A.S. Tanenbaum
Sisteme de operare moderne; ea are anumite deficiente, de ex. este posibil ca
un nou client s
a fie servit n timp ce clientul anterior nc
a execut
a
get_haircut() (este n afara regiunii critice); ar trebui adaugat
a si o
sincronizare client-frizer la sf
arsitul procedurilor cut_hair() si
get_haircut() (exercitiu !).

Instrumente de comunicare intre procese


Conditii de curs
a
Regiuni critice
Dezactivarea ntreruperilor
Variabile z
avor
Alternarea strict
a
Solutia lui Peterson
Instructiunea TSL
Sleep si Wakeup
Semafoare
Mutex
Monitoare
Transfer de mesaje
Bariere
Probleme clasice ale comunicarii interprocese
Problema filozofilor care mananc
a
Problema cititorilor si scriitorilor
Problema frizerului somnoros
Cazul UNIX/Linux
Semnale
IPC
IPC - segmente de memorie partajat
a
IPC - vectori de semafoare
IPC - cozi de mesaje
Fisiere tub

Cuprins

Cazul UNIX/Linux

Comunicarea ntre procese n UNIX/Linux se poate face prin:


- semnale;
- IPC: segmente de memorie partajate, vectori de semafoare, cozi de mesaje;
- fisiere tub (pipe).

Instrumente de comunicare intre procese


Conditii de curs
a
Regiuni critice
Dezactivarea ntreruperilor
Variabile z
avor
Alternarea strict
a
Solutia lui Peterson
Instructiunea TSL
Sleep si Wakeup
Semafoare
Mutex
Monitoare
Transfer de mesaje
Bariere
Probleme clasice ale comunicarii interprocese
Problema filozofilor care mananc
a
Problema cititorilor si scriitorilor
Problema frizerului somnoros
Cazul UNIX/Linux
Semnale
IPC
IPC - segmente de memorie partajat
a
IPC - vectori de semafoare
IPC - cozi de mesaje
Fisiere tub

Cuprins

Semnale

Semnalele (signal) sunt entit


ati ce au ca singur
a informatie asociat
a un cod
numeric ntreg (tipul semnalului).
Codurile semnalelor sunt de la 1 la NSIG-1 si se pot desemna prin mnemonice
scrise cu majuscule si care ncep cu SIG (SIGINT, SIGQUIT, etc.); at
at NSIG
c
at si menmonicele sunt constante simbolice definite n signal.h.
Semnalele pot fi primite de procese, fiind trimise de alte procese sau generate
de anumite evenimente; c
and la un proces ajunge un semnal, acesta nu
cunoaste expeditorul sau evenimentul generator ci doar tipul semnalului (codul
numeric, singura informatie asociat
a semnalului).

Semnale
Un semnal poate fi trimis de un proces altor procese cu apelurile:
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
= dac
a pid este > 0, se trimite semnalul sig procesului cu PID-ul pid;
dac
a pid este = 0, se trimite sig tuturor proceselor din acelasi grup
cu procesul expeditor;
dac
a pid este = -1, se trimite sig tuturor proceselor pentru care
procesul apelant are dreptul s
a trimit
a semnale, mai putin procesului init care
are PID-ul 1 (cu unele exceptii);
dac
a pid este < -1, se trimite sig tuturor proceselor din grupul al
c
arui lider are PID-ul -PID;
dac
a sig este 0, nu se transmite nici un semnal, dar se face verificarea
erorilor (astfel, putem verifica dac
a un proces exist
a trimit
andu-i 0 si verific
and
codul de eroare furnizat de kill() n errno);
procesul expeditor poate trimite un semnal doar dac
a este proces
privilegiat sau dac
a UID-ul sau EUID-ul lui coincide cu UID-ul sau saved
set-user-ID-ul procesului destinatar; n cazul semnalului SIGCONT este
suficient ca expeditorul si destinatarul s
a fie n aceeasi sesiune;
la succes returneaz
a 0, la esec returneaz
a -1;
errno posibile: EINVAL, EPERM, ESRCH.

Semnale
#include <signal.h>
int raise(int sig);
= se trimite semnalul sig nsusi procesului curent;
este echivalent cu kill(getpid(), sig);
la succes returneaz
a 0, la esec returneaz
a 6= 0.

Semnale
Pentru a gestiona semnalele primite, un proces foloseste o structur
a av
and cel
putin urm
atoarele c
ampuri:
................ 3
2
1
---> toate semnalele valide
------------------------------|
|
|
|
|
|
| ---> indicatorul semnalelor in
------------------------------asteptare (pending)
|
|
|
|
|
|
| ---> indicatorul semnalelor
------------------------------blocate
|
|
|
|
|
---> legaturi la handlerele de
V
V
V
V
V
tratare a semnalelor
(pentru un semnal, indicatorul de pending si cel de blocare sunt
biti, iar handlerul este o functie)

C
and la un proces ajunge un semnal n:
- bitul de pending al lui n devine 1; dac
a pe perioada c
at acest bit este 1 mai
vine un semnal n, acesta se va pierde (din fiecare semnal poate fi la un moment
dat n pending doar un singur exemplar);

Semnale
Pentru a gestiona semnalele primite, un proces foloseste o structur
a av
and cel
putin urm
atoarele c
ampuri:
................ 3
2
1
---> toate semnalele valide
------------------------------|
|
|
|
|
|
| ---> indicatorul semnalelor in
------------------------------asteptare (pending)
|
|
|
|
|
|
| ---> indicatorul semnalelor
------------------------------blocate
|
|
|
|
|
---> legaturi la handlerele de
V
V
V
V
V
tratare a semnalelor
(pentru un semnal, indicatorul de pending si cel de blocare sunt
biti, iar handlerul este o functie)

C
and la un proces ajunge un semnal n:
- dac
a bitul de blocaj al lui n este 1, semnalul n nu va fi tratat (el r
am
ane n
pending); dac
a acest bit e 0, semnalul n va fi tratat atunci c
and va fi posibil (a
se vedea mai jos);

Semnale
Pentru a gestiona semnalele primite, un proces foloseste o structur
a av
and cel
putin urm
atoarele c
ampuri:
................ 3
2
1
---> toate semnalele valide
------------------------------|
|
|
|
|
|
| ---> indicatorul semnalelor in
------------------------------asteptare (pending)
|
|
|
|
|
|
| ---> indicatorul semnalelor
------------------------------blocate
|
|
|
|
|
---> legaturi la handlerele de
V
V
V
V
V
tratare a semnalelor
(pentru un semnal, indicatorul de pending si cel de blocare sunt
biti, iar handlerul este o functie)

C
and la un proces ajunge un semnal n:
- n general un proces trateaz
a semnalele aflate n pending doar atunci c
and
este n user mode, deci atunci c
and execut
a instructiuni ale utilizatorului; astfel,
c
and procesul execut
a un apel sistem, el nu trateaz
a semnalele (toate sunt
blocate temporar);

Semnale
Pentru a gestiona semnalele primite, un proces foloseste o structur
a av
and cel
putin urm
atoarele c
ampuri:
................ 3
2
1
---> toate semnalele valide
------------------------------|
|
|
|
|
|
| ---> indicatorul semnalelor in
------------------------------asteptare (pending)
|
|
|
|
|
|
| ---> indicatorul semnalelor
------------------------------blocate
|
|
|
|
|
---> legaturi la handlerele de
V
V
V
V
V
tratare a semnalelor
(pentru un semnal, indicatorul de pending si cel de blocare sunt
biti, iar handlerul este o functie)

C
and la un proces ajunge un semnal n:
- tratarea unui semnal n aflat n pending const
a n apelarea handlerului asociat;
chiar de la nceputul execut
arii handlerului bitul de pending al lui n se
repozitioneaz
a pe 0 (deci procesul poate primi imediat un alt n, care va intra n
pending); pe parcursul execut
arii handlerului n este n principiu blocat
suplimentar, astfel c
a dac
a ntre timp vine un nou n, el va fi blocat n pending
p
an
a la sf
arsitul execut
arii handlerului, c
and va putea fi tratat lans
and din nou
handlerul; un handler poate fi ns
a instalat si a.. acest blocaj suplimentar s
a nu
mai apar
a (a se vedea mai jos).

Semnale

Pentru fiecare semnal exist


a handlere implicite (ale sistemului), dar pentru
majoritatea putem instala handlere proprii (exceptii: SIGKILL, SIGSTOP,
uneori SIGCONT);
Pentru un semnal putem reinstala handlerul implicit, desemnat prin SIG DFL
(care ns
a pentru fiecare semnal poate nseamna altceva), sau un handler de
ingorare al sistemului, desemnat prin SIG IGN (pentru cele dou
a constante
simbolice putem include signal.h);

Semnale
SUBLINIEM:
- dac
a un proces primeste un semnal neblocat n si este ntr-o zon
a unde
execut
a instructiuni ale utilizatorului (indiferent unde), el va fi ntrerupt si se va
executa handlerul h asociat lui n; apoi, dac
a h nu a cerut terminarea
programului, acesta se reia de unde s-a ntrerupt;

Semnale
SUBLINIEM:
- pe parcursul execut
arii lui h mai poate veni un semnal neblocat p si atunci
sunt posibile cazurile:
dac
a h este un handler al sistemului (SIG DFL sau SIG IGN): atunci p
r
am
ane n pending p
an
a la sf
arsitul lui h, deoarece pe perioada lui h procesul
este n kernel mode si toate semnalele sunt blocate temporar; dup
a terminarea
lui h blocajul suplimentar dispare si p va fi tratat - se va lansa handlerul f al lui
p;

Semnale
SUBLINIEM:
- pe parcursul execut
arii lui h mai poate veni un semnal neblocat p si atunci
sunt posibile cazurile:
dac
a h este un handler al utilizatorului (chiar si functia cu corp vid), atunci
sunt posibile subcazurile:
dac
a p = n:
atunci noul n nu se pierde (pentru c
a bitul de pending al lui n s-a repozitionat
pe 0 chiar de la nceputul lui h), dar r
am
ane n pending pe toata perioada lui h
(pentru c
a n este blocat temporar); dup
a terminarea lui h blocajul suplimentar
asupra lui n dispare si noul n va fi tratat (se va lansa iar h); am presupus ns
a
c
a h nu a fost instalat cu eliminarea blocajului suplimentar, cum am spus mai
sus ca se poate;

Semnale
SUBLINIEM:
- pe parcursul execut
arii lui h mai poate veni un semnal neblocat p si atunci
sunt posibile cazurile:
dac
a h este un handler al utilizatorului (chiar si functia cu corp vid), atunci
sunt posibile subcazurile:
dac
a p 6= n:
atunci h se ntrerupe temporar (pentru c
a sunt instructiuni ale utilizatorului), se
execut
a f, apoi se continu
a h de unde a r
amas;
(n cele de mai sus am presupus c
a h si f nu produc terminarea programului).

Semnale
SUBLINIEM:
Astfel, presupun
and c
a n si p nu sunt blocate iar h si f sunt handlere ale
utilizatorului care nu termin
a programul si blocheaz
a semnalul pentru care au
fost lansate pe perioada executiei lor, dac
a procesul primeste foarte repede
secventa n, n, p, va executa: un nceput de h, apoi un f, apoi sf
arsitul primului
h, apoi un alt h.

Semnale
Pentru a manipula semnale din program, avem urm
atoarele instrumente
(furnizate de signal.h):
#include <signal.h>
sigset_t
= tipul multime de semnale;
int sigemptyset(sigset_t *set);
= seteaz
a *set := {};
returneaz
a: 0=succes, -1=esec;
int sigfillset(sigset_t *set);
= seteaz
a *set := {1, ..., NSIG-1};
returneaz
a: 0=succes, -1=esec;
int sigaddset(sigset_t *set, int signum);
= adaug
a *set := *set {signum};
returneaz
a: 0=succes, -1=esec;
int sigdelset(sigset_t *set, int signum);
= elimin
a *set := *set \ {signum};
returneaz
a: 0=succes, -1=esec;
int sigismember(const sigset_t *set, int signum);
= testeaza dac
a signum apartine *set;
returneaza: 0=nu apartine, 1=apartine, -1=esec;
errno posibile: EINVAL.

Semnale
Putem bloca/debloca semnale (modific
and linia a 2-a, a indicatorilor
semnalelor blocate, din structura de gestiune a semnalelor primite de procesul
curent) cu apelul:
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
= seteaz
a/consult
a masca procesului de blocare a semnalelor;
*set=noua masc
a (dac
a set=NULL, nu se schimb
a masca);
*oldset=aici se recupereaz
a vechea masc
a (dac
a oldset=NULL, nu se
mai recupereaz
a);
how=poate fi indicat prin urm
atoarele constante simbolice (furnizate de
signal.h):
SIG SETMASK noua masc
a devine *set
SIG BLOCK noua masc
a devine *oldset *set
SIG UNBLOCK noua masc
a devine *oldset \ *set
returneaz
a: 0=succes, -1=esec;
nu pot fi blocate SIGKILL si SIGSTOP (ncercarea esueaz
a, apelul
returneaz
a -1 si seteaz
a errno=EINVAL);

Semnale

Putem afla semnalele n pending la procesul curent (consult


and prima linie, a
indicatorilor semnalelor n pending, din structura de gestiune a semnalelor
primite) cu apelul:
#include <signal.h>
int sigpending(sigset_t *set);
= pune n *set multimea semnalelor aflate n pending n acel moment;
returneaz
a: 0=succes, -1=esec;

Semnale

Pentru majoritatea semnalelor se pot instala handlere utilizator.


Pentru a putea fi folosit
a ca handler, o functie trebuie s
a aibe un singur
parametru de tip int si s
a returneze void, de exemplu:
void h(int n){...}
C
and procesul va primi semnalul asociat, el va fi transmis ca parametru acestei
functii. Astfel, dac
a aceeasi functie este instalat
a ca handler pentru mai multe
semnale, ea va sti la fiecare apel semnalul pentru care a fost apelat
a (si poate fi
scris
a s
a fac
a ceva diferit n fiecare caz).
Putem instala un handler utilizator pentru un semnal cu apelurile signal() si
sigaction():

Semnale
Apelul signal() este mai simplu:
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
= se instaleaz
a functia specificat
a de handler pentru semnalul signum;
handler poate specifica o functie utilizator (cu signatura mentionat
a),
poate fi SIG IGN (handler-ul de ignorare) sau SIG DFL (handler-ul implicit al
semnalului signum); signum nu poate fi SIGKILL sau SIGSTOP;
signal() returneaz
a adresa vechiului handler folosit pentru semnalul
signum, sau SIG ERR n caz de eroare;
n unele implementari UNIX/Linux, handler-ele functii utilizator sunt
instalate a.i. blocheaz
a temporar pe perioada apelului semnalul pentru care au
fost instalate sau reinstaleaz
a pentru semnalul respectiv, chiar de la nceputul
apelului, handlerul SIG DFL; n acest caz, dac
a dorim s
a instalam handler-ul
permanent, punem la nceputul s
au un apel de reinstalare a sa pentru acelasi
semnal: void h(int n){signal(n,h); ...}
(dac
a n plus pe perioada apelului n este blocat, nu exist
a riscul ca un nou
n s
a ntrerup
a h ntre { si signal(n,h); iar pentru el s
a se execute
SIG DFL); dac
a nu vrem s
a ne baz
am pe aceste comportamente implicite,
folosim sigaction().

Semnale

Apelul sigaction() permite specificarea unor set


ari mai fine, cu ajutorul unei
structuri sigaction:
#include <signal.h>
int sigaction(int signum,
const struct sigaction *act,
struct sigaction *oldact);
= dac
a act este 6=NULL, se instaleaz
a actiunea descris
a de structura
*act pentru semnalul signum;
dac
a oldact este 6=NULL, este recuperat
a vechea actiune n
*oldact;
signum nu poate fi SIGKILL sau SIGSTOP;

Semnale
#include <signal.h>
struct sigaction{
void (*sa_handler)(int);
...
sigset_t sa_mask;
int sa_flags;
...
};
actiunea descris
a de o structur
a sigaction contine urm
atoarele informatii:
sa handler = pointer la functia handler;
sa mask = o masc
a de semnale ad
augate la masca de semnale blocate a
procesului, pe perioada execut
arii handlerului;
sa flags = 0 sau disjunctie pe biti de mai multe optiuni;

Semnale
#include <signal.h>
struct sigaction{
void (*sa_handler)(int);
...
sigset_t sa_mask;
int sa_flags;
...
};
c
ateva dintre optiunile utilizabile la sa flags:
SA ONESHOT, SA RESETHAND = dup
a o singur
a executie a noului
handler se va reinstala automat handlerul implicit; reinstalarea va avea loc chiar
de la nceputul execut
arii noului handler (a se vedea functia signal);
aruia i s-a asociat handlerul nu
SA NOMASK, SA NODEFER = semnalul c
va mai fi blocat pe perioada execut
arii sale (deci handlerul se va putea
ntrerupe de c
atre el nsusi);
SA RESTART = anumite apeluri sistem vor fi restartabile dup
a primirea
semnalului; de exemplu, dac
a procesul doarme ntr-un wait(), la primirea
semnalului va executa handler-ul apoi va continua s
a doarm
a n acel wait()
(altfel, dup
a executarea handler-ului nu se reia wait() ci se trece mai
departe);

Semnale

Dac
a am asociat unui semnal un handler utilizator ce modific
a niste variabile
globale care influienteaz
a cursul ulterior al executiei programului, este foarte
important s
a stim de c
ate ori si n ce momente ale executiei va veni semnalul
respectiv (de asta depinde treseul executiei).
Totusi, la momentul scrierii programului, nu putem anticipa ce semnale va primi
el pe parcursul executiei si n ce momente - asta se decide abia la run-time.
De aceea, n general asemenea programe sunt greu de scris, deoarece trebuie s
a
avem n vedere toate cazurile posibile.

Semnale

Variabilele globale modificate de handlerele utilizator asociate semnalelor e bine


s
a fie declarate cu calificatorul volatile - aceasta este o indicatie c
atre
compilator c
a variabila ar putea fi modificat
a oric
and pe alt
a cale dec
at cursul
normal al executiei descris n program (eventual ar putea fi modificat
a din afara
acestui program).
Compilatorul interpreteaz
a aceast
a indicatie prin aceea c
a nu optimizeaz
a
accesarea variabilei respective n memorie; mai exact orice accesare /
modificare a variabilei va fi f
acut
a cu citirea si scrierea valorii ei curente n
memorie (valoarea curent
a nu va fi mentinut
a mai mult timp n registri).

Semnale
De exemplu secventa:
int a;
int x=1,y;
...
a=x;
a=a+10;
y=a;
va fi tradus
a la compilarea cu gcc -O3 -S n :
movl
addl
movl
movl

x, %eax
$10, %eax
%eax, a
%eax, y

deci, dac
a vine un semnal ntre a=x si a=a+10 iar handler-ul d
a lui a
valoarea 100, n y va ajunge tot 11 (n loc de 110), deoarece valoarea curent
a
a lui a este mentinut
a n registrul %eax si nu este recitita din memorie;

Semnale
In schimb secventa:
volatile int a;
int x=1,y;
...
a=x;
a=a+10;
y=a;
va fi tradus
a la compilarea cu gcc -O3 -S n :
movl
movl
movl
addl
movl
movl
movl

x, %eax
%eax, a
a, %eax
$10, %eax
%eax, a
a, %eax
%eax, y

deci, un semnal care vine ntre aceste instructiuni surs


a are sanse mai mari s
a
prind
a valoarea curent
a a lui a n memorie (nu n registru).

Semnale

Alte apeluri utile:


#include <unistd.h>
unsigned int sleep(unsigned int seconds);
= procesul curent doarme p
an
a trec seconds secunde sau primeste un
semnal neignorat;
returneaz
a 0 dac
a a trecut timpul cerut sau numarul de secunde
neconsumate altfel;
#include <unistd.h>
void usleep(unsigned long usec);
= procesul curent doarme p
an
a trec (cel putin) usec microsecunde
(durata poate creste n functie de nc
arcarea sistemului sau alti factori) sau
primeste un semnal neignorat;

Semnale
Putem adormi procesul n asteptarea unui semnal cu apelurile:
#include <unistd.h>
int pause(void);
= procesul adoarme p
an
a primeste un semnal neignorat - deci neblocat si
cu alt handler decat SIG IGN (exceptie: dac
a semnalul este SIGCONT cu
handlerul SIG DFL, procesul r
am
ane adormit);
returneaz
a dup
a ce handler-ul semnalului a facut return - anume
pause() returneaz
a -1 si seteaz
a errno = EINTR;
#include <signal.h>
int sigsuspend(const sigset_t *mask);
= instaleaz
a temporar (p
an
a la iesirea din apel) *mask ca masc
a de
blocare a semnalelor si adoarme procesul p
an
a la primirea unui semnal neblocat
(cele dou
de *mask; actiunea este ATOMICA
a efecte sunt realizate printr-o
singur
a operatie, deci nu exist
a riscul s
a se trateze un semnal neblocat de
*mask ntre momentul instal
arii acestei m
asti si adormirea procesului);
la primirea unui semnal neblocat de *mask comentariile si return-ul
sunt ca la pause;

Semnale
Putem programa primirea de c
atre procesul curent a unui semnal SIGALRM
dup
a un anumit interval de timp cu apelul:
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
= programeaz
a trimiterea unui semnal SIGALRM c
atre procesul apelant
dup
a trecerea a seconds secunde (apelul nu blocheaza procesul n asteptarea
semnalului, ci procesul si continu
a executia iar dup
a trecerea intervalului de
timp primeste semnalul);
dac
a anterior a mai fost efectuat un apel alarm(), el este anulat
(num
ar
atoarea descrescatoare a secundelor se reia de la seconds);
dac
a apelam alarm() cu parametrul seconds=0 doar se anuleaz
a
orice apel anterior;
returneaz
a num
arul de secunde r
amase de num
arat n urma apelului
anterior, sau 0 dac
a nu a existat un apel anterior.
Obs: apelul sleep() poate fi implementat folosind SIGALRM; de aceea
mixarea apelurilor alarm() si sleep() este contraindicat
a.

Semnale
C
ateva semnale importante si semnificatiile lor:
SIGHUP = 1
C
and procesul lider al unei sesiuni se termin
a, acest semnal este trimis automat
tuturor proceselor din sesiunea respectiv
a.
Handler implicit: terminarea.
Astfel, c
and ne delogam (deci login shell-ul, care era lider de sesiune se
termin
a), toate procesele lansate prin comenzi shell se termin
a automat.
Putem ns
a lansa un program prog cu comanda nohup prog si atunci el va
fi vaccinat la semnalul SIGHUP iar cand l va primi nu se va termina - astfel
putem lansa programe care s
a ruleze si dupa ce ne-am delogat (de exemplu de
pe-o zi pe alta). Iesirea standard si iesirea standard de eroare ale unui program
lansat cu nohup sunt redirectate automat spre un fisier nohup.out aflat n
directorul curent (sau n directorul home, dac
a cel curent nu ofer
a drept de
scriere), cu exceptia cazului c
and s-a indicat explicit o alt
a redirectare (de
exemplu d
am: nohup prog > f), caz n care se respect
a aceast
a redirectare.

Semnale
C
ateva semnale importante si semnificatiile lor:
SIGINT = 2, SIGQUIT = 3
C
and tast
am de la un terminal Ctrl-c, respectiv Ctrl-\ (de fapt caracterele
logice intr si quit), primul, respectiv al doilea semnal este trimis c
atre toate
procesele aflate n foreground la terminalul respectiv.
Handler implicit: terminarea (n cazul lui SIGQUIT se face si un fisier core cu
imaginea memoriei).

Semnale
C
ateva semnale importante si semnificatiile lor:
SIGKILL = 9
Este neblocabil si nu i putem asocia alt handler dec
at cel implicit.
Handler implicit: terminarea.
Acest semnal este folosit de regul
a pentru a termina explicit un proces dat.

Semnale
C
ateva semnale importante si semnificatiile lor:
SIGUSR1 = 10, SIGUSR2 =12
Handler implicit: terminarea.
De regul
a sunt folosite de programatori pentru scopuri particulare.

Semnale
C
ateva semnale importante si semnificatiile lor:
SIGSEGV = 11
C
and un proces ncearc
a s
a acceseze cu instructiuni din programul utilizator
(fiind deci n user mode) o zon
a din afara spatiului de adrese al procesului
primeste semnalul SIGSEGV.
Handler implicit: terminarea.
Se poate instala un handler utilizator sau chiar SIG IGN si atunci procesul
continu
a, doar instructiunea care a ncercat accesul ilegal nu este efectuat
a.

Semnale
C
ateva semnale importante si semnificatiile lor:
SIGALRM = 14
Handler implicit: terminarea.
De regul
a este folosit de c
atre un program pentru a-si planifica un
comportament anume dup
a un anumit interval de timp, indiferent de momentul
unde se afl
a cu executia (cu ajutorul unui handler utilizator asociat semnalului).

Semnale
C
ateva semnale importante si semnificatiile lor:
SIGTERM = 15
Handler implicit: terminare

Semnale
C
ateva semnale importante si semnificatiile lor:
SIGCHLD = 17
Cand un proces se termin
a, p
arintele s
au primeste un semnal SIGCHLD.
Handler implicit: ignorare

Semnale
C
ateva semnale importante si semnificatiile lor:
SIGSTOP = 19, SIGCONT = 18
Semnale folosite la suspendarea / reluarea explicit
a a unor procese (mecanisme
de job control).
Handler implicit: suspendare, respectiv reluare.
SIGSTOP nu poate fi blocat si nu i se poate asocia alt handler dec
at cel
implicit; lui SIGCONT pe anumite sisteme nu-i putem asocia alt handler dec
at
cel implicit.

Semnale

Din linia de comand


a shell putem transmite semnale c
atre procese cu comanda:
kill -semnal proces
unde semnal este codul numeric sau sf
arsitul mnemonicului unui semnal
(partea f
ar
a SIG), iar proces este PID-ul procesului destinatar; n general,
comanda reuseste dac
a proprietarul s
au real coincide cu cel al procesului
destinatar sau este root; mai sunt si alte detalii.
Cu comanda
kill -l
se afisaz
a o list
a cu toate semnalele implementate n sistemul curent.

Semnale

Exemplu:
Un proces genereaz
a un copil si i trimite 2000 de semnale SIGUSR1; p
arintele
num
ar
a c
ate a trimis, copilul num
ar
a c
ate a primit; fiecare, c
and a ajuns la
2000 afisaz
a un mesaj si se termin
a; pentru a nu se pierde semnale, p
arintele
nu trimite un nou semnal p
an
a nu primeste confirmarea primirii celui anterior,
sub forma tot a unui semnal SIGUSR1.


Variant
a INCORECTA:
#include<sys/types.h>
#include<unistd.h>
#include<signal.h>
#include <sys/wait.h>
#include<stdio.h>
#include<stdlib.h>
volatile int nr; char *nume;
void f(int n){signal(n,f);}
void g(int n){printf("%s: %d\n",nume,nr); exit(0);}
int main(){
pid_t p;
signal(SIGUSR1,f); signal(SIGINT,g); nr=0;
if(p=fork()){
nume="parinte";
while(nr<2000){kill(p,SIGUSR1); ++nr; pause();}
wait(NULL);
printf("final parinte\n");
}else{
nume="copil"; p=getppid();
while(nr<2000){pause(); ++nr; kill(p,SIGUSR1);}
printf("final copil\n");
}
return 0;
}

Semnale

Semnale
Comentarii:
- la fiecare iteratie (n total 2000), p
arintele trimite un semnal, l num
ar
a, apoi
adoarme n pause() asteptand confirmarea;
c
and aceasta vine, iese din pause() si executa handler-ul f() care nu face
nimic deosebit (doar se reinstaleaz
a - am presupus c
a n sistemul curent
signal() instaleaz
a handlere doar pentru o singur
a folosire) - rolul lui f()
este doar de a ntrerupe pause();
asem
an
ator copilul, la fiecare iteratie (n total 2000), asteapt
a n pause()
un semnal, c
and acesta vine iese din pause() (si execut
a f()), apoi l
num
ar
a si trimite p
arintelui confirmarea;
- nainte de afisarea mesajului final, p
arintele asteapt
a terminarea copilului
(wait(NULL)); altfel, p
arintele s-ar putea termina primul si atunci fiul s-ar
muta n background si n-ar mai putea afisa mesajul s
au final (dec
at dac
a
terminalul este setat stty -tostop);
- n caz de interblocare, ambele procese pot fi terminate cu un Ctrl-c de la
terminal - ele, fiind n forground, vor primi semnalul SIGINT, iar handler-ul
asociat va afisa semnalele num
arate p
ana la acel moment si va face exit();

Semnale
Comentarii:
- la rulare se constat
a c
a uneori se transmit / receptioneaz
a toate cele 2000
semnale iar procesele se termin
a normal, alteori ele se interblocheaz
a f
ar
a a le
trimite / receptiona pe toate;
- un scenariu care duce la interblocare: p
arintele trimite un semnal, si nainte
de a ajunge la pause() primeste confirmarea; atunci execut
a f() (care nu
face nimic deosebit), iar c
and ajunge la pause() va astepta confirmarea care
venise deja; astfel, p
arintele nu va trece la iteratia urm
atoare s
a mai trimit
a un
semnal, iar copilul nu mai trimite o confirmare p
an
a nu primeste un semnal;
astfel ambele procese ajung s
a doarm
a ntr-un pause();
- ntr-o alt
a varint
a a programului, copilul ar putea trimite confirmarea chiar
din handler-ul care-l trezeste din pause(); atunci ar ap
area n plus si riscul
num
ar
arii gresite a semnalelor, n urma efectu
arii mai multor schimburi de
semnale ntre un pause() si un ++nr din aceeasi iteratie a copilului;
- n plus, dac
a handler-ele s-ar fi instalat n cele dou
a procese dup
a fork(),
exista riscul ca p
arintele s
a nceap
a trimiterea semnalelor nainte ca copilul s
a-si
instaleze handler-ul (si ar fi folosit handler-ul implicit al lui SIGUSR1, care
termina procesul);
- cauza interbloc
arii sau num
ar
arii gresite este c
a semnalele nu sunt tratate
ntotdeauna n locul unde sunt asteptate.


Variant
a CORECTA:

Semnale

#include<sys/types.h>
#include<unistd.h>
#include<signal.h>
#include <sys/wait.h>
#include<stdio.h>
#include<stdlib.h>
volatile int nr; char *nume;
void f(int n){signal(n,f);}
void g(int n){printf("%s: %d\n",nume,nr); exit(0);}
int main(){
pid_t p; sigset_t ms;
signal(SIGUSR1,f); signal(SIGINT,g);
sigemptyset(&ms); sigaddset(&ms,SIGUSR1); sigprocmask(SIG_SETMASK,&ms,NULL);
sigemptyset(&ms); nr=0;
if(p=fork()){nume="parinte";
while(nr<2000){kill(p,SIGUSR1); ++nr; sigsuspend(&ms);}
wait(NULL);
printf("final parinte\n");
}else{nume="copil"; p=getppid();
while(nr<2000){sigsuspend(&ms); ++nr; kill(p,SIGUSR1);}
printf("final copil\n");
}
return 0;
}

Semnale

Comentarii:
n afara lui sigsuspend() SIGUSR1 este blocat (deci dac
a vine r
am
ane n
pending), iar n sigsuspend() este deblocat;
blocarea / deblocarea si intrarea n asteptare sunt efectuate de sigsuspend()
n mod atomic, a.. nu exist
a riscul ca vreun SIGUSR1 s
a fie tratat ntre
deblocare si intrarea n asteptare; n plus, logica programului face ca fiecare
proces s
a nu poat
a trimite dou
a semnale f
ar
a s
a primeasc
a un semnal ntre ele,
astfel c
a destinatarul nu poate pierde un semnal pentru c
a avea deja unul
netratat n pending.

Semnale
Exemplu: utilizare sigaction() si diverse fenomene legate de semnale:
#include<sys/types.h>
#include<unistd.h>
#include<signal.h>
#include<stdio.h>
void h(int n){printf("inceput %d\n",n); sleep(1); printf("sfarsit %d\n",n);}
int main(){
pid_t p; int i; struct sigaction s;
sigset_t ms;
sigemptyset(&ms); for(i=1;i<=3;++i)sigaddset(&ms,i);
sigprocmask(SIG_SETMASK,&ms,NULL);
s.sa_handler=h; s.sa_flags=0;
s.sa_mask=ms; sigaction(1,&s,NULL);
sigdelset(&s.sa_mask,3); sigaction(2,&s,NULL); sigaction(3,&s,NULL);
if(fork()){
sleep(4); sigpending(&ms);
for(i=1;i<NSIG;++i)if(sigismember(&ms,i)) printf("%d ",i); printf("\n");
sigemptyset(&ms); sigprocmask(SIG_SETMASK,&ms,NULL);
}else{p=getppid();
kill(p,1); kill(p,1); kill(p,1); sleep(1);
kill(p,2); sleep(1); kill (p,3);
}
return 0;
}

Semnale
Comentarii:
- la rulare afisaz
a:
1 2 3
inceput
sfarsit
inceput
inceput
sfarsit
sfarsit

1
1
2
3
3
2

- initial p
arintele si-a blocat semnalele 1,2,3, apoi a adormit 4 secunde, timp n
care copilul i-a trimis mai multe din aceste semnale; p
arintele, av
andu-le
blocate, nu s-a trezit din sleep() din cauza lor (a dormit toate cele 4
secunde); mai mult, ultimele dou
a 1 trimise de copil s-au pierdut, deoarece
p
arintele avea deja un 1 in pending;
- dup
a cele 4 secunde, p
arintele si-a consultat semnalele n pending si le-a
afisat, apoi a deblocat semnalele 1,2,3; atunci, semnalele 1,2,3 din pending au
nceput s
a fie tratate (s
a li se apeleze handler-ul f());

Semnale
Comentarii:
- la rulare afisaz
a:
1 2 3
inceput
sfarsit
inceput
inceput
sfarsit
sfarsit

1
1
2
3
3
2

- semnalele din pending au fost tratate n ordine crescatoare, numai c


a odat
a
lansat f(1) semnalele 2,3 erau blocate (pentru 1 f() a fost instalat a.. pe
perioada executiei sale s
a fie blocate suplimentar semnalele 1,2,3), asa c
a f(1)
s-a executat nentrerupt; odat
a lansat f(2), deoarece era un 3 n pending, nu
era blocat (pentru 2 f() a fost instalat a.. pe perioada execut
arii sale s
a fie
blocate suplimentar doar 1,2) iar executia lui f() se face n user mode, f(2)
s-a ntrerupt ca s
a se execute f(3), iar dup
a revenire s-a continuat.

Semnale
Exemplu: folosire alarm():
#include<signal.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
void h(int n){printf("Timpul a expirat\n"); exit(1);}
int main(){int c;
signal(SIGALRM,h);
printf("Asa e ? (d/n): "); alarm(10);
scanf("%c",&c);
alarm(0);
printf("Multumesc pentru raspuns !\n");
return 0;
}

Comentariu: dup
a afisarea ntreb
arii Asa e ? (d/n): , dac
a utilizatorul
r
aspunde n < 10 secunde, se afisaz
a Multumesc pentru raspuns !\n,
alarma se anuleaz
a iar procesul se termin
a; dac
a nu raspunde n timp util,
procesul primeste alarma, handler-ul afisaz
a Timpul a expirat\n iar
procesul se termin
a (nu mai este afisat Multumesc...).

Semnale

Exemplu: folosirea alarm() si sigsetjmp()/siglongjmp() la implementarea


manual
a (f
acut
a explicit de programator) a thread-urilor n spatiul utilizator.
Este urmat tiparul unui exemplu similar din cursul 5, dar se folosesc
sigsetjmp()/siglongjmp() n loc de setjmp()/ longjmp() (se pot folosi si
ultimele, nu este esential), iar thread-urile nu mai cedeaz
a voluntar procesorul
prin apeluri de tip thread yield() ci sunt comutate automat la anumite
intervale de timp, folosind semnale SIGALRM si apeluri alarm().

Semnale
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<stdarg.h>
#include<setjmp.h>
#include<signal.h>
#include<unistd.h>
/* executiv */
#define MAXTHREADS 100
struct thread_table_entry{
sigjmp_buf j[2]; int s;
void (*f)();
} thread_table[MAXTHREADS];
int nthreads, tcurrent;
sigjmp_buf jscheduler, jmain;
unsigned int thread_set[MAXTHREADS], nthreadset;

Semnale
int thread_create(void (*pf)()){
int i,k;
if(nthreadset==nthreads)return -1;
for(i=0;i<nthreads;++i){
for(k=0;k<nthreadset;++k)if(thread_set[k]==i)break;
if(k==nthreadset)break;
}
thread_table[i].f=pf;
thread_table[i].s=0;
thread_set[nthreadset]=i; ++nthreadset;
return i;
}
void thread_terminate(){
int k;
if(nthreadset==0)return;
alarm(0);
for(k=0;k<nthreadset;++k)if(thread_set[k]==tcurrent)break;
--nthreadset; thread_set[k]=thread_set[nthreadset];
siglongjmp(jscheduler,1);
}

Semnale

void schedule_threads(){
if(nthreadset==0){tcurrent=-1; siglongjmp(jmain,1);}
tcurrent=thread_set[rand()%nthreadset];
alarm(1);
if(thread_table[tcurrent].s==0)
{thread_table[tcurrent].s=1; siglongjmp(thread_table[tcurrent].j[0],1);}
else siglongjmp(thread_table[tcurrent].j[1],1);
}
void thread_handler(int n){
signal(n,thread_handler);
if(!sigsetjmp(thread_table[tcurrent].j[1], 1)) siglongjmp(jscheduler,1);
}

Semnale

void initialize_threads(unsigned int nt, unsigned int dim){


static int dimaux=0;
unsigned char buf[1024];
if(dimaux==0){
if(nt>MAXTHREADS || dim==0)exit(1);
srand(time(NULL));
nthreads=nt; dimaux=dim;
nthreadset=0; tcurrent=-1;
signal(SIGALRM,thread_handler);
}
if(dim>0)initialize_threads(nt,dim-1);
else if(nt>0)initialize_threads(nt-1,dimaux);
if(dim==0)
if(nt==nthreads){dimaux=0; if(sigsetjmp(jscheduler, 1)) schedule_threads(); }
else if(sigsetjmp(thread_table[nt].j[0], 1)){
(*thread_table[nt].f)();
thread_terminate();
}
}
void start_threads(){if(!sigsetjmp(jmain, 1))siglongjmp(jscheduler,1);}

Semnale
/* aplicatie */
int vglobal;
void f2(){
int vf2=20,i;
for(i=0;i<4;++i){
sleep(1);
printf("f2: vf2=%d, vglobal=%d\n",++vf2,++vglobal);
}
}
void f1(){
int vf1=10; int j;
thread_create(f2);
for(j=0;j<2;++j){
sleep(1);
printf("f1: vf1=%d, vglobal=%d\n",++vf1,++vglobal);
}
}
int main(){
initialize_threads(2,1);
vglobal=0;
thread_create(f1); start_threads();
return 0;
}

Semnale

La rulare programul poate afisa de exemplu:


f1:
f2:
f2:
f2:
f1:
f2:

vf1=11,
vf2=21,
vf2=22,
vf2=23,
vf1=12,
vf2=24,

vglobal=1
vglobal=2
vglobal=3
vglobal=4
vglobal=5
vglobal=6

Pentru alte comentarii, a se vedea cursul 5.

Semnale
Exemplu: gestionarea erorilor fatale (involuntare) (a se vedea cursul 5):
#include<setjmp.h>
#include<signal.h>
#include<stdio.h>
sigjmp_buf stare;
void h(int n){siglongjmp(stare,1);}
int main(){
int n,*p=NULL;
signal(SIGSEGV,h);
if(!sigsetjmp(stare, 1))
n=*p;
else
fprintf(stderr,"Eroare.\n");
return 0;
}

Comentariu: la prima nt
alnire a apelului sigsetjmp(stare, 1) acesta salveaz
a
n stare contextul de dinaintea operatiei riscante si returneaz
a 0; atunci se
intr
a pe ramura operatiei riscante n=*p;, aceasta genereaz
a o eroare fatal
aaccesarea prin instructiuni utilizator a unei zone din afara spatiului de adrese al
procesului - procesul primeste semnalul SIGSEGV, iar handler-ul asociat
restaureaz
a contextul salvat n stare; atunci se nt
alneste pentru a doua oar
a
apelul sigsetjmp(stare, 1), acum acesta returneaz
a 1 (al doilea parametru al
lui siglongjmp()) si astfel se intr
a pe ramura else.

Instrumente de comunicare intre procese


Conditii de curs
a
Regiuni critice
Dezactivarea ntreruperilor
Variabile z
avor
Alternarea strict
a
Solutia lui Peterson
Instructiunea TSL
Sleep si Wakeup
Semafoare
Mutex
Monitoare
Transfer de mesaje
Bariere
Probleme clasice ale comunicarii interprocese
Problema filozofilor care mananc
a
Problema cititorilor si scriitorilor
Problema frizerului somnoros
Cazul UNIX/Linux
Semnale
IPC
IPC - segmente de memorie partajat
a
IPC - vectori de semafoare
IPC - cozi de mesaje
Fisiere tub

Cuprins

IPC
IPC (Inter Process Communication) desemneaz
a o categorie de entit
ati prin
intermediul c
arora se poate realiza comunicarea ntre procese; ele pot fi de trei
tipuri:
- segment de memorie partajat
a (shared memory segment);
- vector de semafoare (semaphore set);
- coad
a de mesaje (message queue).
Orice IPC are un tip (din cele 3), o cheie extern
a (identificator numeric la
nivelul instantei UNIX/Linux n care se afl
a) si niste identificatori numerici
interni (la nivelul diverselor procese care l folosesc). Identificatorii numerici
externi sunt de tip key t, definit n sys/types.h.
Desi IPC-urile nu sunt tratate ca fisiere, ele au multe atribute asemanatoare:
proprietar, drepturi de acces, momentul ultimului acces, momentul ultimei
modific
ari, etc.

IPC

IPC-urile existente n sistem se pot vizualiza cu comanda shell ipcs (f


ar
a
optiuni le afisaz
a pe toate, cu optiunile -m / -s / -q afisaz
a numai
segmentele de memorie partajat
a / vectorii de semafoare / cozile de mesaje) si
se pot sterge cu comanda shell ipcrm (n forma ipcrm -M key /
ipcrm -S key / ipcrm -Q key sterge segmentul de memorie partajat
a/
vectorul de semafoare / coada de mesaje cu cheia key); IPC-rile pot fi sterse
doar de proprietarul lor sau root.

IPC

Inainte de a crea un IPC, trebuie s


a construim o cheie pentru el; pentru aceasta
folosim apelul:
# include <sys/types.h>
# include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);

= calculeaz
a si returneaz
a o cheie plec
and de la num
arul de disc si num
arul
de i-nod al fisierului cu numele si calea specificate de pathname (trebuie s
a
fie un fisier existent si accesibil) si de la octetul low al ntregului proj_id
(care trebuie s
a fie nenul); nu conteaz
a continutul fisierului; rezultatul va fi
acelasi pentru toate pathname-urile care se refer
a la acelasi fisier, dac
a
folosim acelasi proj_id; n caz de eroare returneaz
a -1 iar errno este setat ca
la apelul stat() (a se vedea cursul despre gestiunea fisierelor).

Instrumente de comunicare intre procese


Conditii de curs
a
Regiuni critice
Dezactivarea ntreruperilor
Variabile z
avor
Alternarea strict
a
Solutia lui Peterson
Instructiunea TSL
Sleep si Wakeup
Semafoare
Mutex
Monitoare
Transfer de mesaje
Bariere
Probleme clasice ale comunicarii interprocese
Problema filozofilor care mananc
a
Problema cititorilor si scriitorilor
Problema frizerului somnoros
Cazul UNIX/Linux
Semnale
IPC
IPC - segmente de memorie partajat
a
IPC - vectori de semafoare
IPC - cozi de mesaje
Fisiere tub

Cuprins

IPC - segmente de memorie partajata


Un proces poate crea si/sau poate obtine un identificator intern propriu pentru
un segment de memorie partajat
a cu apelul:
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, int size, int shmflg);

= returneaz
a un identificator numeric al segmentului de memorie partajat
a
cu cheia key, care va fi intern la nivelul procesului apelant;
key poate fi IPC PRIVATE sau o cheie;
size este o dimensiune n bytes;
shmflg poate fi 0 sau o disjunctie pe biti de constantele simbolice
IPC CREAT, IPC EXCL si constantele simbolice ce descriu drepturile fisierelor
create cu open() pentru proprietar, grup, altii (de ex. pentru drepturi de
read/write pentru proprietar putem ad
auga S IRWXU) - a se vedea cursul
despre gestiunea fisierelor;

IPC - segmente de memorie partajata


Un proces poate crea si/sau poate obtine un identificator intern propriu pentru
un segment de memorie partajat
a cu apelul:
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, int size, int shmflg);

=
dac
a key este IPC PRIVATE, sau dac
a key nu este IPC PRIVATE
dar nu exist
a segmente cu cheia key si n shmflg apare IPC CREAT,
atunci se creaz
a un segment nou (n cazul IPC PRIVATE el va fi accesibil doar
procesului care l-a creat si descendentilor lui); segmentul va avea ca proprietar
proprietarul efectiv al procesului, dimensiunea size rotunjit
a la un multiplu
al valorii PAGE SIZE si drepturile date de ultimii 9 biti semnificativi ai
constantelor referitoare la drepturi prezente n shmflg; size trebuie sa fie
n intervalul [SHMMIN, SHMMAX]; apelul returneaz
a un identificator intern
pentru acest segment;

IPC - segmente de memorie partajata


Un proces poate crea si/sau poate obtine un identificator intern propriu pentru
un segment de memorie partajat
a cu apelul:
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, int size, int shmflg);

=
dac
a exist
a un segment cu cheia key iar n shmflg apare at
at
at si IPC EXCL, apelul esueaza (cu errno = EEXIST);
IPC CREAT c
dac
a exist
a un segment cu cheia key si nu am folosit IPC CREAT,
apelul verific
a dac
a procesul apelant are drepturile necesare pentru a-l accesa si
dac
a size este dimensiunea cu care a fost creat segmentul; n caz
afirmativ apelul returneaz
a un identificator intern pentru acest segment;

IPC - segmente de memorie partajata


Un proces poate crea si/sau poate obtine un identificator intern propriu pentru
un segment de memorie partajat
a cu apelul:
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, int size, int shmflg);

=
la esec, apelul returneaz
a -1; errno posibile: EACCES, EEXIST, EINVAL,
ENFILE, ENOENT, ENOMEM, ENOSPC;

IPC - segmente de memorie partajata


Odata ce a obtinut un identificator intern pentru un segment, procesul l poate
atasa n spatiul s
au de adrese la o anumit
a adres
a (dup
a care i poate accesa
continutul ncep
and de la acea adres
a cu instructiuni obisnuite) folosind apelul:
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);

= atasaza segmentul identificat de shmid la spatiul de adrese al


procesului apelant; adresa de atasare este specificat
a de shmaddr, dup
a
urm
atoarele criterii:
- dac
a shmaddr este NULL, sistemul alege o adres
a (nefolosit
a); este
varianta recomandabil
a;
- dac
a shmaddr nu este NULL iar SHM RND este prezent n shmflg,
adresa de atasare este shmaddr rotunjit
a prin lips
a la cel mai apropiat
multiplu de SHMLBA; daca SHM RND este absent, shmaddr trebuie s
a fie o
adres
a aliniat
a la nivel de pagin
a, si la ea se va atasa segmentul;
shmflg poate fi 0 sau o disjunctie pe biti de constantele simbolice
SHM RND (cu sensul mentionat mai sus), SHM RDONLY (si atunci procesul
apelant va putea accesa segmentul doar n citire - altfel l poate accesa si n
citire si n scriere);

IPC - segmente de memorie partajata


Odata ce a obtinut un identificator intern pentru un segment, procesul l poate
atasa n spatiul s
au de adrese la o anumit
a adres
a (dup
a care i poate accesa
continutul ncep
and de la acea adres
a cu instructiuni obisnuite) folosind apelul:
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);

=
procesul trebuie s
a aibe drepturile necesare tipului de acces intentionat
(citire sau citire si scriere);
la succes apelul shmat() returneaza adresa de atasare, la esec
returneaz
a -1;
errno posibile: EACCES, EINVAL, ENOMEM.

IPC - segmente de memorie partajata

Procesul poate accesa segmentul atasat prin instructiuni obisnuite, folosind de


exemplu pointeri, p
ana l detasaz
a cu apelul:
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);

= detasaz
a segmentul atasat spatiului de adrese al procesului apelant la
adresa shmaddr (returnat
a n prealabil de shmat());
returneaz
a 0 la succes si -1 la esec;
errno posibile: EINVAL.

IPC - segmente de memorie partajata


Atributele unui segment se pot gestiona cu apelul:
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

= consult
a/seteaz
a atributele segmentului cu identificatorul intern shmid
(inclusiv poate marca segmentul pentru distrugere);

IPC - segmente de memorie partajata


Atributele unui segment se pot gestiona cu apelul:
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

=
buf este adresa unei structuri de tip shmid ds, definit n sys/shm.h
astfel:
struct shmid_ds {
struct ipc_perm
size_t
time_t
time_t
time_t
pid_t
pid_t
shmatt_t
...
};

shm_perm;
shm_segsz;
shm_atime;
shm_dtime;
shm_ctime;
shm_cpid;
shm_lpid;
shm_nattch;

/*
/*
/*
/*
/*
/*
/*
/*

Ownership and permissions */


Size of segment (bytes) */
Last attach time */
Last detach time */
Last change time */
PID of creator */
PID of last shmat()/shmdt() */
No. of current attaches */

IPC - segmente de memorie partajata


Atributele unui segment se pot gestiona cu apelul:
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

=
tipul structur
a ipc perm este definit n sys/ipc.h astfel (am evidentiat cu
/* setabil */ c
ampurile setabile cu IPC SET):
struct ipc_perm {
key_t key;
/* setabil */ uid_t uid;
/* setabil */ gid_t gid;
uid_t cuid;
gid_t cgid;
/* setabil */ unsigned short mode;
unsigned short seq;
};

/*
/*
/*
/*
/*
/*

Key supplied to shmget() */


Effective UID of owner */
Effective GID of owner */
Effective UID of creator */
Effective GID of creator */
Permissions + SHM_DEST and
SHM_LOCKED flags */
/* Sequence number */

IPC - segmente de memorie partajata


Atributele unui segment se pot gestiona cu apelul:
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

=
cmd poate avea valorile:
IPC STAT = copiaz
a atributele segmentului n structura pointat
a de buf
(apelantul trebuie s
a aibe drept de citire pe segment);

IPC - segmente de memorie partajata


Atributele unui segment se pot gestiona cu apelul:
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

=
cmd poate avea valorile:
IPC SET = seteaz
a anumite atribute ale segmentului cu cele date prin
structura pointat
a de buf;
se pot modifica atributele: shm perm.uid, shm perm.gid, si ultimii 9 biti
semnificativi ai lui shm perm.mode;
procesul apelant trebuie s
a fie privilegiat, sau proprietarul s
au efectiv trebuie
s
a coincida cu proprietarul (shm perm.uid) sau creatorul (shm perm.cuid)
segmentului;

IPC - segmente de memorie partajata


Atributele unui segment se pot gestiona cu apelul:
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

=
cmd poate avea valorile:
IPC RMID = marcheaz
a segmentul pentru distrugere (practic, un flag
nestandard SHM DEST al lui shm perm.mode va fi setat);
procesul apelant trebuie s
a fie privilegiat sau proprietarul sau creatorul
segmentului;

IPC - segmente de memorie partajata


Atributele unui segment se pot gestiona cu apelul:
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

=
Observatii:
1. apelurile shmget(), shmat(), shmdt(), shmctl() actualizeaz
a
automat anumite atribute ale segmentului la care se refer
a.
2. Un segment este distrus efectiv doar n momentul c
and sunt ndeplinite
simultan dou
a conditii: este marcat pentru distrugere si nici un proces nu mai
este atasat la el (i.e. shm nattch devine 0); apelurile shmat()/ shmdt()
influienteaz
a shm nattch;
3. Un segment nemarcat pentru distrugere persist
a n sistem chiar dac
a nu
mai sunt procese atasate la el si si p
astreaza continutul (poate fi reg
asit de
procese ce se atasaz
a ulterior la el) - deci, dac
a vrem ca segmentele s
a dispar
a,
trebuie distruse explicit;
4. In Linux un proces se poate atasa la un segment chiar dac
a este marcat
pentru distrugere (n unele implementari UNIX nu se poate).
5. la fork() se mostenesc segmentele atasate, la exec() si exit()
segmentele atasate se detasaz
a;

IPC - segmente de memorie partajata


Atributele unui segment se pot gestiona cu apelul:
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

=
Apelul shmctl() returneaz
a 0 la succes si -1 la esec;
errno posibile: EACCES, EFAULT, EIDRM, EINVAL, ENOMEM,
EOVERFLOW, EPERM.

IPC - segmente de memorie partajata

Exemplu: problema producator-consumator cu un segment de memorie


partajat
a si sincronizare prin semnale:
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<unistd.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<signal.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<stdio.h>

IPC - segm. de mem. partajata

#define N 10
pid_t p,c;
int shmid; int *buf;
sigset_t ms;
void finalizare(int);
void f(int sig){signal(sig,f);}
void g(int sig){if(p==getpid()){wait(NULL); finalizare(0);} else exit(0);}
void initializare(){
if((shmid=shmget(IPC_PRIVATE,(N+1)*sizeof(int),S_IRWXU))==-1)
{perror("shmget"); finalizare(1);}
if((buf=(int*)shmat(shmid,NULL,0))==(int *)-1)
{perror("shmat"); finalizare(2);}
signal(SIGUSR1,f); signal(SIGINT,g);
sigemptyset(&ms); sigaddset(&ms, SIGUSR1);
sigprocmask(SIG_SETMASK,&ms,NULL);
sigemptyset(&ms);
}
void finalizare(int n){
switch(n){
default:
shmdt(buf);
case 2: shmctl(shmid,IPC_RMID,NULL);
case 1: ;
}
exit(n);
}

IPC - segm. de mem. partajata

int main(){
int b,v;
initializare(); p=getpid();
b=v=0; buf[N]=0; /* buf[N]: count; buf[0] ... buf[N-1]: buffer */
if(c=fork()){ /* parinte = producator */
int item; item=0;
while(1){
++item;
/* item=produce_item() */
if(buf[N]==N)sigsuspend(&ms); /* if(count==N)sleep() */
buf[b]=item; b=(b+1)%N;
/* insert_item(item) */
++buf[N];
/* count=count+1 */
if(buf[N]==1)kill(c,SIGUSR1); /* if(count==1)wakeup(consumer) */
}
}else{
/* copil = consumator */
int item;
while(1){
if(buf[N]==0)sigsuspend(&ms); /* if(count==0)sleep() */
item=buf[v]; v=(v+1)%N;
/* item=remove_item() */
--buf[N];
/* count=count-1 */
if(buf[N]==N-1)kill(p,SIGUSR1);/* if(count==N-1)wakeup(producer) */
printf("%d\n",item);
/* consume_item(item) */
}
}
finalizare(0);
return 0;
}

IPC - segmente de memorie partajata


Comentarii:
- Structura programului de mai sus urmeaz
a tiparul rezolv
arii date n sectiunea
Sleep si Wakeup, dar elimin
a conditiile de curs
a ap
arute acolo, deoarece n
afara apelurilor sigsuspend() semnalul SIGUSR1 este blocat si doar n
sigsuspend() este neblocat - deci dac
a vine vreun asemenea semnal, r
am
ane
n pending p
an
a n locul unde este asteptat (adic
a n sigsupend()); n plus,
logica programului face ca nici un proces s
a nu trimit
a un nou SIGUSR1 p
an
a
nu primeste un SIGUSR1, a.. nu este posibil
a pierderea de semnale (care ar
putea ap
area dac
a s-ar primi un nou SIGUSR1 c
at timp exist
a unul n pending).
- Deoarece ambele procese efectueaz
a cicluri infinite, ele trebuie terminate
fortat - n acest scop se va tasta Ctrl-C si atunci ambele vor primi c
ate un
semnal SIGINT (ambele sunt n foreground), iar handler-ul asociat le va
termina; n plus, n procesul p
arinte, acest handler asteapt
a terminarea copilului
(dac
a p
arintele se termin
a primul, copilul se mut
a n background si n-ar mai
putea putea afisa ultimile informatii pe terminal dec
at dac
a terminalul este
setat stty -tostop) si dezaloc
a resusele partajate.
- Zona tampon partajat
a este organizat
a ca o coad
a alocat
a ntr-un vector
parcurs circular - indicii baz
a b (unde se introduce) si v
arf v (de unde se
extrage) cresc cu 1 modulo N.

Instrumente de comunicare intre procese


Conditii de curs
a
Regiuni critice
Dezactivarea ntreruperilor
Variabile z
avor
Alternarea strict
a
Solutia lui Peterson
Instructiunea TSL
Sleep si Wakeup
Semafoare
Mutex
Monitoare
Transfer de mesaje
Bariere
Probleme clasice ale comunicarii interprocese
Problema filozofilor care mananc
a
Problema cititorilor si scriitorilor
Problema frizerului somnoros
Cazul UNIX/Linux
Semnale
IPC
IPC - segmente de memorie partajat
a
IPC - vectori de semafoare
IPC - cozi de mesaje
Fisiere tub

Cuprins

IPC - vectori de semafoare

Asupra semafoarelor UNIX/Linux se pot efectua operatiile up, down


(desrise mai nainte) si o operatie nou
a, anume adormirea procesului p
ana
valoarea semaforului devine 0 (n urma unor operatii down efectuate de alte
procese).

IPC - vectori de semafoare


Un proces poate crea si/sau poate obtine un identificator intern propriu pentru
un vector de semafoare cu apelul:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);

= returneaz
a un identificator numeric al vectorului de semafoare cu cheia
key, care va fi intern la nivelul procesului apelant;
parametrii key si semflg au aceleasi valori si semnificatii ca
parametrii key si shmflg de la shmget();
nsems reprezint
a nr. de semafoare create n vector (dac
a se creaz
a un
vector - atunci acest nr. trebuie s
a fie valoarea SEMMSL) sau folosite din
vector (dac
a se acceseaza un vector existent - atunci acest nr. trebuie s
a fie
nr. specificat la crearea vectorului); dac
a se acceseaz
a un vector existent, acest
nr. poate fi 0 (dont care);
la esec, apelul returneaz
a -1; errno posibile: EACCES, EEXIST, EINVAL,
ENOENT, ENOMEM, ENOSPC.

IPC - vectori de semafoare


Asupra unui vector de semafoare (pentru care procesul a obtinut n prealabil un
identificator intern) se poate face un vector de operatii, cu apelul:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, unsigned nsops);

= aplic
a vectorului de semafoare identificat de semid vectorul de operatii
pointat de sops si av
and lungimea nsops;

IPC - vectori de semafoare


Asupra unui vector de semafoare (pentru care procesul a obtinut n prealabil un
identificator intern) se poate face un vector de operatii, cu apelul:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, unsigned nsops);

=
o operatie asupra unui semafor este indicat
a printr-o structur
a sembuf
definit
a astfel:
struct sembuf{
unsigned short sem_num;
short
sem_op;
short
sem_flg;
}

membrii structurii sembuf au semnificatiile:


sem num: indicele semaforului din vector c
aruia i se aplic
a operatia;

IPC - vectori de semafoare


Asupra unui vector de semafoare (pentru care procesul a obtinut n prealabil un
identificator intern) se poate face un vector de operatii, cu apelul:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, unsigned nsops);

=
o operatie asupra unui semafor este indicat
a printr-o structur
a sembuf
definit
a astfel:
struct sembuf{
unsigned short sem_num;
short
sem_op;
short
sem_flg;
}

membrii structurii sembuf au semnificatiile:


sem op: > 0 incrementare cu valoarea respectiv
a (n sensul up);
< 0 decrementare cu valoarea respectiv
a (n sensul down);
= 0 procesul adoarme p
ana valoarea devine 0;
n primele dou
a cazuri, procesul trebuie s
a aibe drept de modificare asupra
vectorului de semafoare; n ultimul caz trebuie sa aibe drept de citire;

IPC - vectori de semafoare


Asupra unui vector de semafoare (pentru care procesul a obtinut n prealabil un
identificator intern) se poate face un vector de operatii, cu apelul:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, unsigned nsops);

=
o operatie asupra unui semafor este indicat
a printr-o structur
a sembuf
definit
a astfel:
struct sembuf{
unsigned short sem_num;
short
sem_op;
short
sem_flg;
}

membrii structurii sembuf au semnificatiile:


sem flg: poate fi 0 sau disjunctie pe biti de constantele simbolice:
IPC NOWAIT operatia este neblocant
a (n loc s
a adoarm
a procesul,
operatiile nu se efectueaz
a, apelul returneaz
a -1 si seteaz
a errno = EAGAIN);
not
am c
a operatiile ce pot adormi procesul sunt sem op < 0 si sem op = 0;
SEM UNDO operatia va fi anulat
a la terminarea procesului care a
efectuat-o;

IPC - vectori de semafoare


Asupra unui vector de semafoare (pentru care procesul a obtinut n prealabil un
identificator intern) se poate face un vector de operatii, cu apelul:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, unsigned nsops);

=
apelul returneaz
a 0 la succes si -1 la esec; errno posibile: E2BIG, EACCES,
EAGAIN, EFAULT, EFBIG, EIDRM, EINTR, EINVAL, ENOMEM, ERANGE.
Observatii esentiale:
- multimea operatiilor din vectorul specificat se efetueaz
a ATOMIC;
- dac
a apelul reuseste, se garanteaz
a c
a toate operatiile au reusit, iar dac
a
apelul esueaz
a, se garanteaz
a ca nici una din operatii nu s-a efectuat;

IPC - vectori de semafoare


Atributele unui vector de semafoare se pot gestiona cu apelul:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);

=
semid este identificatorul intern al unui vector de semafoare;
semnum este indicele unui semafor din acest vector (num
ar
and de la 0);
cmd este tipul operatiei efectuate;
n functie de valoarea lui cmd, poate exista si un al 4-lea parametru, pe care-l
vom numi arg;

IPC - vectori de semafoare


Atributele unui vector de semafoare se pot gestiona cu apelul:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);

=
cmd poate fi:
GETNCNT = apelul returneaz
a nr. de procese ce asteapt
a incrementarea
semaforului de indice semnum;
GETZCNT = apelul returneaz
a nr. de procese ce asteapta ca valoarea
semaforului de indice semnum s
a ajung
a 0;
GETVAL = apelul returneaz
a valoarea semaforului de indice semnum;
GETALL = apelul furnizeaz
a valorile tuturor semafoarelor din vector n
componentele vectorului pointat de arg, care trebuie s
a fie de tip unsigned
short *; parametrul semnum este ignorat;
GETPID = apelul returneaz
a PID-ul ultimului proces care a efectuat o
operatie asupra semaforului de indice semnum;

IPC - vectori de semafoare


Atributele unui vector de semafoare se pot gestiona cu apelul:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);

=
SETVAL = valoarea semaforului de indice semnum devine arg, care
trebuie s
a fie de tip int; se anuleaz
a setarile undo pentru semaforul
modificat n toate procesele; returneaz
a 0=succes, -1=esec;
SETALL = valorile tuturor semafoarelor din vector sunt setate cu
componentele vectorului pointat de arg, care trebuie s
a fie de tip unsigned
short *; parametrul semnum este ignorat; alte detalii sunt ca la SETVAL;

IPC - vectori de semafoare


Atributele unui vector de semafoare se pot gestiona cu apelul:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);

=
IPC STAT = furnizeaz
a n structura pointat
a de arg, care trebuie s
a fie de
tip struct semid ds *, atributele vectorului de semafoare; parametrul
semnum este ignorat;
a unele atribute ale vectorului de semafoare conform
IPC SET = seteaz
structurii pointate de arg, care trebuie s
a fie de tip struct semid ds *;
se pot modifica doar membrii sem perm.uid, sem perm.gid si ultimii 9
biti semnificativi ai lui sem perm.mode; parametrul semnum este ignorat;
tipul structur
a semid ds este definit n sys/sem.h astfel:
struct semid_ds {
struct ipc_perm
time_t
time_t
unsigned short
};

sem_perm;
sem_otime;
sem_ctime;
sem_nsems;

/*
/*
/*
/*

Ownership and permissions */


Last semop time */
Last change time */
No. of semaphores in set */

tipul structur
a ipc perm este definit in sys/ipc.h si a fost descris mai
sus, la apelul shmctl();

IPC - vectori de semafoare


Atributele unui vector de semafoare se pot gestiona cu apelul:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);

=
IPC RMID = distruge imediat vectorul de semafoare, trezind toate procesele
adormite ntr-un semop() la el (lor, semop() le va returna eroare si va seta
errno = EIDRM); parametrul semnum este ignorat;

IPC - vectori de semafoare


Atributele unui vector de semafoare se pot gestiona cu apelul:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);

=
n cazurile GETNCNT, GETZCNT, GETVAL, GETALL, GETPID, IPC STAT
procesul trebuie s
a aibe drept de citire asupra vectorului de semafoare respectiv;
n cazurile SETVAL, SETALL procesul trebuie s
a aibe drept de modificare
asupra vectorului de semafoare respectiv;
n cazurile IPC SET, IPC RMID procesul trebuie s
a fie privilegiat sau
proprietarul s
au efectiv trebuie s
a coincida cu proprietarul sau creatorul
vectorului de semafoare;
n caz de esec apelul semctl() returneaz
a -1, iar n caz de succes returneaz
a
ce am mentionat deja c
and cmd este GETNCNT, GETPID, GETVAL sau
GETZCNT, si 0 c
and cmd are alta valoare;
errno posibile: EACCES, EFAULT, EIDRM, EINVAL, EPERM, ERANGE;
In general, apelurile legate de vectorii de semafoare modific
a automat anumite
atribute ale acestora.

IPC - vectori de semafoare


Exemplu: problema produc
ator-consumator cu un segment de memorie
partajat
a si sincronizare prin semafoare:
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/sem.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/wait.h>
#include<fcntl.h>
#include<signal.h>
#include<stdlib.h>
#include<stdio.h>

IPC - vectori de semafoare


#define N 10
pid_t p;
int shmid, semid; int *buf;
void finalizare(int);
void g(int sig){if(p==getpid()){wait(NULL); finalizare(0);} else exit(0);}
void initializare(){
if((shmid=shmget(IPC_PRIVATE,N*sizeof(int),S_IRWXU))==-1)
{perror("shmget"); finalizare(1);}
if((semid=semget(IPC_PRIVATE,3,S_IRWXU))==-1)
{perror("semget"); finalizare(2);}
if((buf=(int*)shmat(shmid,NULL,0))==(int *)-1)
{perror("shmat"); finalizare(3);}
signal(SIGINT,g);
}
void finalizare(int n){
switch(n){
default:
shmdt(buf);
case 3: semctl(semid,0,IPC_RMID);
case 2: shmctl(shmid,IPC_RMID,NULL);
case 1: ;
}
exit(n);
}

IPC - vectori de semafoare


int main(){
int b,v; struct sembuf sop;
initializare();
p=getpid(); sop.sem_flg=0; /* sem_num: 0=mutex, 1=empty, 2=full */
sop.sem_num=0; sop.sem_op=1; semop(semid,&sop,1);
/* mutex:=1 */
sop.sem_num=1; sop.sem_op=N; semop(semid,&sop,1);
/* empty:=N */
b=v=0; /* buf[0] ... buf[N-1]: buffer */
if(fork()){ /* parinte = producator */
int item; item=0;
while(1){
++item;
/* item=produce_item()
sop.sem_num=1; sop.sem_op=-1; semop(semid,&sop,1); /* down(&empty)
sop.sem_num=0; sop.sem_op=-1; semop(semid,&sop,1); /* down(&mutex)
buf[b]=item; b=(b+1)%N;
/* insert_item(item)
sop.sem_num=0; sop.sem_op=1; semop(semid,&sop,1);
/* up(&mutex)
sop.sem_num=2; sop.sem_op=1; semop(semid,&sop,1);
/* up(&full)
}
}

*/
*/
*/
*/
*/
*/

IPC - vectori de semafoare

else{
/* copil = consumator */
int item;
while(1){
sop.sem_num=2; sop.sem_op=-1; semop(semid,&sop,1);
/* down(&full)
sop.sem_num=0; sop.sem_op=-1; semop(semid,&sop,1); /* down(&mutex)
item=buf[v]; v=(v+1)%N;
/* item=remove_item()
sop.sem_num=0; sop.sem_op=1; semop(semid,&sop,1);
/* up(&mutex)
sop.sem_num=1; sop.sem_op=1; semop(semid,&sop,1);
/* up(&empty)
printf("%d\n",item);
/* consume_item(item)
}
}
finalizare(0);
return 0;
}

*/
*/
*/
*/
*/
*/

IPC - vectori de semafoare


Comentarii:
- programul urmeaz
a tiparul solutiei date n sectiunea Semafoare;
- n locul semafoarelor mutex, empty, full s-a folosit un vector de 3
semafoare identificat intern prin semid; n el, semafoarele de indice 0, 1, 2
corespund respectiv lui mutex, empty, full;
pentru a face operatiile de down si up cu oricare dintre acestea, ca si
pentru initializ
arile lui mutex si empty cu 1, respectiv N s-a folosit o
aceeasi structur
a sop initializat
a n diverse feluri si apeluri semop();
- zona tampon este implementat
a n continuare explicit folosind un segment de
memorie partajat
a, dar n-a mai fost nevoie s
a retinem acolo si num
arul
elementelor (semafoarele se ocup
a cu num
ararea);
- alte comentarii sunt ca la solutia dat
a n sectiunea IPC - segmente de
memorie partajat
a.

Instrumente de comunicare intre procese


Conditii de curs
a
Regiuni critice
Dezactivarea ntreruperilor
Variabile z
avor
Alternarea strict
a
Solutia lui Peterson
Instructiunea TSL
Sleep si Wakeup
Semafoare
Mutex
Monitoare
Transfer de mesaje
Bariere
Probleme clasice ale comunicarii interprocese
Problema filozofilor care mananc
a
Problema cititorilor si scriitorilor
Problema frizerului somnoros
Cazul UNIX/Linux
Semnale
IPC
IPC - segmente de memorie partajat
a
IPC - vectori de semafoare
IPC - cozi de mesaje
Fisiere tub

Cuprins

IPC - cozi de mesaje

Mesajele se transmit/receptioneaz
a prin intermediul unor structuri definite de
utilizator, al c
aror prim c
amp este obligatoriu de tip long (nseamn
a tipul
mesajului) si are o valoare > 0; celelalte c
ampuri reprezint
a continutul
mesajului.
In cele ce urmeaz
a, un asemenea tip structur
a va fi denumit generic tip
mesaj.

IPC - cozi de mesaje

Un proces poate crea si/sau poate obtine un identificator intern propriu pentru
o coad
a de mesaje cu apelul:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);

= returneaz
a un identificator numeric al cozii de mesaje cu cheia key,
care va fi intern la nivelul procesului apelant;
parametrii key si msgflg au aceleasi valori si semnificatii ca
parametrii key si shmflg de la shmget();
la esec, apelul returneaz
a -1; errno posibile: EACCES, EEXIST,
ENOENT, ENOMEM, ENOSPC.

IPC - cozi de mesaje


Inserarea unui mesaj ntr-o coad
a se poate face cu apelul:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg);

=
msqid este identificatorul intern al cozii;
msgp este adresa structurii de tip mesaj contin
and mesajul inserat;
msgsz este lungimea informatiei mesajului (deci f
ara lungimea c
ampului ce
contine tipul mesajului); trebuie s
a fie 0;
msgflg poate fi 0 sau IPC NOWAIT;

IPC - cozi de mesaje


Inserarea unui mesaj ntr-o coad
a se poate face cu apelul:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg);

=
apelul insereaz
a n coada o copie a structurii pointate de msgp;
dac
a exist
a suficient spatiu n coad
a, apelul returneaz
a imediat cu succes;
capacitatea cozii este desemnat
a de atributul s
au msg bytes; la creare,
aceast
a capacitate are valoarea de MSGMNB bytes, dar se poate modifica cu
apelul msgctl();

IPC - cozi de mesaje


Inserarea unui mesaj ntr-o coad
a se poate face cu apelul:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg);

=
dac
a nu exist
a suficient spatiu n coad
a, atunci:
- dac
a am folosit IPC NOWAIT apelul returneaz
a imediat cu esec si seteaz
a
errno = EAGAIN;
ana se intr
a
- dac
a nu am folosit IPC NOWAIT, apelul adoarme procesul p
ntr-una din urm
atoarele situatii:
apare spatiu disponibil n coad
a;
coada este distrus
a; atunci apelul returneaz
a cu esec si seteaz
a errno =
EIDRM;
un semnal ntrerupe apelul; atunci apelul returneaz
a cu esec si seteaz
a errno
= EINTR; apelurile msgsnd() nu sunt niciodat
a restartate dup
a ce au fost
ntrerupte de un semnal, chiar dac
a handler-ul semnalului a fost instalat cu
SA RESTART;

IPC - cozi de mesaje


Inserarea unui mesaj ntr-o coad
a se poate face cu apelul:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg);

=
procesul apelant trebuie s
a aibe drept de scriere asupra cozii;
apelul msgsnd() returneaz
a 0 la succes si -1 la esec;
errno posibile: EACCES, EAGAIN, EFAULT, EIDRM, EINTR, EINVAL,
ENOMEM.

IPC - cozi de mesaje


Extragerea unui mesaj dintr-o coad
a se poate face cu apelul:
#include
#include
#include
ssize_t

<sys/types.h>
<sys/ipc.h>
<sys/msg.h>
msgrcv(int msqid, struct msgbuf *msgp, size_t msgsz,
long msgtyp, int msgflg);

=
msqid este identificatorul intern al cozii;
msgp este adresa structurii de tip mesaj n care se va prelua mesajul
extras;
msgsz este lungimea informatiei mesajului (deci f
ar
a lungimea c
ampului ce
contine tipul mesajului); trebuie s
a fie 0;
msgtyp este tipul mesajului ce se doreste a fi extras;
msgflg poate fi 0 sau disjunctie pe biti de constantele simbolice
IPC NOWAIT, MSG NOERROR si MSG EXCEPT;

IPC - cozi de mesaje


Extragerea unui mesaj dintr-o coad
a se poate face cu apelul:
#include
#include
#include
ssize_t

<sys/types.h>
<sys/ipc.h>
<sys/msg.h>
msgrcv(int msqid, struct msgbuf *msgp, size_t msgsz,
long msgtyp, int msgflg);

=
apelul citeste din coad
a un mesaj de tipul specificat de msgtyp n structura
pointat
a de msgp, elimin
and mesajul citit din coad
a;

IPC - cozi de mesaje


Extragerea unui mesaj dintr-o coad
a se poate face cu apelul:
#include
#include
#include
ssize_t

<sys/types.h>
<sys/ipc.h>
<sys/msg.h>
msgrcv(int msqid, struct msgbuf *msgp, size_t msgsz,
long msgtyp, int msgflg);

=
dac
a msgsz este < lungimea mesajului ales pentru citire atunci:
a iar
- dac
a nu am folosit MSG NOERROR, mesajul nu este eliminat din coad
apelul returneaz
a cu esec set
and errno = E2BIG;
- dac
a am folosit MSG NOERROR, mesajul este citit trunchiat (iar partea
trunchiata se pierde);
msgtyp poate fi:
0 se citeste primul mesaj din coad
a;
> 0 se citeste primul mesaj de tip msgtyp (dac
a nu am folosit
MSG EXCEPT) sau care nu este de tip msgtyp (dac
a am folosit
MSG EXCEPT);
< 0 se citeste primul mesaj cu cel mai mic tip mai mic sau egal cu
modulul lui msgtyp;

IPC - cozi de mesaje


Extragerea unui mesaj dintr-o coad
a se poate face cu apelul:
#include
#include
#include
ssize_t

<sys/types.h>
<sys/ipc.h>
<sys/msg.h>
msgrcv(int msqid, struct msgbuf *msgp, size_t msgsz,
long msgtyp, int msgflg);

=
dac
a n coad
a nu exist
a nici un mesaj de tipul dorit atunci:
- dac
a am folosit IPC NOWAIT, apelul returneaz
a imediat cu esec si seteaz
a
errno = ENOMSG;
an
a se intr
a
- dac
a nu am folosit IPC NOWAIT, apelul adoarme procesul p
ntr-una din urm
atoarele situatii:
un mesaj de tipul dorit este inserat n coad
a;
coada este distrus
a; atunci apelul returneaz
a cu esec si seteaz
a errno =
EIDRM;
un semnal ntrerupe apelul; atunci apelul returneaz
a cu esec si seteaz
a errno
= EINTR; apelurile msgrcv() nu sunt niciodat
a restartate dup
a ce au fost
ntrerupte de un semnal, chiar dac
a handler-ul semnalului a fost instalat cu
SA RESTART;

IPC - cozi de mesaje


Extragerea unui mesaj dintr-o coad
a se poate face cu apelul:
#include
#include
#include
ssize_t

<sys/types.h>
<sys/ipc.h>
<sys/msg.h>
msgrcv(int msqid, struct msgbuf *msgp, size_t msgsz,
long msgtyp, int msgflg);

=
procesul apelant trebuie s
a aibe drept de citire asupra cozii;
n caz de succes, apelul msgrcv() returneaz
a numarul de octeti de mesaj
copiati efectiv n structura pointat
a de msgp (excluz
and deci c
ampul cu tipul
mesajului), iar n caz de esec returneaz
a -1;
errno posibile: E2BIG, EACCES, EFAULT, EIDRM, EINTR, EINVAL,
ENOMSG.

IPC - cozi de mesaje


Atributele unei cozi de mesaje se pot gestiona cu apelul:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);

=
msqid este identificatorul intern al unei cozi de mesaje;
cmd este tipul operatiei efectuate;
buf este adresa unei structuri n care se citesc / din care se seteaz
a
atributele cozii;

IPC - cozi de mesaje


Atributele unei cozi de mesaje se pot gestiona cu apelul:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);

=
tipul structur
a msqid ds este definit n sys/msg.h astfel:
struct msqid_ds {
struct ipc_perm
time_t
time_t
time_t
unsigned long

msg_perm;
msg_stime;
msg_rtime;
msg_ctime;
__msg_cbytes;

/*
/*
/*
/*
/*

msgqnum_t

msg_qnum;

/*

msglen_t

msg_qbytes;

/*

pid_t
pid_t

msg_lspid;
msg_lrpid;

/*
/*

Ownership and permissions


Time of last msgsnd() */
Time of last msgrcv() */
Time of last change */
Current number of bytes in
queue (non-standard) */
Current number of messages
in queue */
Maximum number of bytes
allowed in queue */
PID of last msgsnd() */
PID of last msgrcv() */

};

tipul structur
a ipc perm este definit n sys/ipc.h si a fost descris mai
sus, la apelul shmctl();

IPC - cozi de mesaje


Atributele unei cozi de mesaje se pot gestiona cu apelul:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);

=
cmd poate fi:
IPC STAT = furnizeaz
a n structura pointat
a de buf atributele cozii;
procesul apelant trebuie s
a aibe drept de citire asupra cozii;
a unele atribute ale cozii conform structurii pointate de
IPC SET = seteaz
buf; se pot modifica doar membrii msg qbytes, msg perm.uid,
msg perm.gid si ultimii 9 biti semnificativi ai lui msg perm.mode;
IPC RMID = distruge imediat coada, trezind toate procesele adormite ntr-o
citire sau scriere la la (lor, apelurile le vor returna eroare si vor seta errno =
EIDRM); parametrul buf este ignorat (poate fi chiar omis);
n cazurile IPC SET si IPC RMID procesul trebuie sa fie privilegiat sau
proprietarul s
au efectiv trebuie s
a coincid
a cu proprietarul sau creatorul cozii;

IPC - cozi de mesaje

Atributele unei cozi de mesaje se pot gestiona cu apelul:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);

=
apelul msgctl() returneaz
a 0 la succes si -1 la esec;
errno posibile: EACCES, EFAULT, EIDRM, EINVAL, EPERM;
In general, apelurile legate de cozile de mesaje modific
a automat anumite
atribute ale acestora.

IPC - cozi de mesaje

Exemplu: problema produc


ator-consumator cu cu o coad
a de mesaje:
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/wait.h>
#include<fcntl.h>
#include<signal.h>
#include<stdlib.h>
#include<stdio.h>

IPC - cozi de mesaje


pid_t p;
int msqid;
void finalizare(int);
void g(int sig){if(p==getpid()){wait(NULL); finalizare(0);} else exit(0);}
void initializare(){
if((msqid=msgget(IPC_PRIVATE,S_IRWXU))==-1)
{perror("msgget"); finalizare(1);}
signal(SIGINT,g);
}
void finalizare(int n){
switch(n){
default:
msgctl(msqid,IPC_RMID,NULL);
case 1: ;
}
exit(n);
}

IPC - cozi de mesaje


struct mesaj{long tip; int valoare;};
int main(){
initializare(); p=getpid();
if(fork()){
/* parinte = producator */
int item; struct mesaj m;
m.tip=1l; item=0;
while(1){
++item;
/*
m.valoare=item;
/*
msgsnd(msqid,&m,sizeof(int),0);
/*
}
}else{
/* copil = consumator */
int item; struct mesaj m;
while(1){
msgrcv(msqid,&m,sizeof(int),1l,0); /*
item=m.valoare;
/*
printf("%d\n",item);
/*
}
}
finalizare(0);
return 0;
}

item=produce_item() */
build_message(&m,item) */
send(consumer,&m) */

receive(producer,&m) */
item=extract_item(&m) */
consume_item(item) */

IPC - cozi de mesaje


Comentarii:
- programul urmeaz
a tiparul solutiei date n sectiunea Transfer de mesaje,
ns
a consumatorul nu mai trimite produc
atorului mesaje goale - deci
produc
atorul face doar send iar consumatorul doar receive; de aceea,
num
arul mesajelor din sistem nu este constant si astfel produc
atorul ar putea fi
din c
and n c
and blocat n send;
- observ
am structura si mai simpl
a a programului: nu mai trebuie implementat
a
explicit zona tampon (ntr-un segment de memorie partajat
a) si gestionarea ei
ca o coad
a alocat
a ntr-un vector parcurs circular, nu mai trebuie folosite
instrumente auxiliare de sincronizare (semnale, semafoare) - deci coada de
mesaje contine at
at mecanismele de partajare a datelor c
at si pe cele de
sincronizare;
- alte comentarii sunt ca la solutia dat
a n sectiunea IPC - segmente de
memorie partajat
a.

IPC - cozi de mesaje

Instrumentul cozilor de mesaje este at


at de puternic nc
at cu o singur
a coad
a
de mesaje putem stabili o comunicare ntre mai mult de dou
a procese, f
ar
aa
ap
area confuzii ntre mesajele destinate unuia sau altuia - mesajele din coad
a
sunt delimitate logic (deci nu se pot amesteca continuturile lor), iar tipul cu
care sunt marcate face ca fiecare proces sa poat
a selecta doar mesajele
adresate lui.
Prezent
am n continuare o variant
a generalizat
a a problemei produc
ator consumator, n care avem mai multi produc
atori si mai multi consumatori,
fiecare produc
and / consum
and doar item-uri de un anumit tip si comunic
and
printr-o singur
a coad
a de mesaje f
ar
a a ap
area confuzii ntre mesajele destinate
unuia sau altuia:

IPC - cozi de mesaje


#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/wait.h>
#include<fcntl.h>
#include<stdlib.h>
#include<stdio.h>
pid_t p;
int msqid;
void finalizare(int);
void initializare(){
if((msqid=msgget(IPC_PRIVATE,S_IRWXU))==-1)
{perror("msgget"); finalizare(1);}
}
void finalizare(int n){
switch(n){
default:
msgctl(msqid,IPC_RMID,NULL);
case 1: ;
}
exit(n);
}

IPC - cozi de mesaje


struct mesaj{long tip; int valoare;};
void genprod(char *nume, long tip, int increment, int nr){if(!fork()){
int item,i; struct mesaj m;
m.tip=tip; item=0;
i=0; do{
item+=increment;
/* item=produce_item() */
m.valoare=item;
/* build_message(&m,item) */
msgsnd(msqid,&m,sizeof(int),0);
/* send(consumer,&m) */
++i;
}while(i<nr);
printf("Producator %s de tip %ld: trimise %d\n",nume,tip,i);
exit(0);
}}
void gencons(char *nume, long tip, int nr){if(!fork()){
int item,i; struct mesaj m;
i=0; do{
msgrcv(msqid,&m,sizeof(int),tip,0); /* receive(producer,&m) */
item=m.valoare;
/* item=extract_item(&m) */
printf("%d\n",item);
/* consume_item(item) */
++i;
}while(i<nr);
printf("Consumator %s de tip %ld: primite %d\n",nume,tip,i);
exit(0);
}}

IPC - cozi de mesaje

int main(){
initializare(); p=getpid();
genprod("A",1,1,2); genprod("B",1,10,3); genprod("C",2,100,5);
gencons("x",1,5); gencons("y",2,1); gencons("z",2,4);
while(wait(NULL)!=-1);
finalizare(0);
return 0;
}

IPC - cozi de mesaje

Comentarii:
- fiecare produc
ator / consumator este lansat ca un proces copil si are un nume
nume, un tip tip al mesajelor pe care le produce / consum
a si un anumit
num
ar nr de mesaje pe care le produce / asteapt
a; dac
a se termin
a normal,
fiecare afisaza c
ate mesaje a produs / primit;
- p
arintele comun asteapt
a terminarea copiilor cu while(wait(NULL)!=-1);
aceasta nu este asteptare ocupat
a deoarece la fiecare iteratie se blocheaz
a n
wait(NULL) p
an
a se (mai) termin
a un copil - n total va cicla de at
atea ori
c
ati copii are, apoi la iteratia urm
atoare, ne mai av
and copii, wait(NULL) i
va returna -1.

IPC - cozi de mesaje


La o rulare putem obtine:
Producator
Producator
Producator
1
2
10
20
30
Consumator
100
Consumator
200
300
400
500
Consumator

A de tip 1: trimise 2
B de tip 1: trimise 3
C de tip 2: trimise 5

x de tip 1: primite 5
y de tip 2: primite 1

z de tip 2: primite 4

Instrumente de comunicare intre procese


Conditii de curs
a
Regiuni critice
Dezactivarea ntreruperilor
Variabile z
avor
Alternarea strict
a
Solutia lui Peterson
Instructiunea TSL
Sleep si Wakeup
Semafoare
Mutex
Monitoare
Transfer de mesaje
Bariere
Probleme clasice ale comunicarii interprocese
Problema filozofilor care mananc
a
Problema cititorilor si scriitorilor
Problema frizerului somnoros
Cazul UNIX/Linux
Semnale
IPC
IPC - segmente de memorie partajat
a
IPC - vectori de semafoare
IPC - cozi de mesaje
Fisiere tub

Cuprins

Fisiere tub

Fisierele tub (conduct


a, pipe) sunt un tip special de fisiere.
Investigarea lor necesit
a cunostinte avansate despre fisiere si va fi facut
a n
cursul despre gestiunea fisierelor.
Mentionam doar c
a, spre deosebire de cozile de mesaje, secventele de informatii
scrise n tuburi nu sunt delimitate logic ca mesajele (tubul retine o simpl
a
succesiune de octeti) iar dac
a mai multe secvente de informatii sunt scrise
simultan de procese diferite, octetii lor se pot amesteca.

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