Sunteți pe pagina 1din 37

10.

COMUNICAII INTER-PROCESE

Mecanismele de comunicare ntre procese permit acestora s schimbe date ntre ele i
s-i sincronizeze execuia. Au fost prezentate deja cteva modaliti de comunicare
ntre procese, cum ar fi pipe-urile (nenumite), pipe-urile numite i semnalele. Pipeurile (nenumite) au dezavantajul c sunt cunoscute numai de ctre procesele care
sunt fii ai procesului care a invocat apelul sistem pipe. Procesele nenrudite (ntre care
nu exista o relaie de tip tat-fiu-nepot-...) nu pot comunica prin pipe-uri. Dei pipeurile numite permit comunicarea ntre procese nenrudite, ele nu reprezint totui o
modalitate de comunicare n reea (vezi Cap.13) i nici nu pot fi uor adaptate pentru
folosirea cilor multiple de comunicare ntre grupuri diferite de procese care comunic:
este imposibil multiplexarea unui pipe numit pentru a pune la dispoziie canale
separate perechilor de procese care comunic. De asemenea, procesele oarecare mai
pot comunica i prin trimiterea de semnale unul altuia, cu ajutorul apelului sistem kill,
dar n acest caz mesajul const doar ntr-un numr asociat semnalului.

n acest capitol se descriu alte forme de comunicare ntre procese. Capitolul ncepe
prin studierea execuiei pas cu pas a proceselor, nelegnd prin aceasta faptul c un
proces urmrete i controleaz execuia pas cu pas a altui proces, iar apoi continu
cu explicarea modalitilor de comunicare ntre procese, modaliti specifice variantei
System V i cunoscute sub numele System V IPC: comunicarea prin mesaje, prin
memorie comun (partajat) i prin semafoare. Se face o trecere n revist a
metodelor obinuite prin care se realizeaz comunicarea ntre procese de pe maini
diferite prin intermediul unei reele i, n final, se face o prezentare la nivel utilizator a
socket-urilor BSD. Nu se iau n discuie probleme specifice reelelor, cum ar fi
protocoale i adrese, acestea nefcnd obiectul de studiu al crii.

10.1.

Execuia pas cu pas a proceselor

Sistemul de operare UNIX pune la dispoziie o modalitate rudimentar de


comunicare ntre procese n scopul executrii pas cu pas a acestora, facilitate util in
depanarea programelor. Un proces depanator (debugger process), cum ar fi sdb,
realizeaz executarea pas cu pas a altui proces i controleaz execuia acestuia cu
ajutorul apelului sistem ptrace, stabilind i anulnd puncte de ntrerupere
(breakpoints) i scriind (citind) date n (din) spaiul su virtual de adrese. n
consecin, execuia pas cu pas a unui proces const n sincronizarea execuiei
procesului .depanator cu cea a procesului studiat i n controlul execuiei celui de-al
doilea.

248

if((pid=fork())==0)
{
/* proces fiu=procesul studiat */
ptrace(0,0,0,0);
exec(numele procesului studiat);
}
/* aici continua procesul tat=proccesul depanator */
for(;;)
{
wait((int *)0);
read(intrri pentru instruciuni de execuie pas cu pas);
ptrace(cmd,pid,...);
if(condiie de ieire din execuia pas cu pas)
break;
}
Figura 10.1. Structura unui proces de depanare.
Pseudocodul din figura 3.1. pune in eviden o structur caracteristic pentru un
program de depanare. Procesul .depanator creaz un proces fiu, care invoc apelul
sistem ptrace al crui rezultat este poziionarea pe 1 a bitului de execuie pas cu pas
n intrarea corespunztoare procesului fiu, din tabela de procese (Process Table).
Procesul fiu execut, prin apelul sistem exec, programul care urmeaz a fi .depanat.
De exemplu, dac utilizatorul dorete s depaneze programul a.out , atunci
argumentul transmis apelului sistem exec va fi irul de caractere a.out i procesul fiu
va executa acest program. Nucleul va executa apelul sistem exec ca de obicei, dar la
sfrit va observa faptul c bitul de execuie pas cu pas este poziionat pe 1 i, n
consecin va trimite procesului fiu un semnal .trap. La ntoarcerea din apelul sistem
exec, nucleul verific dac s-au trimis semnale -aa cum o face, de altfel, la
ntoarcerea din orice alt apel sistem- gsete semnalul trap pe care, chiar el, tocmai
l-a trimis i execut pas cu pas codul procesului fiu, ntocmai ca pe o secven
special de tratare a semnalelor. Observnd poziionarea pe 1 a bitului de execuie pas
cu pas din propria intrare n tabela de procese, procesul fiu trezete procesul printe
din starea de ateptare instituit n urma apelului sistem wait (dup cum se va vedea
mai trziu), intr ntr-o stare special (stare trace-de execuie pas cu pas, care nu
este evideniat n diagrama strilor proceselor) asemntoare strii sleep i face o
comutare de context.
n mod obinuit, procesul printe (depanatorul) va fi intrat deja intr-un ciclu,
ateptnd s fie trezit din starea de sleep (instituit de apelul sistem wait ) de ctre
procesul fiu (adic procesul executat pas cu pas). Cnd procesul fiu trezete procesul
printe, acesta ncheie execuia apelului sistem wait, citete comenzile de la intrare ale
utilizatorului prin apelul read i le convertete intr-o serie de apeluri ptrace n scopul
controlrii execuiei procesului fiu.
Sintaxa apelului sistem ptrace este urmtoarea:

ptrace( cmd, pid, addr, data);

-unde cmd specific diverse comenzi cum ar fi citirea sau scrierea datelor, ori reluarea
execuiei i alte comenzi, pid reprezint identificatorul de proces al procesului care se
execut pas cu pas, addr reprezint adresa virtual, din cadrul procesului fiu, la (de la)
care se va scrie (citi) i data reprezint o valoare de tip ntreg care se va scrie. La
249

executarea apelului sistem ptrace, nucleul verific dac procesul depanator are
vreun proces fiu al crui identificator este egal cu valoarea parametrului pid i dac
acest proces fiu se afl n starea trace, utiliznd apoi o structur de date global,
dedicat execuiei pas cu pas, pentru realizarea transferului de date ntre cele dou
procese. Nucleul blocheaz accesul la structura de date global amintit, pentru a
mpiedica scrierea de ctre alte procese aflate n execuie pas cu pas, copiaz valorile
parametrilor cmd, addr i data n structura de date, trezete procesul fiu i-l trece n
starea ready-to-run, punndu-se apoi n ateptare pn la sosirea rspunsului de la
procesul fiu. Cnd procesul fiu i reia execuia (n modul nucleu), el execut comanda
prevzut n parametrul cmd, scrie rezultatul executrii comenzii n structura de date
global dedicat execuiei pas cu pas i apoi trezete procesul depanator. n funcie
de comanda precizat prin parametrul cmd, procesul fiu poate s reintre n starea
trace ateptnd o nou comand, sau poate ncheia tratarea semnalului relundu-i
execuia. Cnd procesul depanator i reia execuia, nucleul memoreaz valoarea de
retur furnizat de procesul executat pas cu pas, deblocheaz accesul la structura de
date global corespunztoare i red controlul utilizatorului.
Dac, n momentul intrrii procesului fiu n starea trace, procesul depanator nu se
afl n starea sleep (ca efect al apelului sistem wait), el nu va putea determina care
este procesul fiu ce se va executa pas cu pas, dect n momentul invocrii apelului
sistem wait, moment n care se va ntoarce imediat i va proceda aa cum tocmai a
fost descris mai sus.

int data[32];
main()
{
int I;
for(i=0;i<32;i++)
printf(data[%d]=%d\n,i,data[i]);
printf(ptrace data addr 0x%x\n,data);
}
Figura 10.2. Programul trace -- un proces executat pas cu pas

S lum n consideraie cele dou programe, denumite trace i debug, prezentate n


figurile 10.2 i respectiv 10.3. Rulnd programul trace la terminal, valorile vectorului
data vor fi egale cu zero; procesul afieaz adresa vectorului data i i ncheie
execuia. Rulnd acum programul debug , avnd ca parametru o valoare egal cu cea
afiat de ctre programul trace, acesta (programul debug) salveaz valoarea
parametrului ce i s-a transmis n variabila addr , creaz un proces fiu care invoc
apelul sistem ptrace pentru a putea fi el nsui executat pas cu pas i execut
programul trace. La sfritul execuiei apelului sistem exec, nucleul trimite procesului
fiu (s-l numim procesul trace) un semnal SIGTRAP, iar procesul trace intr n starea
trace (n curs de execuie pas cu pas), ateptnd comenzi de la procesul debug.
Dac procesul debug se afla n ateptare (n starea wait), acesta se trezete, gsete
procesul fiu executat pas cu pas i prsete starea wait. Apoi, procesul debug invoc
apelul sistem ptrace, scrie valoarea variabilei de ciclare i n spaiul de adrese al
procesului (fiu) trace i incrementeaz valoarea din variabila addr ; n cadrul procesului
trace, variabila addr reprezint adresa unei intrri din vectorul data. Ultima invocare a
apelului ptrace de ctre procesul (printe) debug are ca efect execuia procesului (fiu)
trace , iar n acest caz vectorul data conine valorile de la 0 la 31. Un program
depanator cum ar fi sdb are acces la tabela de simboluri a procesului executat pas cu
250

pas, din coninutul creia determin adresele utilizate ca parametri pentru apelurile
procedurii ptrace.
Utilizarea apelului sistem ptrace n scopul executrii pas cu pas a proceselor, este o
metod rudimentar care prezint mai multe dezavantaje.
Nucleul trebuie s execute patru comutri de context, pentru a realiza transferul unui
octet de date ntre procesul depanator i procesul executat pas cu pas, astfel:-o
comutare de context n cadrul procesului depanator la invocarea apelului sistem
ptrace, pn cnd procesul executat pas cu pas trimite un rspuns;-dou comutri de
context la intrarea n -i la ieirea din- procesul executat pas cu pas; -o comutare de
context napoi ctre procesul depanator mpreun cu rspunsul ctre apelul ptrace.
Aceast penalizare este necesar, ntruct un proces depanator nu are alt
modalitate de a accesa spaiul virtual de adrese al procesului executat pas cu pas, n
consecin execuia pas cu pas este consumatoare de timp.
Un proces depanator poate executa pas cu pas mai multe procese fii simultan, cu
toate c, n practic, aceast facilitate este rareori exploatat. Un aspect mai critic l
reprezint, ns, faptul c un proces depanator nu poate executa pas cu pas dect
procese fii, adic: -dac un proces fiu, executat pas cu pas, invoc apelul sistem fork
(adic i creaz propriul proces fiu), atunci procesul depanator nu are nici un control
asupra procesului nepot, ceea ce reprezint o restricie major n cazul depanrii
programelor complexe. Dac un proces executat pas cu pas invoc apelul sistem exec,
depanarea se continu i la nivelul imaginilor -ulterioare apelului exec - ale procesului
depanat, din cauza apelului ptrace primar, depanatorul neputnd, ns, s tie
numele imaginii procesului care a rezultat n urma apelului exec, ceea ce face dificil
depanarea simbolic.
Un proces depanator nu poate executa pas cu pas un alt proces care se afl deja n
execuie, dac acesta din urm nu a invocat, n prealabil, apelul sistem ptrace , pentru
a preveni nucleul asupra faptului c permite execuia sa, pas cu pas. Acest lucru
este, de asemenea, un dezavantaj, deoarece pentru a executa pas cu pas un anumit
proces aflat deja n execuie, atunci acesta trebuie n prealabil terminat (cu apelul
kill) i relansat n execuie, n modul trace.
Nu este posibil execuia pas cu pas a programelor care au poziionat pe 1 bitul
setuid, deoarece utilizatorii ar putea nclca drepturile de acces, avnd posibilitatea de
a scrie n spaiul virtual de adrese al acestor programe, cu ajutorul apelului sistem
ptrace, i de a executa operaiuni nepermise prin drepturile de acces. De exemplu, s
presupunem c un astfel de program, invoc apelul exec avnd ca parametru numele
de fiier privatefile. Un utilizator iste s-ar putea folosi de apelul sistem ptrace pentru
a modifica parametrul respectiv la valoarea /bin/sh, lansnd astfel n execuie shell-ul
(cu toate programele asociate acestuia) fr a avea permisiunea de a o face. Apelul
exec ignor valoarea bitului setuid dac procesul este executat pas cu pas, pentru a
evita scrierea de ctre un utilizator n cadrul spaiului de adrese a unui astfel de
program.

Killian [Killian 84] descrie o alt modalitate de execuie pas cu pas a proceselor, bazat
pe lucrul cu sistemele de fiiere, descris n capitolul .... . Administratorul de sistem
monteaz un sistem de fiiere, /proc; -utilizatorii identific procesele pe baza PID-ului
acestora i le trateaz ca fiind fiiere din /proc. Nucleul acord permisiunea de
deschidere a fiierelor (cu apelul sistem open), n conformitate cu identificatorii UID i
GID. Utilizatorii au posibilitatea de a accesa spaiul de adrese pentru citire (prin
apelul read) i de a stabili puncte de ntrerupere (breakpoints) prin scriere n fiier (cu
apelul write). Apelul stat furnizeaz diverse informaii de ordin statistic despre
procese. Aceast metod nltur trei dintre dezavantajele utilizrii apelului ptrace.
251

Mai nti, aceast metod este mai rapid deoarece un proces depanator poate
transfera o cantitate mai mare de date la un apel sistem, n comparaie cu apelul
ptrace. n al doilea rnd, un proces depanator poate executa pas cu pas orice
proces, care nu trebuie s fie neaprat un proces fiu al su. n sfrit, un proces
depanat nu necesit msuri prealabile pentru a permite execuia sa pas cu pas; -un
proces depanator poate executa pas cu pas procese deja n curs de execuie. Ca
parte a mecanismului obinuit de protecie a fiierelor, apare faptul c doar superuserul poate executa pas cu pas procesele care au setat bitul setuid pentru fiierele din
rdcin.

10.2.

Comunicarea ntre procese prin pachetul IPC

Pachetul System V IPC din UNIX se compune din trei mecanisme. Mesajele permit
proceselor s trimit fluxuri de date formatate ctre alte procese, memoria comun
permite proceselor s foloseasc n comun pri din spaiul lor virtual de adrese, iar
semafoarele permit proceselor s-i sincronizeze execuia. Implementate n mod
unitar, aceste mecanisme au o serie de proprieti comune:
Fiecare mecanism conine o tabel ale crei intrri descriu toate instanele acelui
mecanism.
Fiecare intrare din tabel conine o cheie numeric (key), care reprezint numele su
desemnat de utilizator.
Fiecare mecanism dispune de un apel sistem de tip get destinat crerii unei noi
intrri, sau obinerii uneia deja existente, parametrii acestor apeluri incluznd o cheie
(key) i un indicator (flag). Nucleul caut n tabela corespunztoare intrarea
desemnat de ctre cheie. Procesele pot invoca apelurile de tip get cu parametrul
cheie avnd valoarea IPC_PRIVATE, pentru a se asigura de obinerea unei intrri
nefolosite. Procesele pot poziiona valoarea bitului IPC_CREAT din cadrul indicatorului
(flag-ului) pentru a crea o nou intrare, dac cea precizat prin cheie nu exist deja, i
de asemenea pot determina semnalizarea erorilor prin poziionarea indicatorilor
IPC_EXCL i IPC_CREAT, pentru situaia n care deja exist o intrare desemnat prin
acea cheie. Apelurile sistem de tip getreturneaz un descriptor determinat de nucleu,
care va fi ulterior utilizat ca parametru al altor apeluri sistem, rezultnd de aici
analogia cu apelurile sistem creat i open.
n cadrul fiecrui mecanism IPC (de comunicare ntre procese), pentru determinarea
indexului din tabela structurilor de date, nucleul utilizeaz urmtoarea formul bazat
pe valoarea descriptorului :

index = descriptor modulo (numrul de intrri din tabel)

De exemplu, dac tabela structurilor de date pentru mesaje conine 100 de intrri,
atunci descriptorii pentru intrarea 1 sunt 1, 101, 201 .a.m.d. Cnd un proces
elibereaz o intrare, nucleul crete valoarea descriptorului asociat acesteia cu o
valoare egal cu numrul de intrri din tabel. Noua valoare, astfel obinut, devine
252

noul descriptor al intrrii respective, n cazul unei noi alocri a acesteia printr-un apel
de tip get. Orice ncercare ulterioar a proceselor de a accesa intrarea respectiv, pe
baza vechiului descriptor, se va solda cu eroare. n legtur cu exemplul de mai sus,
dac descriptorul asociat intrrii 1 din tabela pentru mesaje este 201, atunci cnd
intrarea respectiv este eliberat, nucleul i atribuie acesteia un nou descriptor, 301.
Procesele care ncearc s acceseze intrarea, pe baza vechiului descriptor (201), vor
primi un mesaj de eroare, deoarece acest descriptor nu mai este valabil. Nucleul
recicleaz eventual nnumerele pentru descriptori, probabil dup o lung perioad de
timp.
Fiecare intrare IPC dispune de o structur de date pentru stocarea permisiunilor,
aceasta incluznd indicatorul de utilizator i indicatorul de grup ale procesului care a
creat intrarea, un indicator de utilizator i unul de grup fixai printr-un apel sistem de
tip control i un set de permisiuni de citire-scriere-execuie (read-write-execute)
separat pentru utilizator, grup i ceilali (user, group, others), similare permisiunilor de
acces la fiiere.
Fiecare intrare conine i alte informaii de stare, cum ar fi PID-ul ultimului proces
care a actualizat intrarea (a trimis ori a primit un mesaj, a ataat o zon comun de
memorie .a.m.d.), precum i momentul ultimului acces sau al ultimei actualizri a
intrrii.
Fiecare mecanism dispune de un apel sistem de tip control prin care se primesc
informaii despre starea unei intrri, se seteaz starea unei intrri, sau se elibereaz o
intrare. Cnd un proces solicit informaii despre starea unei intrri, nucleul verific
dac acesta are drept de citire i apoi copiaz datele din intrare la adresa specificat
de utilizator. n mod similar, pentru modificarea valorilor dintr-o intrare, nucleul
verific dac UID-ul procesului corespunde cu UID-ul utilizatorului, sau cu UID-ul celui
care a creat intrarea, sau dac procesul n cauz este executat de ctre superuser;
dreptul de scriere nu este suficient pentru a se permite modificarea parametrilor unei
intrri. Nucleul copiaz datele precizate de ctre utilizator n intrarea din tabel,
setnd valorile UID-ului, GID-ului, permisiunile de acces, precum i coninutul altor
cmpuri, n funcie de tipul mecanismului de comunicare. Nucleul nu modific valorile
UID-ului i GID-ului aparinnd celui care a creat intrarea, astfel nct utilizatorul care
a creat intrarea respectiv i pstreaz drepturile de control asupra acesteia. n
sfrit, un utilizator poate elibera o intrare din tabel, dac este superuser, sau dac
PID-ul procesului su corespunde celui din structura de date a intrrii. Nucleul crete
numrul de descriptor asociat intrrii, astfel nct, la urmtoarea alocare a intrrii
respective, va returna un alt descriptor. Din acest motiv, aa cum s-a explicat mai sus,
apelurile sistem vor ntoarce un mesaj de eroare, dac un proces va ncerca s
acceseze o intrare utiliznd unul din vechii descriptori.

10.2.1. Comunicarea ntre procese prin mesaje

Exist patru apeluri sistem pentru comunicarea prin mesaje: msgget ntoarce (sau
creaz) un descriptor de mesaje, care desemneaz o coad de mesaje, utilizat ca
parametru n celelalte apeluri sistem; msgctl dispune de opiuni de setare sau
returnare a valorilor parametrilor asociai unui descriptor de mesaje, precum i de o
opiune de eliberare a descriptorilor; msgsnd trimite un mesaj, iar msgrcv
recepioneaz un mesaj.

253

Sintaxa apelului sistem msgget este urmtoarea:

msgqid = msgget(key, flag);

unde msgqid este descriptorul returnat de apelul sistem, iar key i flag au
semnificaiile descrise mai sus, n cazul general al apelurilor de tip get. Nucleul
memoreaz mesajele sub forma unei liste nlnuite (queue - coad de mesaje) creia
i corespunde un descriptor, i folosete un descriptor de coad de mesaje -msgqid- ca
index ntr-un tabel de antete de cozi de mesaje. n plus fa de cmpul de permisiuni
generale de comunicare ntre procese, menionat mai sus, structura de date asociat
unei cozi de mesaje conine urmtoarele cmpuri:

Pointeri ctre primul i ctre ultimul mesaj din lista nlnuit;


Numrul de mesaje i numrul total de octei de date din lista nlnuit;
Numrul maxim de octei de date pe care i poate conine lista nlnuit;
Identificatorii ultimelor procese care au trimis i, respectiv recepionat, mesaje;
Momentele de timp ale ultimelor operaii msgsnd, msgrcv i msgctl.

Atunci cnd un utilizator invoc apelul sistem msgget pentru a crea un nou descriptor,
nucleul caut n tabela de cozi de mesaje pentru a vedea dac exist vreuna care s
corespund specificaiei dat prin cheie. Dac nu exist nici o intrare care s
corespund cheii, nucleul aloc o nou structur de date de tip coad de mesaje, o
iniializeaz i returneaz utilizatorului un identificator pentru aceast nou structur.
Dac exist deja o astfel de coad de mesaje, atunci nucleul verific drepturile de
acces i revine din apelul sistem.
Procesele folosesc apelul sistem msgsnd pentru a trimite un mesaj, apelul avnd
urmtoarea sintax:

msgsnd(msgqid, msg, count, flag);

-unde msgqid este descriptorul cozii de mesaje, returnat n mod obinuit de apelul
sistem msgget, msg este un pointer ctre o structur de date -controlat de ctre
utilizator- compus din tipul mesajului (care poate lua valori ntregi) i un ir de
caractere, count specific dimensiunea n octei a irului de caractere msg, iar flag
precizeaz aciunea pe care urmeaz s o ntreprind nucleul, n situaia n care nu
mai are la dispoziie suficient spaiu n buffer-ele de memorie.

254

algoritmul msgsnd
/* trimiterea unui mesaj */
intrri: (1)descriptorul cozii de mesaje
(2)adresa structurii de date pentru mesaje
(3)lungimea mesajului
(4)indicatori /* flags */
ieiri: numrul de octeti efectiv trimii
{
verifica valabilitatea descriptorului i a drepturilor de acces;
while(nu exist spaiu de memorie suficient pt. stocarea mesajului)
{
if(flag-ul specific faptul c procesul nu ateapt)
return;
sleep(eveniment: exist spaiu suficient pt. stocarea mesajului)
}
aloca antet de mesaj;
copiaz mesajul propriu-zis din spaiul user in spaiul nucleu;
actualizeaz structura de date:
-insereaz n list noul antet de mesaj;
-iniializeaz pointerul din antetul de mesaj ctre zona de date;
-nscrie valoarea:-contorului de octei;
-momentelor de timp;
-identificatorului procesului transmitor;
trezete toate procesele care ateapt s citeasc mesaje din coad;
}
Figura 10.4. Algoritmul pentru apelul sistem Msgsnd

n Figura 10.4. nucleul verific dac procesul care trimite un mesaj are drept de
scriere pentru descriptorul respectiv, dac lungimea mesajului nu depete limitele
sistemului, dac lista de mesaje nu conine prea muli octei i dac tipul mesajului are
o valoare ntreag i pozitiv. Dac toate condiiile sunt ndeplinite, nucleul aloc
spaiu pentru mesaj din spaiul liber pentru mesaje, gestionat de ctre o tabel (map)
similar celei care gestioneaz spaiul liber de pe dispozitivul de swap (revedei
seciunea ... .1) i copiaz acolo datele din spaiul utilizatorului. Nucleul aloc un antet
de mesaj pe care-l plaseaz la sfritul listei nlnuite de mesaje, corespunztoare
cozii respective. n antet se nscriu tipul i dimensiunea mesajului, se iniializeaz
antetul pentru a referi irul de caractere ce constituie mesajul propriu-zis, iar n
antetul cozii de mesaje se actualizeaz cmpurile ce conin informaii de ordin statistic
(numrul de mesaje i de octei de date din coada de mesaje, momentele de timp i
identificatorul procesului care a trimis mesajul). Apoi, nucleul trezete procesele care
ateptau sosirea unui mesaj n coada de mesaje. Dac numrul de octei depete
limita cozii de mesaje, procesul care trimite mesajul este trecut n starea sleep, pn la
eliberarea spaiului din coada de mesaje, prin trimiterea altor mesaje deja existente
n coad. Dac procesul a semnalizat nucleului s nu-l treac n ateptare (cu ajutorul
flag-ului IPC_NOWAIT), atunci el revine imediat din apelul sistem, cu eroare. Figura
10.5 prezint mesajele dintr-o coad, evideniind antetele de mesaje, listele nlnuite
de antete de mesaje i pointerii din antetele de mesaje ctre zona de date.

255

Figura 10.5. Structurile de date pentru comunicarea prin mesaje


S considerm programul din Figura 10.6.: un proces invoc apelul sistem msgget
pentru a obine un descriptor pentru MSGKEY. Procesul stabilete o lungime a
mesajului de 256 de octei, dei folosete doar primul ntreg, i copiaz propriul
identificator de proces n zona de text a mesajului, atribuie valoarea 1 pentru tipul
mesajului, apoi invoc apelul sistem msgsnd pentru a trimite mesajul. Vom reveni mai
trziu la acest exemplu.
Procesele primesc mesaje cu ajutorul apelului sistem msgrcv, care are urmtoarea
sintax:

count = msgrcv(id, msg, maxcount, type, flag);

unde id este descriptorul de mesaj, msg este adresa unei structuri de date a
utilizatorului n care se va scrie mesajul primit, maxcount este dimensiunea irului de
caractere din structura de date msg, type specific tipul de mesaj pe care utilizatorul
dorete s-l citeasc, iar flag precizeaz ce trebuie s fac nucleul dac nu exist
mesaje n coada de mesaje. Valoarea ntoars de apel, count, reprezint numrul de
octei de date efectiv primii de ctre utilizator.
n Figura 10.7. nucleul verific dac utilizatorul are drepturile necesare de acces la
coada de mesaje. Dac mesajul cerut de utilizator are valoarea din cmpul type egal
cu 0, nucleul va localiza primul mesaj din lista nlnuit. Dac lungimea mesajului
este mai mic sau egal cu dimensiunea cerut de utilizator, atunci nucleul copiaz
mesajul propriu-zis n structura de date a utilizatorului i actualizeaz n mod
corespunztor structurile de date ale cozii de mesaje: -scade valoarea contorului de
mesaje aflate n list i numrul de octei de date din coada de mesaje, nscrie
momentul recepionrii mesajului i identificatorul procesului care a recepionat
mesajul, reface lista nlnuit ca urmare a eliminrii mesajului i elibereaz spaiul n
care a fost memorat mesajul. Dac exist procese care doresc s trimit mesaje i
sunt n ateptare ca urmare a faptului c nu a existat spaiu suficient n lista de
mesaje, atunci nucleul le trezete. Dac lungimea mesajului este mai mare dect
256

parametrul maxcount precizat de utilizator, atunci nucleul ntoarce eroare la revenirea


din apelul sistem i las mesajul n list. Dac procesul ignor restriciile de
dimensiune a mesajelor (prin poziionarea bitului MSG_NOERROR din cadrul flag-ului),
atunci nucleul trunchiaz mesajul, ntoarce numrul de octei cerut i extrage ntregul
mesaj din list.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define MSGKEY

75 /*cheia mesajului=index n tabela de mesaje */

struct msgform
/* descrierea formei mesajului: */
{ long mtype;
/* -tipul mesajului; */
char mtext[256]; /* -coninutul mesajului; */
};
main()
{
struct msgform msg;
int msgid, pid, *pint;
msgid=msgget(MSGKEY, 0777);/* obinerea identificatorului asociat */
/* mesajului cu cheia MSGKEY
*/
pid=getpid();
pint=(int *) msg.text; /* se copiaz pid-ul ca text al mesajului */
msg.type=1;
msgsnd(msgid, &msg, sizeof(int), 0);
msgrcv(msgid, &msg, 256, pid, 0); /* pid-ul este folosit drept */
/* tip al mesajului
*/
printf(client: recepioneaz de la pid %d\n, *pint);
}
Figura 10.6. Un proces client
Prin stabilirea corespunztoare a valorii parametrului type, un proces poate recepiona
mesaje de un anumit tip. Dac valoarea este un ntreg pozitiv, atunci nucleul ntoarce
primul mesaj de tipul respectiv. Dac valoarea este un ntreg negativ, atunci nucleul
caut tipul cel mai mic ca valoare dintre toate mesajele din coada de mesaje,
presupus a fi mai mic sau egal n valoare absolut dect valoarea type, i ntoarce
primul mesaj de acest tip. De exemplu, dac o coad conine trei mesaje, ale cror
tipuri sunt 3, 2 i respectiv 1, i dac un utilizator cere un mesaj cu valoarea pentru
tip -2, atunci nucleul ntoarce mesajul care are tipul 1, din cadrul cozii de mesaje. n
toate cazurile, dac nici un mesaj din coada de mesaje nu satisface condiia de tip din
cererea de recepionare, nucleul trece procesul n starea de sleep, dac acesta nu a
specificat revenirea imediat din contextul nucleu prin poziionarea bitului
IPC_NOWAIT din cadrul flag-ului.

257

Algoritmul msgrcv /* recepionare mesaj */


intrri: (1)descriptorul de mesaj
(2)adresa vectorului de date pentru mesajele care sosesc
(3)dimensiunea vectorului de date
(4)tipul de mesaj cerut
(5)indicatori (flag-uri)
ieiri: numrul de octei din mesajul returnat
{
verific permisiunile;
ciclu:
verific validitatea descriptorului de mesaj;
/* gsete mesajul care va fi returnat ctre utilizator */
if (tipul de mesaj cerut==0)
se consider primul mesaj din coada de mesaje;
else
if (tipul de mesaj cerut > 0)
se consider primul mesaj care are tipul cerut, din coada de
mesaje;
else /* valoarea tipului mesajului este negativ */
se consider primul mesaj dintre cele care au valoarea
tipului cea mai mic din coada de mesaje, astfel incit
aceast valoare s fie mai mic sau egal, in modul, cu
valoarea tipului cerut;
if (exist un mesaj)
{
corecteaz dimensiunea mesajului sau returneaz eroare dac
dimensiunea dat de utilizator este prea mic;
copiaz tipul i textul mesajului din spaiul nucleu n spatiul
user;
terge mesajul din coada de mesaje;
return;
}
/* nu exist nici un mesaj */
if (flag-urile precizeaz ca procesul s nu treac n sleep)
return (cu eroare);
sleep(eveniment:sosirea unui mesaj in coada de mesaje);
goto ciclu;
}
Figura 10.7. Algoritmul de recepionare a unui mesaj
S privim programele din figurile 3.6 i 3.8. Programul din figura 10.8 prezint
structura unui server care furnizeaz servicii generale proceselor client. De exemplu,
acesta (server-ul) poate primi, de la procesele client, cereri de furnizare de informaii
dintr-o baz de date; procesul server constituie singurul punct de acces la coninutul
bazei de date, ceea ce face mai uoar rezolvarea problemei consistenei i securitii
datelor. Procesul server creaz o structur de mesaj prin setarea flag-ului, din cadrul
apelului mssget, la valoarea IPC_CREAT i recepioneaz toate mesajele cu tipul 1
(adic cereri de la procesele client). Procesul server citete textul mesajului, caut
identificatorul procesului client i seteaz valoarea tipului mesajului care va fi returnat
la valoarea identificatorului procesului client. n acest exemplu, procesul server i
trimite propriul identificator de proces ctre procesul client n cadrul textului mesajului,
iar procesul client va recepiona numai mesajele al cror tip este egal cu valoarea
identificatorului de proces al serveru-lui. Astfel, procesul server recepioneaz numai
mesajele trimise acestuia de ctre procesele client, iar procesele client recepioneaz
numai mesajele trimise lor de ctre procesul server. Procesele coopereaz pentru a
stabili canale multiple de comunicare pe o singur coad de mesaje.

258

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define MSGKEY 75 /*cheia mesajului=index n tabela de mesaje */
struct msgform
/* descrierea formei mesajului: */
{ long mtype;
/* -tipul mesajului; */
char mtext[256]; /* -coninutul mesajului; */
} msg;
int msgid;
main()
{
int i, pid, *pint;
extern cleanup();
for(i=0; i<20; i++)
signal(i, cleanup());
msgid=msgget(MSGKEY, 0777|IPC_CREAT);
for(;;)
{
msgrcv(msgid, &msg, 256, 1, 0);
pint=(int *) msg.mtext;
pid= *pint;
printf(server: recepioneaz de la pid %d\n, pid);
msg.mtype=pid;
*pint=getpid();
msgsnd(msgid, &msg, sizeof(int), 0);
}
}
cleanup()
{
msgctl(msgid, IPC_RMID, 0);
exit();
}
Figura 10.8. Un proces server

Mesajele sunt formate din perechi tip-date, unde datele din fiier formeaz un ir de
octei. Prefixul type permite proceselor s selecteze, dac se dorete, mesaje de un
anumit tip, facilitate decare nu se putea beneficia cu uurin n cadrul sistemului de
fiiere. Astfel, procesele pot extrage mesaje de un anumit tip din coada de mesaje, n
ordinea n care acestea sosesc, iar nucleul pstreaz ordinea corect. Dei este
posibil implementarea, la nivel utilizator, a unei scheme de comunicare prin fiiere,
comunicarea prin mesaje ofer aplicaiilor o modalitate mai eficient de transfer al
datelor ntre procese.
Un proces poate inspecta starea unui descriptor de mesaj, i poate stabili valoarea i
poate terge un descriptor de mesaj cu ajutorul apelului sistem msgctl. Sintaxa
apelului este

msgctl(id, cmd, mstatbuf)


259

unde id identific descriptorul de mesaj, cmd precizeaz tipul comenzii, iar mstatbuf
reprezint adresa structurii de date utilizator care va conine parametrii de control sau
rezultatele unei interogri. Implementarea apelului sistem este simpl, n anex
precizndu-se n detaliu parametrii.
ntorcndu-ne la exemplul din figura 10.8 cu procesul server, acesta recepioneaz
semnalele i invoc funcia cleanup pentru a terge coada de mesaje din memoria
sistemului. Dac procesul server nu a recepionat semnalele, sau dac primete un
semnal SIGKILL (care nu poate fi tratat), atunci coada de mesaje va rmne n sistem,
chiar dac nici un proces nu o mai refer. ncercrile ulterioare de a crea (n
exclusivitate) o nou coad de mesaje pe baza cheii date, vor eua pn n momentul
n care vechea coad va fi tears.

10.2.2. Comunicarea ntre procese prin zone de memorie partajat

Procesele pot comunica direct unele cu celelalte prin folosirea n comun a unor pri
din spaiul lor virtual de memorie i prin scrierea i citirea datelor memorate n
memoria comun. Apelurile sistem pentru manipularea zonelor de memorie comun
sunt similare apelurilor sistem dedicate comunicaiei prin mesaje. Apelul sistem
shmget creaz o nou zon (regiune) comun de memorie sau ntoarce una existent,
apelul sistem shmat ataeaz la nivel logic o regiune de memorie la spaiul virtual de
adrese al unui proces, apelul sistem shmdt detaeaz o regiune de spaiul virtual de
adrese al unui proces, iar apelul sistem shmctl manipuleaz diveri parametri asociai
zonelor comune de memorie. Procesele scriu n -i citesc date din- memoria comun
utiliznd aceleai instruciuni main pe care le utilizeaz i la scrierea ori citirea din
memoria obinuit. Dup ataarea unei zone de memorie comun, aceasta devine
parte a spaiului virtual de adrese al procesului, accesibil n acelai mod n care sunt
accesibile i celelalte adrese virtuale; nu sunt necesare apeluri sistem pentru scrierea
sau citirea datelor din zona de memorie comun.
Sintaxa apelului sistem shmget este urmtoarea

shmid = shmget(key, size, flag);

unde size reprezint numrul de octei din regiunea de memorie. Nucleul caut cheia
key n tabela cu zonele comune de memorie: dac gsete n aceast tabel o intrare
care s corespund cheii i dac drepturile de acces sunt validate, atunci ntoarce
descriptorul acelei intrri din tabel. Dac nu gsete o intrare creia s-i corespund
cheia key, i dac utilizatorul a dat indicatorului flag valoarea IPC_CREAT, n scopul
crerii unei noi regiuni comune de memorie, atunci nucleul verific dac valoarea
parametrului size se situeaz ntre valorile limit minim i maxim de memorie ale
sistemului i apoi aloc o structur de date pentru o regiune de memorie, utiliznd
algoritmul allocreg. Nucleul memoreaz n tabela cu zonele comune de memorie
(Shared Memory Table) drepturile drepturile de acces, dimensiunea i un pointer ctre
intrarea din tabela cu regiunile de memorie (Region Table) (vezi figura 10.19.) i
stabilete valoarea unui indicator pentru a arta c regiunii nu i este asociat nici o
cantitate (pagin) de memorie. Nucleul aloc regiunii memorie (tabele de pagini
260

.a.m.d.) numai atunci cnd un proces ataeaz regiunea respectiv la spaiul su de


adrese. De asemenea, nucleul stabilete valoarea unui indicator din intrarea n tabela
regiunilor de memorie (Region Table) pentru a indica faptul c regiunea nu trebuie
eliberat atunci cnd ultimul proces care a ataat-o spaiului su de adrese i-a
ncheiat execuia printr-un apel exit. Astfel, datele din zona comun de memorie
rmn intacte dei nici un proces nu mai include zona respectiv n spaiul su virtual
de adrese.

Figura 10.9. Structurile de date implicate n gestiunea zonelor comune de


memorie
Un proces ataeaz o regiune de memorie comun spaiului su virtual de adrese cu
ajutorul apelului sistem shmat, care are urmtoarea sintax:

virtaddr = shmat(id, addr, flags)

unde id, returnat de apelul sistem anterior (shmget), identific regiunea de memorie
comun, addr reprezint adresa virtual unde utilizatorul dorete s ataeze zona
comun de memorie, iar flags specific dac regiunea este accesibil numai pentru
citire (read-only) i dac nucleul s trunchieze adresa specificat de utilizator.
Valoarea ntoars, virtaddr, reprezint adresa virtual la care nucleul a ataat
regiunea, care nu trebuie s fie neaprat egal cu valoarea cerut de ctre proces.
La execuia apelului sistem shmat nucleul verific dac procesul are drepturile
necesare de acces la regiunea de memorie (figura 10.10.). Acesta examineaz adresa
specificat de ctre utilizator: dac este zero, atunci nucleul alege o adres virtual
convenabil.

261

Algoritmul shmat /* ataare zona comuna de memorie */


intrri: (1)descriptorul regiunii comune de memorie
(2)adresa virtual la care s se fac ataarea
(3)indicatori (flags)
ieiri: adresa virtual la care s-a ataat zona comun de memorie
{
verific validitatea descriptorului i a drepturilor de acces;
if (adresa virtual specificat de utilizator 0)
{
trunchiaz adresa virtual conform specificaiilor indicatorilor;
verific validitatea adresei virtuale i a dimensiunii regiunii;
}
else /*utilizatorul dorete ca nucleul s gseasc o adres*/
nucleul alege o adres virtual (daca nu este nici una la
dispozitie, atunci eroare);
ataeaz regiunea la spaiul de adrese al procesului(alg. attachreg)
if (regiunea este ataat pentru prima dat)
aloc tabele de pagini, memorie pentru regiune (alg. Growreg);
return(adresa virtuala la care s-a fcut ataarea);
}
Figura 10.10. Algoritmul de ataare a unei zone comune de memorie
Zona comun de memorie nu trebuie s se ntind peste alte regiuni din cadrul
spaiului virtual de adrese al procesului, deci att amplasarea ct i dimensiunea ei
trebuie alese judicios pentru ca, n cazul unor modificri ulterioare de dimensiune, alte
regiuni s nu se extind peste zona comun de memorie. De exemplu, un proces
poate mri dimensiunea regiunii sale de date prin apelul sistem brk, noua regiune de
date fiind virtual contigu vechii regiuni de date; n consecin nucleul nu trebuie s
plaseze zona de memorie comun n apropierea regiunii de date. Similar, nucleul nu
trebuie s plaseze zona de memorie comun n apropierea vrfului stivei, pentru a
preveni astfel fenomenul de cretere a stivei n interiorul zonei de memorie comun.
De exemplu, dac stiva crete ctre valorile mari ale adreselor, cel mai bun loc de
amplasare a zonei de memorie comun ar putea fi imediat naintea adresei de nceput
a regiunii de stiv.
Nucleul verific dac regiunea de memorie comun ncape n spaiul de adrese al
procesului i o ataeaz, utiliznd algoritmul attachreg. Dac procesul apelant este
primul proces care ataeaz regiunea respectiv, atunci nucleul aloc tabelele
necesare, utiliznd algoritmul growreg, corecteaz valoarea coninut n cmpul last
time attached al intrrii corespunztoare din tabela regiunilor de memorie comun, i
ntoarce adresa virtual la care a ataat regiunea respectiv.
Un proces detaeaz o regiune de memorie comun din spaiul su virtual de adrese
cu ajutorul apelului sistem
shmdt(addr)

unde addr reprezint adresa virtual ntoars de un apel shmat anterior. Dei ar prea
mai logic ca apelul shmdt s primeasc drept parametru un identificator, se
ntrebuineaz totui adresa virtual a regiunii de memorie comun pentru ca procesul
s poat face distincie ntre mai multe instane ale unei regiuni de memorie comun,
care sunt ataate la spaiul virtual de adrese al procesului, i pentru c identificatorul
ar putea s fi fost ters. Nucleul caut regiunea procesului ataat la adresa virtual
262

indicat i o detaeaz din spaiul de adrese al procesului, utiliznd algoritmul


detachreg. Deoarece tabelele cu regiuni de memorie nu conin pointeri napoi ctre
tabela cu regiuni de memorie comune (Shared Memory Table), nucleul caut n
aceast tabel intrarea corespunztoare regiunii i corecteaz valoarea din cmpul
care conine momentul ultimei detari a regiunii.
S considerm programul din figura 10.10. Un proces creaz o regiune de memorie
comun de 128 KO i o ataeaz de dou ori n spaiul su de adrese, la dou adrese
virtuale diferite. Procesul scrie date n prima regiune de memorie comun i citete
date din cea de-a doua.

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define SHMKEY 75
#define K 1024
int shmid;
main()
{
int i,*pint;
char *addr1,*addr2;
extern char *shmat();
extern cleanup();
for(i=0; i<20; i++)
signal(i,cleanup);
shmid=shmget(SHMKEY, 128*K, 0777|IPC_CREAT);
addr1=shmat(shmid, 0, 0);
addr2=shmat(shmid, 0, 0);
printf(addr1 0x%x addr2 0x%x\n, addr1, addr2);
pint=(int *) addr1;
for(i=0; i<256; i++)
*pint++=i;
pint=(int *) addr1;
*pint=256;

pint=(int *) addr2;
for(i=0; i<256; i++)
printf(index %d\tvalue %d\n, i, *pint++);
pause();

cleanup()
{
shmctl(shmid, IPC_RMID, 0);
exit();
}

Figura 10.10. Ataarea unei regiuni de memorie comun de dou ori la


spaiul de adrese al unui proces

263

Figura 10.12. arat un alt proces care ataeaz aceeai regiune (acesta preia numai
64 KO, pentru a ilustra faptul c fiecare proces poate ataa o cantitate diferit de
memorie dintr-o regiune de memorie comun;) procesul respectiv ateapt pn cnd
primul proces scrie o valoare nenul n primul cuvnt al regiunii de memorie comun i
apoi citete coninutul regiunii de memorie comun. Primul proces execut un apel
pause pentru a permite execuia celui de al doilea; cnd primul proces primete un
semnal, atunci el detaeaz regiunea de memorie comun.

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define SHMKEY 75
#define K 1024
int shmid;
main()
{
int i,*pint;
char *addr;
extern char *shmat();
shmid=shmget(SHMKEY, 64*K, 0777);
addr=shmat(shmid, 0, 0);
pint=(int *) addr;
while(*pint==0)
;
for(i=0; i<256; i++)
printf(%d\n,*pint++);
}
Figura 10.12. Folosirea n comun a memoriei de ctre procese
Pentru examinarea strii i stabilirea valorilor parametrilor unei regiuni de memorie
comun, un proces utilizeaz apelul sistem shmctl, care are urmtoarea sintax:

shmctl(id, cmd, shmstatbuf);

unde id identific intrarea corespunztoare din tabela regiunilor de memorie comune,


cmd precizeaz tipul operaiunii, iar shmstatbuf reprezint adresa unei structuri de
date utilizator care conine informaiile de stare a intrrii n tabela regiunilor de
memorie comune, atunci cnd se face interogarea asupra strii sau stabilirea valorilor
de stare. Nucleul trateaz comenzile de interogare asupra strii i de schimbare a
proprietarului i a drepturilor de acces, n mod asemntor implementrii adoptate
pentru comunicarea prin mesaje. Pentru ndeprtarea rea unei regiuni de memorie
comun, nucleul elibereaz intrarea corespunztoare din tabela de regiuni de memorie
comune (Shared Memory Table) i examineaz coninutul intrrii din tebela cu regiuni
de memorie (Region Table): dac nu mai exist nici un proces care s aib ataat
264

regiunea respectiv la spaiul su de adrese, atunci nucleul elibereaz intrarea din


aceast tabel i toate resursele asociate, utiliznd algoritmul freereg. Dac regiunea
mai este nc ataat la vreun proces (adic valoarea contorului de referine este
pozitiv), atunci nucleul terge valoarea indicatorului care semnalizeaz ca regiunea s
nu fie eliberat la detaarea ei de la spaiul de adrese al ultimului proces. Procesele
care utilizeaz regiunea de memorie comun pot continua s o fac, dar nici un alt
proces nu o mai poate ataa. Atunci cnd toate procesele au detaat regiunea
respectiv, nucleul o elibereaz. Acest caz este analog celui de la sistemul de fiier, n
care un proces poate deschide un fiier i poate continua s-l acceseze i dup
executarea apelului sistem unlink.

10.2.3. Comunicarea ntre procese prin semafoare

Semafoarele reprezint una din soluiile la problema sincronizrii proceselor, propus


de E.W. Dijkstra. n continuare vom prezenta cteva probleme generale, de ordin
teoretic, cu privire la semafoare. Un semafor reprezint o variabil de tip ntreg care
indic numrul de activri salvate pentru viitoarea utilizare a unei resurse. Aceast
variabil poate avea valoarea:
-zero, semnificnd faptul c nu s-au salvat activri pentru viitoarea
utilizare a resursei;
resursei;

-n, pozitiv, semnificnd faptul c sunt n ateptare n activri ale

ncercnd s dm o definiie vom spune c un semafor S este un ansamblu format din


variabilele ntregi P(s) i firul de ateptare asociat, f(s). La crearea contorului de
resurse, e(s), firul de ateptare f(s) este vid. Pentru a aciona asupra semafoarelor,
Dijkstra propune dou primitive, P i V, care implementeaz generic dou aciuni: activare i ateptare.
Astfel, procedura P(s) este executat de un proces oarecare, r, pentru ocuparea unei
resurse i are urmtorul pseudocod:

procedura P(s);
{
e(s)=e(s)-1; /* se micoreaz contorul de resurse disponibile; */
if(e(s)<0) /* nu sunt resurse la dispoziie, atunci *
stare(r)=blocat; /* pune procesul r n firul de ateptare ; */
}

265

Cu privire la numrul e(s), facem observaia c, n funcie de valoarea pe care o are la


un moment dat, el are urmtoarele interpretri: -dac e(s) > 0 atunci el indic
numrul de resurse disponibile;
cereri de resurse nesatisfcute;

-daca e(s) < 0 atunci el indic numrul de

Procedura V(s) este executat de un proces oarecare, q, pentru eliberarea unei


resurse i are urmtorul pseudocod:

procedura V(s);
{
e(s)=e(s)+1; /* se mrete contorul de resurse disponibile; */
if(e(s)<0) /* mai snt cereri de resurse nesatisfcute */
stare(q)=activ; /* scoate procesul q din firul de ateptare ; */
}

Se pot enumera cteva proprieti ale semafoarelor:


1.-un semafor nu poate fi iniializat la valori negative, dar poate deveni negativ dup
un numr de operaii de tip P ;
2.-dac notm cu : -nP(s)-numrul de operaii P asupra lui S;
-nV(s)-numrul de operaii V asupra lui S;
-eo(s) -valoarea iniial a lui S;
-atunci exist relaia: e(s) = eo(s) - nP( s) + nV(s);
3. -fie nf(s) numrul de procese care, dup ce au fcut P(s), fie nu au fost
blocate, fie au fost blocate, dar au fost ulterior deblocate cu V(s).
Atunci avem relaia: nf(s) nP(s);
4. -efectul primitivelor P i V asupra lui nf(s) este urmtorul:
- efectul primitivei P(s):

nP(s) := nP(s) + 1;
dac nP(s) eo(s) + nV(s)
atunci nf(s) = nf(s) - 1;

- efectul primitivei V(s):

nV(s) := nV(s) +1;


dac nP(s) eo(s) + nV(s)
atunci nf(s) = nf(s) + 1;

266

5. -execuia primitivelor P i V las invariant relaia: nf(s) = min( nP(s),


eo(s) + nV(s)) ;
- observaie: - relaia de mai sus spune c numrul de procese din firul de
ateptare este cel mai
disponibile.

mic dintre numrul de cereri de resurse i numrul de resurse

n continuare se va prezenta problema productor-consumator (reader-writer), n


care este vorba despre un set de N buffere. Procesele productor completeaz cte un
buffer cu informaii, n timp ce procesul consumator folosete aceste informaii golind
bufferele. Pentru implementarea celor dou proceduri se folosesc trei semafoare:
plin -pentru numrul de buffere din set ocupate (iniial, plin=0);
gol -pentru numrul de buffere din set libere (iniial, gol=N);
mutex -pentru protejarea accesului n setul de buffere (iniial, mutex=1). Acesta este
un semafor liber.

procedura PRODUCTOR();
{
while(TRUE) /* ciclu infinit; */
{
PRODUCE_BUFFER(buf);/*genereaz informaii pt. a fi scrise n buffer;*/
P(gol); /* cere acces n setul de buffere;*/
P(mutex); /* intrare n regiunea critic; */
SCRIE_BUFFER(buf);/* scrie informaia generat n bufferul pus la */
/* dispoziie; */
V(mutex); /* prsirea regiunii critice; */
V(plin); /* activarea unui proces aflat n ateptare; */
}
}

procedura CONSUMATOR();
{
while(TRUE) /* ciclu infinit; */
{
267

P(plin); /* decrementeaz contorul plin; */


P(mutex); /* intrare in regiunea critic; */
SCOATE_BUFFER(buf);/* ia un buffer din set; */
V(mutex); /* prsirea regiunii critice; */
V(gol);

/* incrementeaz contorul gol; */

CONSUMA_BUFFER(buf); /* folosete informaia; */


}
}

Pe lng aspectele pozitive ale soluiei se pot semnala i unele limite ale metodei:
-n sistemele mari este dificil folosirea semafoarelor;
-exist posibilitatea ca, din cauza unor erori de programare, s se sar n mod
accidental peste executarea lui P, ceea ce duce la accesul neprotejat la regiunile
critice;
-n mod analog, dac se sare peste execuia lui V, se poate ajunge la fenomenul de
blocare;
-n procesul de compilare nu se poate verifica utilizarea corect a semafoarelor;

Apelurile sistem pentru lucrul cu semafoare permit proceselor s-i sincronizeze


execuia prin efectuarea unor operaii atomice asupra unei mulimi de semafoare.
nainte de implementarea semafoarelor, dac un proces dorea blocarea accesului la o
resurs, atunci trebuia s creeze un fiier de blocare cu ajutorul apelului sistem creat.
Apelul creat se ncheie cu eroare dac un asemenea fiier exist deja, iar procesul
presupune c un altul a blocat accesul la resursa respectiv. Principalele dezavantaje
ale acestei abordri sunt faptul c procesele nu tiu cnd s ncerce din nou blocarea
accesului la resurs i faptul c fiierele de blocare pot rmne neterse atunci cnd
sistemul iese n mod accidental din funciune, la urmtoarea iniializare a sistemelor
ele continund s blocheze accesul.
Dijkstra a publicat algoritmul Dekker care descrie o implementare a semafoarelor, care
dup cum am mai precizat sunt variabile de tip ntreg pe care sunt definite dou
operaii elementare (atomice): P i V. Operaia P decrementeaz valoarea semaforului
dac aceasta este pozitiv, iar operaia V incrementeaz valoarea semaforului.
Deoarece ambele operaii sunt elementare, n orice moment cel mult o singur
operaie de tip P sau V se poate executa asupra unui semafor. Apelurile sistem pentru
lucrul cu semafoare, din cadrul System V, sunt o generalizare a operaiilor P i V ale lui
Dijkstra, n sensul c se pot executa simultan mai multe operaii asupra unui
semafor, valoarea de incrementare sau decrementare putnd fi i mai mare dect 1.
Nucleul execut toate operaiile n mod atomic, nici un alt proces putnd modifica
valoarea semaforului pn n momentul ncheierii tuturor operaiunilor. Dac nucleul
nu poate executa toate operaiile, atunci nu execut nici una, caz n care procesul
trece n starea sleep pn n momentul n care nucleul poate efectua toate operaiile,
dup cum se va explica.

268

n cadrul System V, un semafor se compune din urmtoatele elemente:


-Valoarea semaforului;
-Identificatorul ultimului proces care a modificat valoarea semaforului;
-Numrul de procese care care aeapt creerea valorii semaforului;
-Numrul de procese care aeaptca valoarea semaforului s fie egal cu 0;

Apelurile sistem pentru lucrul cu semafoare sunt, semget pentru a crea i pentru a
obine accesul la un set de semafoare, semctl pentru executarea unor diverse
operaiuni de control asupra setului de semafoare i semop pentru a modifica valorile
semafoarelor.

Figura 10.13. Structurile de date implicate n lucrul cu semafoare


Apelul sistem semget creaz un tablou (vector) de semafoare, avnd urmtoarea
sintax:

id = semget(key, count, flag);

unde key, id i flag au aceleai semnificaii ca i parametrii corespunztori din cadrul


mecanismului de comunicare prin memorie comun. Nucleul aloc o intrare care indic
un vector de structuri de tip semafor, n numr de count. (Figura 10.13.). Intrarea mai
specific de asemenea i numrul de semafoare din cadrul vectorului, momentul
executrii ultimului apel semop i momentul executrii ultimului apel semctl. De
exemplu, apelul sistem semget din figura 10.14. creaz un semafor cu dou elemente.

269

Procesele opereaz asupra valorii semafoarelor cu ajutorul apelului sistem semop,


avnd urmtoarea sintax:
oldval = semop(id, oplist, count);

unde id este descriptorul ntors de apelul semget, oplist este un pointer ctre un
vector de operaii asupra semaforului, iar count este dimensiunea vectorului. Valoarea
ntoars, oldval, reprezint valoarea ultimului semafor din set asupra cruia s-au fcut
operaii, nainte de operaia respectiv. Formatul fiecrui element din oplist este
urmtorul:
-numrul semaforului, care identific
semafoare pe care se opereaz;

intrarea

-operaia propriu-zis;
-valoarea indicatorilor (flags)

270

corespunztoare

vectorului

de

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define SEMKEY 75
int semid;
unsigned int count;
/* definiia structurii de date sembuf din fiierul sys/sem.h> */
/* struct sembuf
*/
/* {
*/
/*
unsigned short sem_num;
*/
/*
short sem_op;
*/
/*
short sem_flg;
*/
/* };
*/
struct sembuf psembuf,vsembuf; /* operaii corespunztoare lui P si V */
main(argc, argv)
int argc;
char *argv[];
{
int i, first, second;
short initarray[2], outarray[2];
extern cleanup();
if(argc==1)
{
for(i=0;i<20;i++)
signal(i, cleanup());
semid=semget(SEMKEY, 2, 0777|IPC_CREAT);
initarray[0]=initarray[1]=1;
semctl(semid, 2, SETALL, initarray);
semctl(semid, 2, GETALL, outarray);
printf(valori initiale semafor %d %d\n, outarray[0],outarray[1]);
pause(); /* trece n sleep pn la trezirea de ctre un semnal */
}
else if(argv[1][0]==a)
{
first=0;
second=1;
}
else
{
first=1;
second=0;
}
semid=semget(SEMKEY, 2, 0777);
psembuf.sem_op=-1;
psembuf.sem_flg=SEM_UNDO;
vsembuf.sem_op=1;
vsembuf.sem_flg=SEM_UNDO;
for(count=0; ;count++)
{
psembuf.sem_num=first;
semop(semid, &psembuf, 1);
psembuf.sem_num=second;
semop(semid, &psembuf, 1);
printf(procesul %d nr. %D\n, getpid(), count);
vsembuf.sem_num=second;
271

semop(semid, &vsembuf, 1);


vsembuf.sem_num=first;
semop(semid, &vsembuf, 1);
}

cleanup()
{
semctl(semid, 2, IPC_RMID, 0);
exit();
}

Figura 10.14. Operaii de blocare i de deblocare

Algoritmul semop /*operaii asupra semafoarelor */


intrri:(1)descriptorul de semafor
(2)vectorul de operaii asupra semafoarelor
(3)numrul de elemente din vector
ieiri:-valoarea iniial a ultimului semafor care a fost modificat
{ verific validitatea descriptorului de semafor;
start:
citete vectorul de operaii asupra semafoarelor din spaiul de adrese
user n spaiul de adrese nucleu;
for(fiecare operaie din vectorul de operaii)
{
if(operaia este pozitiv)
{
adun valoarea operaiei la valoarea semaforului;
if(este setat flagul UNDO pt. Operaie)
actualizeaz structura undo a procesului;
trezete toate procesele(ev: cretere valoare semafor);
}
else if(operaia este negativ)
{
if(valoare_operaie + valoare_semafor >=0)
{
adun valoarea operaiei la valoarea semaforului;
if(este setat flagul UNDO)
actualizeaz structura undo a procesului;
if(valoarea semaforului=0)
trezete toate procesele(ev:val. semaforului devine zero);
continue;
}
anuleaz toate operaiunile deja fcute cu semafoarele n acest
apel sistem(iteraia anterioar);
if(flag-urile specific s nu se treac n sleep)
return(eroare);
sleep(ev:valoarea semaforului crete);
goto start; /* se reia ciclul */
}
else /* valoarea operaiei este zero */
{
if(valoarea semaforului diferit de zero)
{
anuleaz toate operaiunile deja fcute cu semafoarele n
272

acest apel sistem;


if(flag-urile specific s nu se treac n sleep)
return(eroare);
sleep(ev:valoarea semaforului devine zero);
goto start; /* se reia ciclul */
}

}
/* toate operaiile cu semafoare au reut */
actualizeazinregistrrile de timp, identificatorii proceselor;
return(valoarea iniial a ultimului semafor modificat);
}
Figura 10.15. Algoritmul pentru operarea asupra semafoarelor

S considerm programul din figura 10.14. i s presupunem c un utilizator l execut


(a.out) de trei ori n urmtoarea secven:

a.out &
a.out a &
a.out b &

Atunci cnd programul este rulat fr parametri, procesul creaz un set de semafoare
cu dou elemente, ale cror valori sunt iniializate cu 1. Apoi, procesul execut apelul
pause( ) i trece n starea sleep pn cnd este trezit de un semnal, la tergerea unui
semafor n cadrul funciei cleanup( ). Atunci cnd programul se execut cu parametrul
a, procesul (A) face n cadrul ciclului patru operaii distincte cu semafoarele:
decrementeaz valoarea semaforului 0, decrementeaz valoarea semaforului 1,
execut instruciunea printf i apoi incrementeaz valoarea semaforului 1 i a
semaforului 0. Dac un proces ncearc decrementarea valorii unui semafor care are
deja valoarea 0, atunci este trecut n starea sleep i n consecin semaforul se
consider blocat (locked). Deoarece semafoarele au fost iniializate cu valoarea 1 i
nici un alt proces nu le mai folosete, procesul A nu va trece niciodat n starea sleep,
iar valorile semafoarelor vor bascula ntre valorile 0 i 1. Atunci cnd programul se
execut cu parametrul b, procesul (B) decrementeaz semafoarele 0 i 1 n ordinea
invers celei n care a fcut-o procesul A. Atunci cnd procesele A i B se execut
simultan, poate aprea situaia n care procesul A a blocat semaforul 0 i dorete s
blocheze semaforul 1, dar procesul B a blocat semaforul 1 i dorete s blocheze
semaforul 0. n acest caz, ambele procese trec n starea sleep, fiind n imposibilitatea
de a-i continua execuia. Ele sunt deblocate i i ncheie execuia numai la primirea
unui semnal.
Pentru a evita astfel de probleme, procesele pot executa simultan mai multe
operaiuni asupra semafoarelor. Acest rezultat se poate obine folosind urmtoarele
date i instruciuni:

struct sembuf psembuf[2];

273

psembuf[0].sem_num=0;
psembuf[1].sem_num=1;
psembuf[0].sem_op=-1;
psembuf[1].sem_op=-1;
semop(semid, psembuf, 2);

Psembuf este un vector de operaii care decrementeaz simultan semafoarele 0 i 1.


Dac nici una din operaii nu se ncheie cu succes, atunci procesul intr n starea sleep
pn cnd ambele operaii reuesc. De exemplu, dac valoarea semaforului 0 este 1 i
valoarea semaforului 1 este 0, atunci nucleul va lsa ambele valori nemodificate pn
n momentul n care le poate decrementa pe amndou.
Un proces poate activa flagul IPC_NOWAIT n cadul apelului sistem semop; dac
nucleul ajunge n situaia n care procesul trebuie s treac n starea sleep ateptnd
ca semaforul s depeasc o anumit valoare, sau s ia valoarea zero, atunci nucleul
revine din apelul sistem cu eroare. Astfel, este posibil realizarea unui semafor
condiional, la care un proces s nu trec n starea sleep dac nu poate executa
operaiunea atomic.
Situaii periculoase pot aprea atunci cnd un proces execut o operaie asupra unui
semafor, probabil pentru blocarea unei resurse, i apoi i ncheie execuia (printr-un
apel exit) fr s restabileasc valoarea iniial a semaforului. Astfel de situaii pot
aprea ca rezultat al unei greeli de programare, sau ca rezultat al recepionrii unui
semnal care determin ncheierea imediat a execuiei unui proces. Dac, n figura
10.14., procesul recepioneaz un semnal kill dup decrementarea valorilor
semafoarelor, el nu va mai putea s le incrementeze din nou, deoarece semnalele kill
nu pot fi tratate explicit sau ignorate. n consecin, alte procese vor gsi semaforul
blocat chiar dac procesul care a realizat blocarea nu mai exist. Pentru a evita
asemenea probleme, un proces poate seta valoarea indicatorului SEM_UNDO n cadrul
apelului sistem semop; atunci cnd procesul i ncheie execuia (cu exit), nucleul
inverseaz efectul fiecrei operaii cu semafoarele pe care a fcut-o procesul respectiv.
Pentru a implementa aceast facilitate, nucleul memoreaz un tabel care conine o
intrare pentru fiecare proces din sistem. Fiecare intrare indic un set de structuri
undo, cte una pentru fiecare semafor folosit de ctre proces (figura 10.16.). Fiecare
structur undo este un vector de triplei care conin un identificator de semafor, un
numr de semafor din set determinat de identificatorul de semafor i o valoare de
corecie.

274

Figura 10.16. Structurile Undo pentru semafoare


Atunci cnd un proces execut primul su apel semop, avnd setat indicatorul
SEM_UNDO, nucleul aloc dinamic structuri de tip undo. La urmtoarele apeluri semop
care seteaz indicatorul SEM_UNDO, nucleul caut printre structurile de tip undo pe
aceea care conine acelai indicator de semafor i acelai numr ca i cele din apelul
semop: dac gsete o asemenea structur, atunci nucleul scade valoarea operaiei
din valoarea de corecie. Astfel, structura undo va conine suma cu semn schimbat a
valorilor tuturor operaiilor pe care procesul le-a executat asupra semaforului pentru
care a fost setat indicatorul SEM_UNDO. Dac nu exist o asemenea structur pentru
semafor, atunci nucleul va crea una, gestionnd totodat o list de structuri de tip
undo ordonate dup identificatorul de semafor i dup numr. Dac o valoare de
corecie devine zero, atunci nucleul elimin structura de tip undo care o conine.
Atunci cnd un proces i ncheie execuia (cu exit), nucleul apeleaz o rutin special
care parcurge toate structurile undo asociate procesului modificnd semafoarele
corespunztoare.
Identificator
de

Semid

Identificator

semafor

de

---------------------------------Numr

de

semafor

---------------------------------

Numr

semafor

de

semafor

-----------------------------------Corecie

Semid

---------------------------------

Corecie

-------------------------------------

--------------------------------

a)dup prima operaie

b)dup a doua operaie

275

Identificator
de

Semid

semafor

-------------------------------------------------------------------------------------Numr

de

Empty

semafor
-------------------------------------------------------------------------------------Corecie

c)dup a treia operaie

d)dup a patra operaie

Figura 10.17. Secven de structuri Undo

Revenind la figura 10.14., nucleul creaz o structur undo ori de cte ori un proces
decrementeaz valoarea unui semafor i elimin o structur de tip undo ori de cte ori
procesul incrementeaz valoarea unui semafor, deoarece valoarea de corecie din
structura undo devine zero. Figura 10.17. prezint succesiunea evoluiei structurilor de
tip undo n cazul execuiei programului cu parametrul a. Dup prima operaie,
procesul are un singur triplet (o singur structur de tip undo) corespunztoare
numrului de semafor 0 i valorii de corecie 1, iar dup cea de-a doua operaie
procesul dispune i de al doilea triplet, corespunztor numrului de semafor 1 i valorii
de corecie 1. Dac procesul i-ar ncheia execuia n acest moment, atunci nucleul va
parcurge fiecare triplet, adugnd valoarea 1 la fiecare semafor, readucndu-le
valoarea la zero. n mod obinuit, nucleul decrementeaz valoarea de corecie a
semaforului 1 pe timpul celei de a treia operaii, corespunztor creterii valorii
semaforului, i elimin tripletul, deoarece valoarea sa de corecie a devenit zero. Dup
cea de-a patra operaie, procesul nu mai dispune de triplei, deoarecetoate valorile de
corecie vor fi zero.
Operaiile din vectorul de operaii asupra semafoarelor permit proceselor s evite
blocrile, dup cum a fost exemplificat mai sus, dar sunt complicate i majoritatea
aplicaiilor nu are nevoie de ntreaga lor capacitate. Aplicaiile care necesit utilizarea
mai multor semafoare vor trebui s rezolve problema condiiilor de blocare, la nivel
utilizator, pentru ca nucleul s nu conin astfel de apeluri sistem complicate.
Apelul sistem semctl conine o mulime de operaii de control relative la semafoare,
avnd urmtoarea sintax:
semctl(id, number, cmd, arg);

Parametrul arg este declarat de tip union:

276

union semunion
{

int val;
struct semid_ds *semstat;
unsigned short *array;

} arg;

Nucleul interpreteaz parametrul arg pe baza valorii parametrului cmd, n mod


asemntor celui n care interpreteaz comenzile ioctl. Efectele comenzii se produc
pentru valori ale parametrilor cmd care restaureaz sau seteaz parametrii de control
(drepturile de acces i ali parametri), iniializeaz una sau toate valorile semafoarelor
dintr-un set de semafoare, sau realizeaz citirea valorilor semafoarelor. Pentru
comanda de eliminare a unui semafor, IPC_RMID, nucleul gsete toate procesele care
au structuri de tip undo pentru semaforul respectiv i elimin tripletele
corespunztoare. Apoi, reiniializeaz structura de date pentru semafoare i trezete
toate procesele care ateapt producerea unui eveniment relativ la semafoare. Atunci
cnd procesele i reiau execuia, vor constata c identificatorul de semafor nu mai
este valabil i vor ntoarce o eroare ctre apelant.

10.2.4. Concluzii generale

Exist cteva asemnri ntre sistemul de fiiere i mecanismele de comunicare ntre


procese (mecanismele IPC). Apelurile sistem get sunt similare apelurilor sistem creat
i open, iar apelurile sistem control conin o opiune de eliminare a descriptorilor din
structurile de date, ca i apelul sistem unlink. Dar nu exist nici o operaiune analoag
apelului sistem close, din cadrul sistemului de fiiere. Astfel, nucleul nu are o eviden
a proceselor care au acces la vreunul din mecanismele de comunicare, i ntradevr,
procesele pot avea acces la unul din mecanismele de comunicare, cu condiia s
cunoasc identificatorul corect i s ndeplineasc restricia privind drepturile de acces,
dei aceste procese nu au invocat un apel sistem de tip get. Nucleul nu poate terge
automat structurile de date ale mecanismelor de comunicare, deoarece el nu tie dac
acestea mai sunt sau nu folosite. n acest fel, procesele care nu sunt programate
foarte minuios pot lsa la voia ntmplrii, dup ncheierea execuiei lor, strucuri de
date care nu mai sunt necesare nimnui i, n consecin, care nu mai sunt folosite de
nimeni. Cu toate c, dup ncheierea execuiei unui proces, nucleul poate salva
informaiile de stare i datele n cadrul structurilor de date ale mecanismelor de
comunicare, totui este mai indicat folosirea fiierelor n acest scop.
Mecanismele de comunicare ntre procese aduc cu ele un nou spaiu de nume, noi
chei, n locul arhicunoscutelor i tradiionalelor fiiere. Este dificil extinderea
semanticii cheilor la nivelul unei reele, deoarece acestea (cheile) pot descrie obiecte
diferite de pe maini diferite. Pe scurt, cheile au fost gndite pentru a opera ntr-un
mediu concentrat (de tip o singur main). Numele de fiiere sunt mai potrivite a
opera n medii distribuite. Utilizarea cheilor n locul numelor de fiiere, mai implic i
faptul c facilitile IPC sunt entiti date ca atare, folositoare n cadrul aplicaiilor
277

dedicate, dar crora le lipsesc posibilitile de construire de instrumente, aspecte


inerente n cazul fiierelor i al pipe-urilor, de exemplu. Mare parte din
funcionalitatea lor poate fi emulat prin folosirea celorlalte faciliti ale sistemului,
astfel c, din punctul de vedere al eleganei n lucru, ele nu ar trebui incluse n nucleu.
Totui, n cazul aplicaiilor care funcioneaz ntr-o strns legtur, ele asigur
performane superioare facilitilor oferite de sistemul de fiiere.

10.3.

Comunicarea n reea prin socket-uri

Programe precum cel de pot electronic, transfer de fiiere de la distan i


deschidere de sesiune la distan, care au nevoie s comunice cu alte maini, au
utilizat n trecut metode dedicate pentru a stabili n mod fizic legturile i pentru a
schimba date. De exemplu, programele standard de pot electronic salveaz textul
unui mesaj potal al utilizatorului ntr-un anumit fiier, cum ar fi fiierul
/usr/mail/mjb pentru utilizatorul mjb. Atunci cnd un utilizator emite un mesaj
potal ctre un alt utilizator aflat pe acceai main, programul mail adaug mesajul la
sfritul fiierului destinatarului, ntrebuinnd n acest scop fiiere cu acces restrictiv
(lock files) i fiiere temporare pentru asigurarea uniformitii. Atunci cnd un
utilizator citete corespondena, programul mail face un apel open pentru deschiderea
fiierului de pot electronic al persoanei respective, i un apel read pentru a citi
efectiv octeii ce compun mesajele. Pentru a trimite un mesaj unui utilizator aflat pe o
alt main, programul mail trebuie n final s gseasc fiierul de pot electronic
corespunztor, de pe cealalt main. Deoarece programul nu poate manipula n mod
direct fiierele de pe acea main, atunci trebuie ca un proces care se execut acolo s
acioneze ca un fel de agent potal pentru instana local (procesul local) a
programului de pot electronic; prin urmare, procesul local are nevoie de o
modalitate de a comunica cu agentul su aflat (care se execut) la distan, pe
cealalt main. Procesul local este numit client (beneficiar de servicii) al procesului de
la distan, numit server (furnizor de servicii).
Deoarece sistemul de operare UNIX creaz noi procese prin intermediul apelului sistem
fork, procesul server trebuie s existe naintea momentului n care procesul client
ncearc s stabileasc legtura. Ar fi o inconsisten n proiectarea sistemului (fa de
filozofia sa) ca nucleul de pe maina de la distan s creeze un nou proces n
momentul sosirii din reea a unei cereri de conectare. n schimb, un anumit proces, de
regul procesul init, creaz un proces server care ascult linia de comunicaie, pn
n momentul recepionrii unei cereri de asigurare a unui serviciu, ulterior aplicnd un
anume protocol pentru a ncheia secvena de iniializare a legturii. Programele server
i client fie aleg, n general, mediul i protocoalele reelei, n funcie de informaiile din
bazele de date ale aplicaiilor, fie exist posibilitatea ca datele s fie codificate hard n
cadrul programelor.
De exemplu, programul uucp permite transferul de fiiere prin reea i execuia de
comenzi de la distan. Un proces client interogheaz o baz de date n scopul obinerii
informaiilor de adres i de rutare (de exemplu un numr de telefon), face un apel
open pentru deschiderea unui fiier special (un fiier device, pentru un dispozitiv
modem), face un apel write sau ioctl pentru scrierea informaiilor pe baza
descriptorului fiierului deschis i cheam maina aflat la distan. Maina aflat la
distan poate avea linii speciale pentru lucrul programului uucp; procesul init de pe
278

aceast main creaz procese getty (adic procesele server) pentru supravegherea
liniilor i pentru notificarea stabilirii legturii. Dup realizarea fizic a legturii,
procesul client deschide sesiunea, urmnd n acest sens protocolul obinuit: procesul
getty face un apel exec pentru a lansa un proces care execut codul unui interpretor
special de comenzi, uucico, precizat n fiierul /etc/passwd, iar procesul client face un
apel write pentru a scrie secvena de comenzi pe maina de la distan, determinnd
executarea proceselor de pe aceasta n folosul mainii locale.
Comunicaiile prin reea au ridicat o problem sistemelor UNIX, concretizat n faptul
c mesajele trebuie, n mod frecvent, s conin poriuni de date i de informaii de
control. Segmentul de control poate conine informaii de adresare, pentru specificarea
destinaiei unui mesaj. Informaia de adresare este structurat n funcie de tipul de
reea i de protocol utilizate. Prin urmare, procesele trebuie s cunoasc tipul reelei,
n ideea c utilizatorilor trebuie s le fie transparent tipul unui anume fiier, deoarece
toate dispozitivele periferice sunt tratate ca fiiere. Metodele tradiionale de
implementare a comunicrii prin reea se bazeaz n mod consecvent i n mare
msur pe apelul sistem ioctl pentru a preciza informaia de control, dar aceast
abordare nu este generalizat la toate tipurile de reele. O consecin nedorit a
acestui aspect o constituie faptul c este posibil ca programele proiectate pentru o
reea s nu funcioneze pe alte reele.
mbuntirea interfeelor de reea pentru sistemele UNIX a necesitat un efort
semnificativ. Implementarea sub form de fluxuri (streams) specific ultimelor
variante ale System V furnizeaz un mecanism elegant de asigurare a lucrului n reea,
deoarece modulele de implementare a protocoalelor pot fi combinate n mod flexibil
prin includerea lor n stream-uri i deoarece utilizarea lor este uniform la nivel
utilizator. Urmtorul subcapitol descrie socket-urile, care reprezint soluia problemei,
adoptat n cadrul variantei BSD.

Subcapitolul anterior a artat modul n care pot comunica procesele care se execut
pe maini diferite, subliniind faptul c este foarte probabil ca metodele prin care aceste
procese stabilesc legturile, s difere n funcie de protocol i de mediul de transmisie.
Mai mult, aceste metode pot s nu permit comunicarea ntre procesele chiar de pe
aceeai main, deoarece ele presupun existena unui proces server care se afl n
ateptare n cadrul unui apel sistem open sau read, relativ la un dispozitiv. Pentru a
furniza metode generale de comunicare ntre procese i pentru a permite utilizarea
protocoalelor de reea sofisticate, sistemul BSD pune la dispoziie un mecanism
cunoscut sub numele de socket-uri. n acest subcapitol sunt descrise unele aspecte, la
nivel utilizator, ale socket-urilor.

279

Figura 10.18. Modelul socket-urilor


Structura acestui mecanism la nivelul nucleului, prezint trei pri: nivelul socket
nivelul protocol i nivelul dispozitiv (figura 10.18). Nivelul socket furnizeaz interfaa
dintre apelurile sistem i nivelurile inferioare, nivelul protocol conine modulele de
protocoale folosite pentru comunicaie (n figur, TCP i IP), iar nivelul dispozitiv
conine driverele de care controleaz lucrul dispozitivelor de reea. La configurarea
sistemului se precizeaz combinaiile de protocoale acceptate i driverele, metod ce
nu este la fel de flexibil ca i includerea n stream-uri a modulelor. Procesele
comunic dup modelul client-server: un proces server ascult la un socket, unul din
capetele unei linii bidirecionale de comunicaii, iar procesul client transmite serverului
prin cellalt socket, adic cellalt capt al liniei, care poate fi pe o alt main. Nucleul
pstreaz conexiunea intern i direcioneaz datele de la client ctre server.
Socket-urile care au aceleai proprieti de comunicaie, cum ar fi conveniile de nume
i formatele de adres, sunt grupate n domenii (domains). Sistemul BSD 4.2 suport
domeniul sisteme UNIX de comunicare ntre procesele de pe aceeai main, i
domeniul Internet de comunicare ntre procese prin reea utiliznd protocoalele de
comunicaie DARPA(Defense Advanced Research Project Agency). Fiecare socket are
un tip, i anume circuit virtual (virtual circuit, sau stream socket, n terminologia
Berkeley) sau datagram (datagram). Un circuit virtual permite, la recepie, o
furnizare secvenial (n ordinea emiterii) i sigur (fr pierderi) a datelor transmise.
Datagramele nu garanteaz, la recepie, pstrarea ordinii de la emisie a datelor,
integritatea acestora, sau neduplicarea lor, n schimb sunt mai puin costisitoare dect
circuitele virtuale, pentru c nu necesit operaiuni de iniializare deosebite; prin
urmare ele sunt utile pentru anumite tipuri de comunicaii. Sistemul conine un
protocol implicit pentru fiecare combinaie acceptat de tip de domain-socket. De
exemplu, protocolul TCP (Transport Connect Protocol) furnizeaz serviciul de circuit
virtual, iar protocolul UDP (User Datagram Protocol) serviciul de datagrame, n
domeniul Internet.
Mecanismul de socket-uri conine cteva apeluri sistem. Apelul sistem socket
stabilete punctul final al unei de legturi de comunicaie.

sd = socket(format, type, protocol)

280

unde parametrul format precizeaz domeniul de comunicaie (domeniul UNIX, sau


domeniul Internet), type indic tipul de comunicaie prin socket (circuit virtual, sau
datagram), iar protocol precizeaz un anumit protocol pentru controlul comunicaiei.
n cadrul altor apeluri sistem, procesele vor ntrebuina descriptorul de socket, sd.
Apelul sistem close nchide socket-urile.
Apelul sistem bind asociaz un nume unui descriptor de socket:

bind(sd, address, length);

unde sd este descriptorul de socket, iar address este adresa unei structuri care
precizeaz un indicator al domeniului i protocolului de comunicaie, precizate n cadrul
apelului sistem socket. Parametrul length reprezint lungimea structurii de date
address; fr acest parametru nucleul nu ar ti ct de lung este adresa, deoarece
lungimea acesteia poate diferi de la un domeniu (sau protocol) la altul. De exemplu, n
cadrul domeniului UNIX, o adres este un nume de fiier. Procesul server transform
adresele din apelul bind n socket-uri i face publice numele lor pentru a fi
identificate de ctre procesele client.
Apelul sistem connect cere nucleului s fac o conexiune cu un socket existent:

connect(sd, address, length);

unde semnificaia parametrilor este aceeai ca la apelul bind, cu deosebirea c


parametrul address reprezint adresa socket-ului de destinaie care va constitui
cellalt capt al liniei de comunicaie. Ambele socket-uri trebuie s foloseasc acelai
domeniu i protocol de comunicaie, rmnnd n sarcina nucleului iniializarea corect
a legturilor de comunicaie. Dac tipul socket-ului este datagram, atunci apelul
connect informeaz nucleul asupra adresei de utilizat n cadrul apelurilor send
ulterioare prin socket-ul respectiv; n momentul apelului nu se realizeaz nici o
legtur.
Atunci cnd un proces server accept legturile printr-un circuit virtual, nucleul trebuie
s pun ntr-o coad de ateptare cererile care sosesc, pn n momentul n care va
putea s le satisfac. Apelul sistem listen precizeaz lungimea maxim a cozii de
ateptare:

listen(sd, qlength);

unde sd este descriptorul de socket i qlength reprezint numrul maxim de cereri


care vor fi luate n consideraie.

281

Figura 10.19. Acceptarea unui apel de cre un proces server


Apelul sistem accept primete cererile de conectare la un proces server, care sosesc:

nsd = accept(sd, address, addrlen);

unde sd este descriptorul de socket, address indic un vector de date utilizator pe care
nucleul l completeaz cu adresa de retur a procesului client care se conecteaz, iar
addrlen precizeaz dimensiunea acestui vector. La revenirea din apelul accept, nucleul
scrie n addrlen un numr care semnific dimensiunea spaiului ocupat de adres.
Apelul accept ntoarce un nou descriptor de socket, nsd, diferit de descriptorul sd. Un
proces server poate continua s asculte la socket-ul anunat, n timp ce comunic cu
un proces client pe un canal separat de comunicaie (figura 10.19).
Apelurile sistem send i recv transmit date printr-un socket conectat:

count = send(sd, msg, length, flags);

unde sd este descriptorul de socket, msg este un pointer ctre datele care urmeaz a
fi trimise, length reprezint lungimea datelor de trimis, iar count este numrul de
octei efectiv trimii. Parametrului flags i se poate atribui valoarea SOF_OOB pentru a
realiza o trimitere out-of-band a unor date, nelegnd prin aceasta faptul c datele
trimise nu sunt considerate c fac parte din schimbul obinuit de date ntre procesele
care comunic. De exemplu, un program de deschidere de sesiune la distan
(remote-login program) poate trimite un mesaj out-of-band pentru a simula
apsarea tastei Del de ctre un utilizator, la un terminal. Sintaxa apelului sistem recv
este

count = recv(sd, buf, length, flags);

unde buf este locul unde se memoreaz datele care sosesc, length este lungimea
teoretic ateptat a datelor, iar count este numrul de octei efectiv copiai n
programul utilizatorului. Parametrul flags poate primi valoarea peek, pentru un
mesaj care sosete, n scopul examinrii coninutului su fr a-l scoate din coada de
ateptare, sau valoarea out-of-band pentru cazul explicat mai sus. Versiunile pentru
datagrame ale acestor apeluri sistem, sendto i recvfrom, au n plus parametri i
282

pentru adrese. Procesele pot utiliza apelurile sistem read i write n contextul streamsocket-urilor, n locul apelurilor send i recv, dup iniializarea legturii. Astfel,
procesele server se pot ngriji de negocierea protocoalelor de reea i de crearea
proceselor care utilizeaz numai apelurile sistem read i write, ca i cnd acestea ar
lucra cu fiiere obinuite.
Apelul sistem shutdown nchide o legtur cu un socket:

shutdown(sd, mode);

unde mode indic dac partea care trimite datele, partea care le recepioneaz, sau
ambele pri ncheie transmisia datelor. Acest apel informeaz protocoalele de nivel
inferior s ncheie comunicaia prin reea, ns cu meninerea intact a descriptorilor
de socket-uri. Apelul sistem close elimin i descriptorul de socket.
Apelul sistem getsockname furnizeaz numele asociat unui socket, printr-un apel bind
anterior:

getsockname(sd, name, length);

Apelurile getsockopt i setsockopt obin i respectiv seteaz diferite opiuni asociate


socket-ului, n concordan cu domeniul i protocolul socket-ului.
S considerm programul server din figura 10.20. Procesul creaz un stream-socket n
cadrul domeniului UNIX (UNIX system domain) i printr-un apel bind i asociaz
numele sockname. Apoi invoc apelul sistem listen, pentru a preciza lungimea cozii de
ateptare pentru mesajele care sosesc i intr ntr-o bucl, ateptnd cererile care
sosesc. Apelul accept ateapt pn cnd protocolul de nivel inferior notific sosirea
unei cereri de legtur cu socket-ul care are numele respectiv; apoi, apelul accept
ntoarce un nou descriptor pentru cererea care sosete. Procesul server creaz un
proces (prin apelul fork) care s comunice cu procesul client: procesele printe i fiu
fac un apel close pentru descriptorii corespunztori, astfel c nu intervin n
comunicaiile celorlalte procese. Procesul fiu i continu dialogul cu procesul client,
ncheindu-i execuia (n acest exemplu) dup apelul read. Procesul server reintr n
bucl i ateapt o alt cerere de legtur, n cadrul apelului accept.

283

#include <sys/types.h>
#include <sys/socket.h>
main()
{
int sd, ns;
char buf[256];
struct sockaddr sockaddr;
int fromlen;
sd=socket(AF_UNIX, SOCKSTREAM, 0);
/* asocierea unui nume -- nu includei caracterul nul n nume */
bind(sd, sockname, sizeof(sockname)-1);
listen(sd, 1);
for(;;)
{
ns=accept(sd, &sockaddr, &fromlen);
if(fork()==0)
{
/* codul procesului fiu */
close(sd);
read(ns, buf, sizeof(buf));
printf(serverul citete %s\n, buf);
exit();
}
close(ns);
}
}
Figura 10.20. Un proces server din cadrul domeniului UNIX

Figura 10.21 prezint procesul client corespunztor procesului server. Procesul creaz
un un socket n acelai domeniu ca i serverului emite o cerere connect pentru acelai
sockname, asociat de un server unui socket. La ieirea din apelul connect, procesul
client dispune de un circuit virtual ctre un proces server. n acest exemplu, procesul
client scrie (prin apelul write) un singur mesaj i i ncheie execuia (cu exit).
Dac procesul server furnizeaz servicii proceselor dintr-o reea, atunci n cadrul
apelurilor sale sistem se specific faptul c socket-ul aparine domeniului Internet
(Internet domain), prin

socket(AF_INET, SOCK_STREAM, 0);

i asociaz (prin apelul bind) o adres de reea furnizat de un name server. Sistemul
BSD dispune de apeluri de bibliotec, care fac aceste funcii. Similar, al doilea
parametru al apelului connect fcut de procesul client, ar trebui s conin informaia
de adres necesar identificrii mainii n reea (sau adresa de rutare cu care s se
trimit mesajele la maina de destinaie, prin maini intermediare), precum i
informaii suplimentare de identificare a unui anumit socket de pe maina de
destinaie. Dac procesul server dorete s asculte att n reea, ct i la procesele
284