Documente Academic
Documente Profesional
Documente Cultură
2008
Cuprins
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
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).
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
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.
Cuprins
Dezactivarea ntreruperilor
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.
Cuprins
Variabile zavor
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.
Cuprins
Alternarea stricta
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)
Alternarea stricta
Cuprins
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
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
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.
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.
/* 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);
}
}
/* 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);
}
}
/* 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.
/* 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.
/* 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.
Cuprins
Semafoare
Semafoare
Semafoare
Semafoare
Semafoare
/*
/*
/*
/*
/*
/* procesul producator */
void producer(void){
int item;
while(TRUE){
item=produce_item();
down(&empty);
down(&mutex);
insert_item(item);
up(&mutex);
up(&full);
}
}
Semafoare
Semafoare
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
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.
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
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
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.
Cuprins
Transfer de mesaje
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);
}
}
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.
Cuprins
Cuprins
'$
h
R
R
h
R
h
*h h
Y
*
Y
*
Y
6
&%
6
6
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);
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;
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;
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).
/*
/*
/*
/*
/*
/*
numarul de filozofi */
indexul vecinului stang al lui i */
indexul vecinului drept al lui i */
filozoful gandeste */
filozoful incearca sa ia furculitele
filozoful mananca */
/*
/*
/*
/*
*/
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
Cuprins
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 */
Cuprins
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():
/* date partajate */
#define CHAIRS 5
typedef int semaphore;
semaphore customers=0;
semaphore barbers=0;
semaphore mutex=1;
int waiting=0;
nr. de
nr. de
pentru
nr. de
Cuprins
Cazul UNIX/Linux
Cuprins
Semnale
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
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
Semnale
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
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
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
Semnale
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
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
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
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
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
vf1=11,
vf2=21,
vf2=22,
vf2=23,
vf1=12,
vf2=24,
vglobal=1
vglobal=2
vglobal=3
vglobal=4
vglobal=5
vglobal=6
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.
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
= 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).
Cuprins
= 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;
=
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;
=
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;
=
la esec, apelul returneaz
a -1; errno posibile: EACCES, EEXIST, EINVAL,
ENFILE, ENOENT, ENOMEM, ENOSPC;
=
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.
= 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.
= consult
a/seteaz
a atributele segmentului cu identificatorul intern shmid
(inclusiv poate marca segmentul pentru distrugere);
=
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;
/*
/*
/*
/*
/*
/*
/*
/*
=
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;
};
/*
/*
/*
/*
/*
/*
=
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);
=
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;
=
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;
=
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;
=
Apelul shmctl() returneaz
a 0 la succes si -1 la esec;
errno posibile: EACCES, EFAULT, EIDRM, EINVAL, ENOMEM,
EOVERFLOW, EPERM.
#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);
}
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;
}
Cuprins
= 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.
= aplic
a vectorului de semafoare identificat de semid vectorul de operatii
pointat de sops si av
and lungimea 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;
}
=
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;
}
=
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;
}
=
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;
=
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;
=
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;
=
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 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;
/*
/*
/*
/*
tipul structur
a ipc perm este definit in sys/ipc.h si a fost descris mai
sus, la apelul shmctl();
=
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;
=
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.
*/
*/
*/
*/
*/
*/
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;
}
*/
*/
*/
*/
*/
*/
Cuprins
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.
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.
=
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;
=
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();
=
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;
=
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.
<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;
<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;
<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;
<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;
<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.
=
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;
=
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;
/*
/*
};
tipul structur
a ipc perm este definit n sys/ipc.h si a fost descris mai
sus, la apelul shmctl();
=
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;
#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.
item=produce_item() */
build_message(&m,item) */
send(consumer,&m) */
receive(producer,&m) */
item=extract_item(&m) */
consume_item(item) */
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;
}
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.
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
Cuprins
Fisiere tub