Documente online.
Zona de administrare documente. Fisierele tale
Am uitat parola x Creaza cont nou
 HomeExploreaza
upload
Upload




COMUNICATII INTER-PROCESE

Informatica


COMUNICA II INTER-PROCESE



Mecanismele de comunicare īntre procese permit acestora sa schimbe date īntre ele si sa-si sincronizeze executia. Au fost prezentate deja cāteva modalitati de comunicare īntre procese, cum ar fi pipe-urile (nenumite), pipe-urile numite si semnalele. Pipe-urile (nenumite) au dezavantajul ca sunt cunoscute numai de catre procesele care sunt fii ai procesului care a invocat apelul sistem pipe. Procesele neīnrudite nu pot comunica prin pipe-uri. Desi pipe-urile numite permit comunicarea īntre procese neīnrudite, ele nu reprezinta totusi o modalitate de comunicare īn retea si nici nu pot fi usor adaptate pentru folosirea cailor multiple de comunicare īntre grupuri diferite de procese este imposibila multiplexarea unui pipe numit pentru a pune la dispozitie canale separate perechilor de procese care comunica. De asemenea, procesele oarecare mai pot comunica si prin trimiterea de semnale unul altuia, cu ajutorul apelului sistem kill, dar īn acest caz "mesajul" consta doar īntr-un numar asociat semnalului.

Īn acest capitol se descriu alte forme de comunicare īntre procese. Capitolul īncepe prin studierea executiei pas cu pas a proceselor, īntelegānd prin aceasta faptul ca un proces urmareste si controleaza executia pas cu pas a altui proces, iar apoi continua cu explicarea modalitatilor de comunicare īntre procese, modalitati specifice variantei System V si cunoscute sub numele System V IPC comunicarea prin mesaje, prin memorie partajata si prin semafoare. Se face o trecere īn revista a metodelor obisnuite prin care se realizeaza comunicarea īntre procese de pe masini diferite prin intermediul unei retele si, īn final, se face o prezentare la nivel utilizator a socket-urilor BSD. Nu se iau īn discutie probleme specifice retelelor, cum ar fi protocoale si adrese, acestea nefacānd obiectul de studiu al cartii.

10.1. Executia pas cu pas a proceselor

Sistemul de operare UNIX pune la dispozitie o modalitate rudimentara de comunicare īntre procese īn scopul executarii pas cu pas a acestora, facilitate utila in depanarea programelor. Un proces depanator (debugger process), cum ar fi sdb, creaza un proces pe care īl executa pas cu pas si īi controleaza executia cu ajutorul apelului sistem ptrace, stabilind si anulānd puncte de īntrerupere (breakpoints) si scriind (citind) date īn (din) spatiul sau virtual de adrese. Īn consecinta, executia pas cu pas a unui proces consta īn sincronizarea executiei procesului depanator cu cea a procesului depanat si īn controlul executiei celui de-al doilea.

if((pid=fork())==0)

/* aici continua procesul parinte=procces 727e43h ul depanator */

for(;;)

Figura 10.1. Structura unui proces de depanare

Pseudocodul din figura 10.1. pune in evidenta o structura caracteristica pentru un program de depanare. Procesul depanator creaza un proces fiu, care invoca apelul sistem ptrace al carui rezultat este pozitionarea pe 1 a bitului de executie pas cu pas īn intrarea corespunzatoare procesului fiu, din tabela de procese. Procesul fiu executa, prin apelul sistem exec, programul care urmeaza sa fie depanat. De exemplu, daca utilizatorul doreste sa depaneze programul a.out , atunci argumentul transmis apelului sistem exec va fi sirul de caractere a.out si procesul fiu va executa acest program. Nucleul va executa apelul sistem exec ca de obicei, dar la sfārsit va observa faptul ca bitul de executie pas cu pas este pozitionat pe 1 si, īn consecinta, va trimite procesului fiu un semnal trap. La īntoarcerea din apelul sistem exec, nucleul verifica daca s-au trimis semnale -asa cum o face, de altfel, la īntoarcerea din orice alt apel sistem- gaseste semnalul "trap" pe care, chiar el, tocmai l-a trimis si executa pas cu pas codul procesului fiu, īntocmai ca pe o secventa speciala de tratare a semnalelor. Observānd pozitionarea pe 1 a bitului de executie pas cu pas din propria intrare īn tabela de procese, procesul fiu trezeste procesul parinte din starea de asteptare īn care intrase prin executarea apelului sistem wait (dupa cum se va vedea mai tārziu), intra īntr-o stare speciala de executie pas cu pas (trace), care nu este evidentiata īn diagrama starilor proceselor si face o comutare de context.

Īn mod obisnuit, procesul parinte este deja intrat intr-un ciclu, asteptānd sa fie trezit de catre procesul fiu care se executa pas cu pas. Cānd procesul fiu trezeste procesul parinte, acesta īncheie executia apelului sistem wait, citeste comenzile de la intrare ale utilizatorului si le converteste intr-o serie de apeluri ptrace īn scopul controlarii executiei procesului fiu.

Sintaxa apelului sistem ptrace este urmatoarea

ptrace( cmd, pid, addr, data)

unde cmd specifica diverse comenzi cum ar fi citirea sau scrierea datelor, ori reluarea executiei, pid reprezinta identificatorul de proces al procesului care se executa pas cu pas, addr reprezinta adresa virtuala, din cadrul procesului fiu, la (de la) care se va scrie (citi) si data reprezinta o valoare de tip īntreg care va fi scrisa. La executarea apelului sistem ptrace, nucleul verifica daca procesul depanator are vreun proces fiu al carui identificator este egal cu valoarea parametrului pid si daca acest proces fiu se afla īn starea "executie pas cu pas", utilizīnd apoi o structura de date globala, dedicata executiei pas cu pas, pentru realizarea transferului de date īntre cele doua procese. Nucleul blocheaza accesul la structura de date globala amintita, pentru a īmpiedica scrierea de catre alte procese aflate īn executie pas cu pas, copiaza valorile parametrilor cmd, addr si data īn structura de date, trezeste procesul fiu si-l trece īn starea "gata de executie", punāndu-se apoi īn asteptare pīna la sosirea raspunsului de la procesul fiu. Cānd procesul fiu īsi reia executia (īn modul nucleu), el executa comanda prevazuta īn parametrul cmd, scrie rezultatul executarii comenzii īn structura de date globala dedicata executiei pas cu pas si apoi trezeste procesul depanator. Īn functie de comanda precizata prin parametrul cmd, procesul fiu poate sa reintre īn starea "executie pas cu pas" asteptānd o noua comanda, sau poate īncheia tratarea semnalului reluāndu-si executia. Cānd procesul depanator īsi reia executia, nucleul memoreaza valoarea de retur furnizata de procesul executat pas cu pas, deblocheaza accesul la structura de date globala corespunzatoare si reda controlul utilizatorului.

Daca, īn momentul intrarii procesului fiu īn starea "executie pas cu pas", procesul depanator nu se afla īn asteptare īn apelul sistem wait, el nu va putea determina care este procesul fiu ce se va executa pas cu pas, decāt īn momentul invocarii apelului sistem wait, moment īn care se va termina imediat si va proceda asa cum tocmai a fost descris mai sus.

int datas32t;

main()

Figura 10.2. Programul trace -- un proces executat pas cu pas

Sa luam īn consideratie cele doua programe, denumite trace si debug, prezentate īn figurile 10.2 si respectiv 10.3. Rulānd programul trace la terminal, valorile vectorului data vor fi egale cu zero procesul afiseaza adresa vectorului data si īsi īncheie executia. Rulānd acum programul debug , avānd ca parametru o valoare egala cu cea afisata de catre programul trace, acesta salveaza valoarea parametrului ce i s-a transmis īn variabila addr , creaza un proces fiu care invoca apelul sistem ptrace pentru a putea executa pas cu pas si un alt proces executa programul trace.

adefine TRĪSETUP 0

adefine TRĪWRITE 5

adefine TRĪRESUME 7

int addr;

main(argc,argv)

int argc;

char *argvst;

for(i=0;i<32;i++)

/* procesul executat pas cu pas īsi reia executia */

ptrace(TRĪRESUME,pid,1,0);

}

Figura 10.3 Debug-un proces de executie pas cu pas

La sfārsitul executiei apelului sistem exec, nucleul trimite procesului fiu (sa-l numim procesul trace) un semnal SIGTRAP, iar procesul trace intra īn starea "executie pas cu pas", asteptānd comenzi de la procesul debug. Daca procesul debug se afla īn asteptare īn algoritmul wait, acesta se trezeste, gaseste procesul fiu executat pas cu pas si paraseste apelul wait. Apoi, procesul debug invoca apelul sistem ptrace, scrie valoarea variabilei de ciclare i īn spatiul de adrese al procesului trace si incrementeaza valoarea din variabila addr īn cadrul procesului trace, variabila addr reprezinta adresa unei intrari din vectorul data. Ultima invocare a apelului ptrace de catre procesul debug are ca efect executia procesului trace , iar īn acest caz vectorul data contine valorile de la 0 la 31. Un program depanator cum ar fi sdb are acces la tabela de simboluri a procesului executat pas cu pas, din continutul careia determina adresele utilizate ca parametri pentru apelurile procedurii ptrace.

Utilizarea apelului sistem ptrace īn scopul executarii pas cu pas a proceselor, este o metoda rudimentara care prezinta mai multe dezavantaje.

Nucleul trebuie sa execute patru comutari de context, pentru a realiza transferul a doi octeti de date īntre procesul depanator si procesul executat pas cu pas nucleul executa o comutare de context īn cadrul procesului depanator la invocarea apelului sistem ptrace, pāna cānd procesul executat pas cu pas trimite un raspuns,comuta contextul la si de la procesul executat pas cu pas, si comuta contextul īnapoi la procesul depanator īmpreuna cu raspunsul catre apelul ptrace. Aceste comutari sunt necesare, īntrucāt un proces depanator nu are alta modalitate de a accesa spatiul virtual de adrese al procesului executat pas cu pas, si īn consecinta este un proces lent.

Un proces depanator poate executa pas cu pas mai multe procese fiu simultan, cu toate ca, īn practica, aceasta facilitate este rareori exploatata. Un aspect mai critic īl reprezinta, īnsa, faptul ca un proces depanator nu poate executa pas cu pas decāt procese fiu. Daca un proces fiu, executat pas cu pas, invoca apelul sistem fork (adica īsi creaza propriul proces fiu), atunci procesul depanator nu are nici un control asupra procesului "nepot" ceea ce reprezinta o restrictie majora īn cazul depanarii programelor complexe. Daca un proces executat pas cu pas invoca apelul sistem exec, depanarea se continua si la nivelul imaginilor -ulterioare apelului exec - ale procesului depanat, din cauza apelului ptrace initial, depanatorul neputānd, īnsa, sa stie numele imaginii procesului care a rezultat īn urma apelului exec, ceea ce face dificila depanarea simbolica.

Un proces depanator nu poate executa pas cu pas un alt proces care se afla deja īn executie, daca acesta din urma nu a invocat, īn prealabil, apelul sistem ptrace , pentru a preveni nucleul asupra faptului ca permite executia sa, pas cu pas. Acest lucru este, de asemenea, un dezavantaj, deoarece pentru a executa pas cu pas un anumit proces aflat deja īn executie, acesta trebuie īn prealabil terminat (cu apelul kill) si relansat īn executie, īn modul "executie pas cu pas".

Nu este posibila executia pas cu pas a programelor de tip setuid, deoarece utilizatorii ar putea īncalca drepturile de acces, avānd posibilitatea de a scrie īn spatiul virtual de adrese al acestor programe, cu ajutorul apelului sistem ptrace, si de a executa operatiuni nepermise. De exemplu, sa presupunem ca un astfel de program, invoca apelul exec avānd ca parametru numele de fisier privatefile. Un utilizator istet s-ar putea folosi de apelul sistem ptrace pentru a modifica parametrul respectiv la valoarea bin sh , lansānd astfel īn executie shell-ul (cu toate programele asociate acestuia) fara a avea permisiunea de a o face. Apelul exec ignora valoarea bitului setuid daca procesul este executat pas cu pas, pentru a evita scrierea de catre un utilizator īn cadrul spatiului de adrese al unui astfel de program.

Killian descrie o alta modalitate de executie pas cu pas a proceselor, bazata pe lucrul cu sistemele de fisiere, descris īn capitolul 5 . Administratorul de sistem monteaza un sistem de fisiere, proc -utilizatorii identifica procesele pe baza identificatorilor acestora si le trateaza ca fiind fisiere din proc. Nucleul acorda permisiunea de deschidere a fisierelor, īn conformitate cu identificatorul utilizatorului si identificatorul de grup. Utilizatorii au posibilitatea de a examina spatiul de adrese al procesului prin citire si de a stabili puncte de īntrerupere prin scriere īn fisier. Apelul stat furnizeaza diverse informatii de ordin statistic despre procese. Aceasta metoda īnlatura trei dintre dezavantajele utilizarii apelului ptrace. Mai īntāi, aceasta metoda este mai rapida deoarece un proces depanator poate transfera o cantitate mai mare de date la un apel sistem, īn comparatie cu apelul ptrace. Īn al doilea rānd, un proces depanator poate executa pas cu pas orice proces, care nu trebuie sa fie neaparat un proces fiu al sau. Īn sfārsit, un proces depanat nu necesita masuri prealabile pentru a permite executia sa pas cu pas un proces depanator poate executa pas cu pas procese deja īn curs de executie. Ca parte a mecanismului obisnuit de protectie a fisierelor, apare faptul ca doar superutilizatorul poate executa pas cu pas procesele de tip setuid pentru fisierele din radacina.

10.2 Comunicarea īntre procese prin pachetul IPC

Pachetul System V IPC din UNIX se compune din trei mecanisme. Mesajele permit proceselor sa trimita fluxuri de date formatate catre alte procese, memoria partajata permite proceselor sa foloseasca īn comun parti din spatiul lor virtual de adrese, iar semafoarele permit proceselor sa-si sincronizeze executia. Implementate īn mod unitar, aceste mecanisme au o serie de proprietati comune

Fiecare mecanism contine o tabela ale carei intrari descriu toate instantele acelui mecanism.

Fiecare intrare din tabela contine o cheie numerica, care reprezinta numele sau desemnat de utilizator.

Fiecare mecanism dispune de un apel sistem de tip "get" destinat crearii unei noi intrari, sau obtinerii uneia deja existente, parametrii acestor apeluri incluzānd o cheie si un indicator. Nucleul cauta īn tabela corespunzatoare intrarea desemnata de catre cheie. Procesele pot invoca apelurile de tip "get" cu parametrul cheie avānd valoarea IPC PRIVATE, pentru a se asigura de obtinerea unei intrari nefolosite. Procesele pot seta indicatorul la valoarea IPC CREAT pentru a crea o noua intrare, daca cea precizata prin cheie nu exista deja, si de asemenea pot determina semnalizarea erorilor prin pozitionarea indicatorilor IPC EXCL si IPC CREAT, pentru situatia īn care deja exista o intrare desemnata prin acea cheie. Apelurile sistem de tip "get"returneaza un descriptor determinat de nucleu, care va fi utilizat ulterior ca parametru al altor apeluri sistem, rezultānd de aici analogia cu apelurile sistem creat si open.

Īn cadrul fiecarui mecanism IPC, pentru determinarea indexului din tabela structurilor de date, nucleul utilizeaza urmatoarea formula de calcul bazata pe valoarea descriptorului

index = descriptor modulo (numarul de intrari din tabela)

De exemplu, daca tabela structurilor de date pentru mesaje contine 100 de intrari, atunci descriptorii pentru intrarea 1 sunt 1, 101, 201 s.a.m.d. Cānd un proces elibereaza o intrare, nucleul creste valoarea descriptorului asociat acesteia cu o valoare egala cu numarul de intrari din tabela. Noua valoare, astfel obtinuta, devine noul descriptor al intrarii respective, īn cazul unei noi alocari a acesteia printr-un apel de tip "get". Orice īncercare ulterioara a proceselor de a accesa intrarea respectiva, pe baza vechiului descriptor, se va solda cu eroare. Īn legatura cu exemplul de mai sus, daca descriptorul asociat intrarii 1 din tabela pentru mesaje este 201, atunci cānd intrarea respectiva este eliberata, nucleul īi atribuie acesteia un nou descriptor, 301. Procesele care īncearca sa acceseze intrarea, pe baza vechiului descriptor (201), vor primi un mesaj de eroare, deoarece acest descriptor nu mai este valabil. Nucleul recicleaza eventual numerele pentru descriptori, probabil dupa o lunga perioada de timp.

Fiecare intrare a unui mecanism IPC dispune de o structura de date pentru stocarea permisiunilor, aceasta incluzānd indentificatorul de utilizator si indentificatorul de grup ale procesului care a creat intrarea, un indentificator de utilizator si unul de grup fixati printr-un apel sistem de tip "control" si un set de permisiuni de citire-scriere-executie separat pentru utilizator, grup si ceilalti, similare permisiunilor de acces la fisiere.

Fiecare intrare contine si alte informatii de stare, cum ar fi identificatorul ultimului proces care a actualizat intrarea (a trimis ori a primit un mesaj, a atasat o zona comuna de memorie s.a.m.d.), precum si momentul ultimului acces sau al ultimei actualizari a intrarii.

Fiecare mecanism dispune de un apel sistem de tip "control" prin care se primesc informatii despre starea unei intrari, se seteaza starea unei intrari, sau se elibereaza o intrare. Cānd un proces solicita informatii despre starea unei intrari, nucleul verifica daca acesta are drept de citire si apoi copiaza datele din intrare la adresa specificata de utilizator. Īn mod similar, pentru modificarea valorilor dintr-o intrare, nucleul verifica daca identificatorul utilizator al procesului corespunde cu identificatorul utilizatorului, sau cu identificatorul celui care a creat intrarea, sau daca procesul īn cauza este executat de catre superuutilizator dreptul de scriere nu este suficient pentru a se permite modificarea parametrilor unei intrari. Nucleul copiaza datele precizate de catre utilizator īn intrarea din tabela, setānd valorile identificatorilor utilizator si de grup, permisiunile de acces, precum si continutul altor cāmpuri, īn functie de tipul mecanismului de comunicare. Nucleul nu modifica valorile identificatorilor utilizator si de grup apartinānd celui care a creat intrarea, astfel īncāt utilizatorul care a creat intrarea respectiva īsi pastreaza drepturile de control asupra acesteia. Īn sfārsit, un utilizator poate elibera o intrare din tabela, daca este superutilizator, sau daca identificatorul procesului sau corespunde celui din structura de date a intrarii. Nucleul creste numarul de descriptor asociat intrarii, astfel īncāt, la urmatoarea alocare aacesteia el va returna un alt descriptor. Din acest motiv, asa cum s-a explicat mai sus, apelurile sistem vor īntoarce un mesaj de eroare, daca un proces va īncerca sa acceseze o intrare utilizānd unul din vechii descriptori.

10.2.1. Comunicarea īntre procese prin mesaje

Exista patru apeluri sistem pentru comunicarea prin mesaje msgget īntoarce (sau creaza) un descriptor de mesaje, care desemneaza o coada de mesaje, utilizat ca parametru īn celelalte apeluri sistem msgctl dispune de optiuni de setare sau examinare a valorilor parametrilor asociati unui descriptor de mesaje, precum si de o optiune de eliberare a descriptorilor msgsnd trimite un mesaj, iar msgrcv receptioneaza un mesaj.

Sintaxa apelului sistem msgget este urmatoarea

msgqid = msgget(key, flag)

unde msgqid este descriptorul returnat de apelul sistem, iar key si flag au semnificatiile descrise mai sus, īn cazul general al apelurilor de tip "get". Nucleul memoreaza mesajele sub forma unei liste īnlantuite (coada de mesaje) careia īi corespunde un descriptor, si foloseste un descriptor de coada de mesaje -msgqid- ca index īntr-un tabela de antete de cozi de mesaje. Īn plus fata de cāmpul permisiuni asociat oricarei mecanism IPC, mentionat mai sus, structura de date asociata unei cozi de mesaje contine urmatoarele cāmpuri

Pointeri catre primul si ultimul mesaj din lista īnlantuita

Numarul de mesaje si numarul total de octeti de date din lista īnlantuita

Numarul maxim de octeti de date pe care īi poate contine lista

īnlantuita

Identificatorii ultimelor procese care au trimis si, respectiv receptionat,

mesaje

Momentele de timp ale ultimelor operatii msgsnd, msgrcv si msgctl.

Atunci cānd un utilizator invoca apelul sistem msgget pentru a crea un nou descriptor, nucleul cauta īn tabela de cozi de mesaje pentru a vedea daca exista vreuna care sa corespunda cheii specificate. Daca nu exista nici o intrare care sa corespunda cheii, nucleul aloca o noua structura de date de tip coada de mesaje, o initializeaza si returneaza utilizatorului un identificator pentru aceasta noua structura. Daca exista deja o astfel de coada de mesaje, atunci nucleul verifica drepturile de acces si revine din apelul sistem.

Procesele folosesc apelul sistem msgsnd pentru a trimite un mesaj, apelul avānd urmatoarea sintaxa

msgsnd(msgqid, msg, count, flag)

unde msgqid este descriptorul cozii de mesaje, returnat īn mod obisnuit de apelul sistem msgget, msg este un pointer catre o structura de date -controlata de catre utilizator- compusa din tipul mesajului (care poate lua valori īntregi) si un sir de caractere, count specifica dimensiunea īn octeti a sirului de caractere din structura msg, iar flag precizeaza actiunea pe care urmeaza sa o īntreprinda nucleul, īn situatia īn care nu mai are la dispozitie suficient spatiu īn buffer-ele interne de memorie.

algoritmul msgsnd /* trimitere mesaj */

intrari (1)descriptorul cozii de mesaje

(2)adresa structurii de date pentru mesaje

(3)lungimea mesajului

(4)indicatori

iesiri numarul de octeti efectiv trimisi

obtine un antet de mesaj;

copiaza mesajul propriu-zis din spatiul utilizator īn spatiul nucleu;

actualizeaza structura de date:

-insereaza īn lista noul antet de mesaj;

-initializeaza pointerul din antetul de mesaj sa pointeze catre zona de date;

-īnscrie valoarea:-contorului de octeti;

-momentelor de timp;

-identificatorului procesului transmitator;

trezeste toate procesele care asteapta sa citeasca mesaje din coada;

}

Figura 10.4. Algoritmul pentru transmiterea unui mesaj

Īn figura 10.4. nucleul verifica daca procesul care trimite un mesaj are drept de scriere pentru descriptorul respectiv, daca lungimea mesajului nu depaseste limitele sistemului, daca coada de mesaje nu contine prea multi octeti si daca tipul mesajului are o valoare īntreaga si pozitiva. Daca toate conditiile sunt īndeplinite, nucleul aloca spatiu pentru mesaj din spatiul liber pentru mesaje, gestionat de catre o tabela (map) similara celei care gestioneaza spatiul liber de pe dispozitivul de swap si copiaza acolo datele din spatiul utilizatorului. Nucleul aloca un antet de mesaj pe care-l plaseaza la sfārsitul listei īnlantuite de antete, corespunzatoare cozii respective. Īn antet se īnscriu tipul si dimensiunea mesajului, se initializeaza antetul pentru a referi sirul de caractere ce constituie mesajul propriu-zis, iar īn coada de antete se actualizeaza cāmpurile ce contin informatii de ordin statistic (numarul de mesaje si de octeti de date din coada de mesaje, momentele de timp si identificatorul procesului care a trimis mesajul). Apoi, nucleul trezeste procesele care asteptau sosirea unui mesaj īn coada de mesaje. Daca numarul de octeti depaseste limita cozii de mesaje, procesul care trimite mesajul se pune īn asteptare pāna la eliberarea spatiului din coada de mesaje, prin trimiterea altor mesaje deja existente īn coada. Daca procesul a semnalizat nucleului sa nu-l treaca īn asteptare (cu ajutorul indicatorului IPC NOWAIT), atunci el revine imediat din apelul sistem, cu eroare. Figura 10.5 prezinta mesajele dintr-o coada, evidentiind coada de antete, listele īnlantuite de antete de mesaje si pointerii din antetele de mesaje catre zona de date.


Figura 10.5. Structurile de date pentru comunicarea prin mesaje 

Sa consideram programul din figura 10.6 un proces invoca apelul sistem msgget pentru a obtine un descriptor pentru cheia MSGKEY. Procesul stabileste o lungime a mesajului de 256 de octeti, desi foloseste doar primul īntreg, īsi copiaza propriul identificator de proces īn zona de text a mesajului, atribuie valoarea 1 pentru tipul mesajului, apoi invoca apelul sistem msgsnd pentru a trimite mesajul. Vom reveni mai tārziu la acest exemplu.

Procesele receptioneaza mesaje cu ajutorul apelului sistem msgrcv, care are urmatoarea sintaxa

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 sirului de caractere din structura de date msg, type specifica tipul de mesaj pe care utilizatorul doreste sa-l citeasca, iar flag precizeaza ce trebuie sa faca nucleul daca nu exista mesaje īn coada de mesaje. Valoarea īntoarsa de apel, count, reprezinta numarul de octeti de date receptionati efectiv de catre utilizator.

Īn figura 10.7 nucleul verifica daca utilizatorul are drepturile necesare de acces la coada de mesaje. Daca mesajul cerut de utilizator are valoarea din cāmpul type egala cu 0, nucleul va localiza primul mesaj din lista īnlantuita. Daca lungimea mesajului este mai mica sau egala cu dimensiunea ceruta de utilizator, atunci nucleul copiaza mesajul propriu-zis īn structura de date a utilizatorului si īsi actualizeaza īn mod corespunzator structurile interne de date decrementeaza valoarea contorului de mesaje aflate īn lista si numarul de octeti de date din coada de mesaje, īnscrie momentul receptionarii mesajului si identificatorul procesului care a receptionat mesajul, reface lista īnlantuita ca urmare a eliminarii mesajului si elibereaza spatiul īn care a fost memorat mesajul.

ainclude <sys/types.h>

ainclude <sys/ipc.h>

ainclude <sys/msg.h>

adefine MSGKEY 75 /*cheia mesajului=index īn tabela de mesaje */

struct msgform /* descrierea formei mesajului */

;

main()

Figura 10.6. Un proces client

Daca exista procese care doresc sa trimita mesaje si sunt īn asteptare ca urmare a faptului ca nu a existat spatiu suficient īn lista de mesaje, atunci nucleul le trezeste.

Algoritmul msgrcv /* receptionare mesaj */

intrari: (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

iesiri: numarul de octeti din mesajul returnat

/* nu exista nici un mesaj */

if (indicatorul precizeaza ca procesul sa nu treaca īn asteptare)

return (eroare);

sleep(eveniment:sosirea unui mesaj īn coada de mesaje);

goto ciclu;

Figura 10.7. Algoritmul pentru receptionarea unui mesaj

Daca lungimea mesajului este mai mare decāt parametrul maxcount precizat de utilizator, atunci nucleul īntoarce eroare la revenirea din apelul sistem si lasa mesajul īn lista. Daca procesul ignora restrictiile de dimensiune a mesajelor (prin pozitionarea indicatorului MSG NOERROR, atunci nucleul trunchiaza mesajul, īntoarce numarul de octeti cerut si sterge īntregul mesaj din lista.Prin stabilirea corespunzatoare a valorii parametrului type, un proces poate receptiona mesaje de un anumit tip. Daca valoarea este un īntreg pozitiv, atunci nucleul īntoarce primul mesaj de tipul respectiv. Daca valoarea este un īntreg negativ, atunci nucleul cauta tipul cel mai mic ca valoare dintre toate mesajele din coada de mesaje, presupus a fi mai mic sau egal īn valoare absoluta decāt valoarea type, si īntoarce primul mesaj de acest tip. De exemplu, daca o coada contine trei mesaje, ale caror tipuri sunt 3, 2 si respectiv 1, si daca un utilizator cere un mesaj cu valoarea pentru tip egala cu -2, atunci nucleul īntoarce mesajul care are tipul 1, din cadrul cozii de mesaje. Īn toate cazurile, daca nici un mesaj din coada de mesaje nu satisface conditia de tip din cererea de receptionare, nucleul trece procesul īn starea de asteptare, daca acesta nu a specificat revenirea imediata din contextul nucleu prin pozitionarea bitului IPC NOWAIT

Sa privim programele din figurile 3.6 si 3.8. Programul din figura 10.8 prezinta structura unui server care furnizeaza servicii generale proceselor client. De exemplu, acesta (server-ul) poate primi, de la procesele client, cereri de furnizare de informatii dintr-o baza de date procesul server constituie singurul punct de acces la continutul bazei de date, ceea ce face mai usoara rezolvarea problemei consistentei si securitatii datelor. Procesul server creaza o structura de mesaj prin setarea flag-ului, din cadrul apelului mssget, la valoarea IPC CREAT si receptioneaza toate mesajele cu tipul 1 (adica cereri de la procesele client). Procesul server citeste textul mesajului, cauta identificatorul procesului client si seteaza valoarea tipului mesajului care va fi returnat la valoarea identificatorului procesului client.

Īn acest exemplu, procesul server īsi trimite propriul identificator de proces catre procesul client īn cadrul textului mesajului, iar procesul client va receptiona numai mesajele al caror tip este egal cu valoarea identificatorului de proces al serveru-lui. Astfel, procesul server receptioneaza numai mesajele trimise acestuia de catre procesele client, iar procesele client receptioneaza numai mesajele trimise lor de catre procesul server. Procesele coopereaza pentru a stabili canale multiple de comunicare pe o singura coada de mesaje.

Mesajele sunt formate din perechi tip-date, pe cānd datele din fisier formeaza un sir de octeti. Parametrul type permite proceselor sa selecteze, daca se doreste, mesaje de un anumit tip, facilitate de care nu se putea beneficia cu usurinta īn cadrul sistemului de fisiere.

ainclude <sys/types.h>

ainclude <sys/ipc.h>

ainclude <sys/msg.h>

adefine MSGKEY 75

struct msgform

msg;

int msgid;

main()

cleanup() /* rutina pentru tratarea oricarei semnal */

Figura 10.8. Un proces server

Astfel, procesele pot extrage mesaje de un anumit tip din coada de mesaje, īn ordinea īn care acestea sosesc, iar nucleul pastreaza ordinea corecta a acestora. Desi este posibila implementarea, la nivel utilizator, a unei scheme de comunicare prin fisiere, comunicarea prin mesaje ofera aplicatiilor o modalitate mai eficienta de transfer al datelor īntre procese.

Un proces poate inspecta starea unui descriptor de mesaj, īi poate seta valoarea si poate sterge un descriptor de mesaj cu ajutorul apelului sistem msgctl. Sintaxa apelului este:

msgctl(id, cmd, mstatbuf)

unde id identifica descriptorul de mesaj, cmd precizeaza tipul comenzii, iar mstatbuf reprezinta adresa structurii de date utilizator care va contine parametrii de control sau rezultatele unei interogari.

Īntorcāndu-ne la exemplul din figura 10.8 procesul server receptioneaza semnalele si invoca functia cleanup pentru a sterge coada de mesaje din memoria sistemului. Daca procesul server nu intercepteaza semnalele, sau daca primeste un semnal SIGKILL (care nu poate fi interceptat), atunci coada de mesaje va ramāne īn sistem, chiar daca nici un proces nu o mai refera. Īncercarile ulterioare de a crea (īn exclusivitate) o noua coada de mesaje pe baza cheii date, vor esua pāna īn momentul īn care vechea coada va fi stearsa.

10.2.2.Comunicarea īntre procese prin zone de memorie partajata

Procesele pot comunica direct unele cu celelalte prin folosirea īn comun a unor parti din spatiul lor virtual de adrese si prin scrierea si citirea datelor memorate īn memoria partajata. Apelurile sistem pentru manipularea zonelor de memorie partajata sunt similare apelurilor sistem dedicate comunicatiei prin mesaje. Apelul sistem shmget creaza o noua zona regiune de memorie partajata sau īntoarce una existenta, apelul sistem shmat ataseaza logic o regiune de memorie la spatiul virtual de adrese al unui proces, apelul sistem shmdt detaseaza o regiune de spatiul virtual de adrese al unui proces, iar apelul sistem shmctl manipuleaza diversi parametri asociati zonelor de memorie partajata. Procesele scriu si citesc memoria patajata utilizānd aceleasi instructiuni masina pe care le utilizeaza si la scrierea ori citirea din memoria obisnuita. Dupa atasarea unei zone de memorie patajata, aceasta devine parte a spatiului virtual de adrese al procesului, accesibila īn acelasi mod īn care sunt accesibile si celelalte adrese virtuale.

Sintaxa apelului sistem shmget este urmatoarea

shmid = shmget(key, size, flag)

unde size reprezinta numarul de octeti din regiunea de memorie. Nucleul cauta cheia key īn tabela cu zonele de memorie partajata daca gaseste īn aceasta tabela o intrare care sa corespunda cheii si daca drepturile de acces sunt validate, atunci īntoarce descriptorul acelei intrari din tabela. Daca nu gaseste o intrare careia sa-i corespunda cheia key, si daca utilizatorul a dat indicatorului flag valoarea IPC CREAT, īn scopul crearii unei noi regiuni comune de memorie, atunci nucleul verifica daca valoarea parametrului size se situeaza īntre valorile limita minima si maxima de memorie ale sistemului si apoi aloca o structura de date pentru o regiune de memorie, utilizānd algoritmul allocreg. Nucleul memoreaza īn tabela cu zonele de memorie partajata(Shared Memory Table) drepturile de acces, dimensiunea si un pointer catre intrarea din tabela de regiuni (Figura 10.19.) si stabileste valoarea unui indicator pentru a arata ca regiunii nu īi este asociata memorie. Nucleul aloca memorie regiunii (tabele de pagini s.a.m.d.) numai atunci cānd un proces ataseaza regiunea respectiva la spatiul sau de adrese. De asemenea, nucleul stabileste valoarea unui indicator din intrarea īn tabela de regiunilor pentru a indica faptul ca regiunea nu trebuie eliberata atunci cānd ultimul proces care a atasat-o spatiului sau de adrese se termina. Astfel, datele din zona de memorie partajata ramān intacte desi nici un proces nu mai include zona respectiva īn spatiul sau virtual de adrese.

Figura 10.9. Structurile de date implicate īn gestiunea

zonelor de memorie partajata

Un proces ataseaza o regiune de memorie comuna spatiului sau virtual de adrese cu ajutorul apelului sistem shmat, care are urmatoarea sintaxa

virtaddr = shmat(id, addr, flags)

unde id, returnat de apelul sistem shmget anterior, identifica regiunea de memorie partajata, addr reprezinta adresa virtuala unde utilizatorul doreste sa ataseze regiunea de memorie partajata, iar flags specifica daca regiunea este accesibila numai pentru citire (read-only) si daca nucleul trebuie sa rotunjeasca adresa specificata de utilizator.

Valoarea īntoarsa, virtaddr, reprezinta adresa virtuala la care nucleul a atasat regiunea, care nu trebuie sa fie neaparat egala cu valoarea ceruta de catre proces.

La executia apelului sistem shmat nucleul verifica daca procesul are drepturile necesare de acces la regiunea de memorie (Figura 10.10.). Acesta examineaza adresa specificata de catre utilizator daca este zero, atunci nucleul alege o adresa virtuala convenabila.

Algoritmul shmat /* ataseaza o zona de memorie */

intrari: (1)descriptorul zonei de memorie partajata

(2)adresa virtuala la care sa se faca atasarea

(3)indicatori

iesiri: adresa virtuala la care s-a atasat zona de memorie partajata

else /*utilizatorul doreste ca nucleul sa gaseasca o adresa

corespunzatoare*/

nucleul alege o adresa virtuala (daca nu este nici una disponibila īntoarce

eroare);

ataseaza regiunea la spatiul de adrese al procesului(alg. attachreg)

if (regiunea este atasata pentru prima data)

aloca tabele de pagini, memorie pentru regiune (alg. growreg);

return(adresa virtuala la care s-a facut atasarea);

Figura 10.10. Algoritmul de atasare a unei zone de memorie partajata

Zona de memorie partajata nu trebuie sa se suprapuna peste alte regiuni din cadrul spatiului virtual de adrese al procesului, deci atāt amplasarea cāt si dimensiunea ei trebuie alese judicios pentru ca, īn cazul unor modificari ulterioare de dimensiune, alte regiuni sa nu se extinda peste zona de memorie partajata. De exemplu, un proces poate mari dimensiunea regiunii sale de date prin apelul sistem brk, noua regiune de date fiind virtual contigua vechii regiuni de date īn consecinta nucleul nu trebuie sa plaseze zona de memorie partajata īn apropierea regiunii de date. Similar, nucleul nu trebuie sa plaseze zona de memorie partajata īn apropierea vārfului stivei, pentru a preveni astfel fenomenul de crestere a stivei īn interiorul zonei de memorie partajata. De exemplu, daca stiva creste catre valorile mari ale adreselor, cel mai bun loc de amplasare a zonei de memorie partajata ar putea fi imediat īnaintea adresei de īnceput a regiunii de stiva.

Nucleul verifica daca regiunea de memorie partajata īncape īn spatiul de adrese al procesului si o ataseaza, utilizānd algoritmul attachreg. Daca procesul apelant este primul proces care ataseaza regiunea respectiva, atunci nucleul aloca tabelele necesare, utilizānd algoritmul growreg, corecteaza valoarea continuta īn cāmpul momentul ultimei atasari al intrarii corespunzatoare din tabela de memorie partajata, si īntoarce adresa virtuala la care a atasat regiunea respectiva.

Un proces detaseaza o regiune de memorie comuna din spatiul sau virtual de adrese cu ajutorul apelului sistem

shmdt(addr)

unde addr reprezinta adresa virtuala īntoarsa de un apel shmat anterior. Desi ar parea mai logic ca apelul shmdt sa primeasca drept parametru un identificator, se īntrebuinteaza totusi adresa virtuala a regiunii de memorie partajata pentru ca procesul sa poata face distinctie īntre mai multe instante ale unei regiuni de memorie partajata, care sunt atasate la spatiul virtual de adrese al procesului, si pentru ca identificatorul poate fi sters. Nucleul cauta regiunea atasata procesului la adresa virtuala indicata si o detaseaza din spatiul de adrese al procesului, utilizānd algoritmul detachreg. Deoarece tabelele de regiuni nu contin pointeri īnapoi catre tabela cu regiuni de memorie partajata, nucleul cauta īn aceasta tabela intrarea corespunzatoare regiunii si corecteaza valoarea din cāmpul care contine momentul ultimei detasari a regiunii.

Sa consideram programul din figura 10.11. Un proces creaza o regiune de memorie partajata de 128 Ko si o ataseaza de doua ori la spatiul sau de adrese, la doua adrese virtuale diferite. Procesul scrie date īn prima regiune de memorie partajata si citeste date din cea de-a doua

ainclude <sys/types.h>

ainclude <sys/ipc.h>

ainclude <sys/shm.h>

adefine SHMKEY 75

adefine K 1024

int shmid;

main()

cleanup()

Figura 10.11. Atasarea unei regiuni de memorie partajata de doua ori

la spatiul de adrese al unui proces

Figura 10.12. prezinta un alt proces care ataseaza aceeasi regiune (acesta preia numai 64 Ko, pentru a ilustra faptul ca fiecare proces poate atasa zone de memorie partajata de dimensiuni diferite procesul respectiv asteapta pāna cānd primul proces scrie o valoare nenula īn primul cuvānt al regiunii de memorie partajata si apoi citeste continutul acesteia. Primul proces executa un apel pause pentru a permite executia celui de al doilea cānd primul proces intercepteaza un semnal, el sterge regiunea de memorie partajata.

Pentru examinarea starii si stabilirea valorilor parametrilor unei regiuni de memorie partajata, un proces utilizeaza apelul sistem shmctl, care are urmatoarea sintaxa

shmctl(id, cmd, shmstatbuf)

unde id identifica intrarea corespunzatoare din tabela regiunilor de memorie partajata, cmd precizeaza tipul operatiunii, iar shmstatbuf reprezinta adresa unei structuri de date utilizator care contine informatiile de stare din intrarea īn tabela regiunilor de memorie partajata, atunci cānd se face interogarea sau setarea parametrilor de stare.

ainclude <sys/types.h>

ainclude <sys/ipc.h>

ainclude <sys/shm.h>

adefine SHMKEY 75

adefine K 1024

int shmid;

main()

Figura 10.12. Folosirea īn comun a memoriei de catre procese

Nucleul trateaza comenzile de interogare asupra starii si de schimbare a proprietarului si a drepturilor de acces, īn mod asemanator implementarii adoptate pentru comunicarea prin mesaje. Cānd este stearsa o zona de memorie partajata, nucleul elibereaza intrarea corespunzatoare din tabela de regiuni de memorie partajata si examineaza continutul intrarii din tabela de regiuni daca nu mai exista nici un proces care sa aiba atasata regiunea respectiva la spatiul sau de adrese, atunci nucleul elibereaza intrarea din aceasta tabela si toate resursele asociate, utilizānd algoritmul freereg. Daca regiunea mai este īnca atasata la vreun proces (adica valoarea contorului de referinte este pozitiva), atunci nucleul sterge valoarea indicatorului care semnalizeaza ca regiunea nu trebuie sa fie eliberata la detasarea ei de la spatiul de adrese al ultimului proces. Procesele care utilizeaza regiunea de memorie partajata pot continua sa o faca, dar nici un alt proces nu o mai poate atasa. Atunci cānd toate procesele au detasat regiunea respectiva, nucleul o elibereaza. Acest caz este analog celui de la sistemul de fisier, īn care un proces poate deschide un fisier si poate continua sa-l acceseze si dupa executarea apelului sistem unlink.

10.2.3.Comunicarea īntre procese prin semafoare

Semafoarele reprezinta una din solutiile la problema sincronizarii executiei proceselor, propusa de E.W. Dijkstra. Īn continuare se vor prezenta cāteva probleme generale, de ordin teoretic, cu privire la semafoare. Un semafor reprezinta o variabila de tip īntreg care indica numarul de activari salvate pentru viitoarea utilizare a unei resurse. Aceasta variabila poate avea valoarea

-zero, semnificānd faptul ca nu s-au salvat activari pentru viitoarea

utilizare a resursei;

-n, pozitiva, semnificānd faptul ca sunt īn asteptare n activari ale resursei;

Un semafor S este un ansamblu format din variabilele īntregi P(s) si firul de asteptare asociat, f(s). La crearea contorului de resurse, e(s), firul de asteptare f(s) este vid. Pentru a actiona asupra semafoarelor, Dijkstra propune doua primitive, P si V, care implementeaza generic doua actiuni: -activare si asteptare.

Astfel, procedura P(s) este executata de un proces oarecare, r, pentru ocuparea unei resurse si are urmatorul pseudocod

procedura P(s);

Valoarea e(s) are urmatoarele interpretari:

-daca e(s) > 0 atunci el indica numarul de resurse disponibile;

-daca e(s) < 0 atunci el indica numarul de cereri de resurse nesatisfacute;

Procedura V(s) este executata de un proces oarecare, q, pentru eliberarea unei resurse si are urmatorul pseudocod

procedura V(s);

Se pot enumera cāteva proprietati ale semafoarelor

-un semafor nu poate fi initializat cu valori negative, dar poate deveni

negativ dupa un numar de operatii de tip P ;

-daca notam cu : -nP(s)-numarul de operatii P asupra lui S;

-nV(s)-numarul de operatii V asupra lui S;

-eo(s) -valoarea initiala a lui S;

atunci este adevarata relatia: e(s) = eo(s) - nP( s) + nV(s);

-fie nf(s) numarul de procese care, dupa ce au facut P(s), fie nu au fost

blocate, fie au fost blocate, dar au fost ulterior deblocate cu V(s).

Atunci este adevarata relatia: nf(s) nP(s);

-efectul primitivelor P si V asupra lui nf(s) este urmatorul:

- efectul primitivei P(s): nP(s) := nP(s) + 1;

daca nP(s) eo(s) + nV(s)

atunci nf(s) = nf(s) - 1;

- efectul primitivei V(s): nV(s) := nV(s) +1;

daca nP(s) eo(s) + nV(s)

atunci nf(s) = nf(s) + 1;

-executia primitivelor P si V lasa invarianta relatia: nf(s) = min( nP(s), eo(s) + nV(s)) ;

-observatie: - relatia de mai sus spune ca numarul de procese din firul de

asteptare este cel mai mic dintre numarul de cereri de resurse

si numarul de resurse disponibile.

Īn continuare se va prezenta problema producator-consumator, īn care este vorba despre un set de N buffere. Procesul producator completeaza cāte un buffer cu informatii, īn timp ce procesul consumator foloseste aceste informatii golind buffere-le. Pentru implementarea celor doua proceduri se folosesc trei semafoare

plin -pentru numarul de buffere din set ocupate (initial, plin=0)

gol -pentru numarul de buffere din set libere (initial, gol=N)

mutex -pentru protejarea accesului la setul de buffere (initial,

mutex=1). Acesta este un semafor de excluziune mutuala.

procedura PRODUC~TOR();

procedura CONSUMATOR();

Pe lānga aspectele pozitive ale solutiei se pot semnala si unele limite ale metodei

-īn sistemele mari este dificila folosirea semafoarelor;

-exista posibilitatea ca, din cauza unor erori de programare, sa se sara īn mod accidental peste executarea procedurii P, ceea ce duce la accesul neprotejat la regiunile critice;

-īn mod analog, daca se sare peste executia procedurii V, se poate ajunge la fenomenul de blocare;

-īn procesul de compilare nu se poate verifica utilizarea corecta a semafoarelor;

Apelurile sistem pentru lucrul cu semafoare permit proceselor sa-si sincronizeze executia prin efectuarea unor operatii atomice asupra unei multimi de semafoare. Īnainte de implementarea semafoarelor, daca un proces dorea blocarea accesului la o resursa, atunci trebuia sa creeze un fisier de blocare cu ajutorul apelului sistem creat. Apelul creat se īncheie cu eroare daca fisierul exista deja, iar procesul presupune ca un altul a blocat accesul la resursa respectiva. Principalele dezavantaje ale acestei abordari sunt faptul ca procesele nu stiu cānd sa īncerce din nou accesarea resursei si faptul ca fisierele de blocare pot ramāne nesterse atunci cānd sistemul iese īn mod accidental din functiune, la urmatoarea initializare a sistemelor ele continuānd sa blocheze accesul.

Dijkstra a publicat algoritmul Dekker care descrie o implementare a semafoarelor, care dupa cum s-a precizat sunt variabile de tip īntreg pe care sunt definite doua operatii elementare (atomice) P si V. Operatia P decrementeaza valoarea semaforului daca aceasta este pozitiva, iar operatia V incrementeaza valoarea semaforului. Deoarece ambele operatii sunt elementare, īn orice moment cel mult o singura operatie 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 operatiilor P si V propuse de Dijkstra, īn sensul ca se pot executa simultan mai multe operatii asupra unui semafor, si operatiile de incrementare sau decrementare se pot face asupra unor valori mai mari decāt 1. Nucleul executa toate operatiile īn mod atomic nici un alt proces neputānd modifica valoarea semaforului pāna īn momentul īncheierii tuturor operatiilor. Daca nucleul nu poate executa toate operatiile, atunci nu executa nici una, caz īn care procesul trece īn asteptare pāna īn momentul īn care nucleul poate efectua toate operatiile.

Īn cadrul System V, un semafor se compune din urmatoatele elemente

Øvaloarea semaforului

Øidentificatorul ultimului proces care a modificat valoarea semaforului

Ønumarul de procese care asteapta cresterea valorii semaforului

Ønumarul de procese care asteapta ca valoarea semaforului sa fie egala cu 0

Apelurile sistem pentru lucrul cu semafoare sunt semget pentru a crea si pentru a obtine accesul la un set de semafoare, semctl pentru executarea diveritelor operatiuni de control asupra setului de semafoare si semop pentru a modifica valorile semafoarelor.

Figura 10.13. Structurile de date implicate īn lucrul cu semafoare

Apelul sistem semget creaza o lista de semafoare, si are urmatoarea sintaxa

id = semget(key, count, flag)

unde key, id si flag au aceleasi semnificatii ca si parametrii corespunzatori din cadrul mecanismului de comunicare prin memorie patajata. Nucleul aloca o intrare care pointeaza catre o structura de tip lista de semafoare, cu count elemente. (Figura 10.13.). Intrarea mai specifica de asemenea si numarul de semafoare din lista, momentul executarii ultimului apel semop si momentul executarii ultimului apel semctl. De exemplu, apelul sistem semget din figura 10.14. creaza un semafor cu doua elemente.

Procesele opereaza asupra valorii semafoarelor cu ajutorul apelului sistem semop, avānd urmatoarea sintaxa

oldval = semop(id, oplist, count)

unde id este descriptorul īntors de apelul semget, oplist este un pointer catre o lista de operatii asupra semaforului, iar count este dimensiunea listei.Valoarea īntoarsa, oldval, reprezinta valoarea ultimului semafor din set asupra caruia s-au facut operatii, īnainte de operatia respectiva. Formatul fiecarui element din oplist este urmatorul

Ønumarul semaforului, care identifica intrarea corespunzatoare listei de

semafoare pe care se opereaza

Øoperatia propriu-zisa

Øvaloarea indicatorilor

Nucleul citeste lista de operatii asupra semafoarelor, oplist, din cadrul spatiului de adrese utilizator si verifica validitatea numerelor de semafoare si daca procesul are drepturile de acces necesare pentru a citi sau pentru a modifica valoarea semafoarelor (figura 10.15.). Daca procesului nu i se acorda permisiunea, atunci apelul sistem se īncheie cu eroare. Daca, pe timpul executarii operatiilor asupra semafoarelor, nucleul trebuie sa astepte, atunci acesta restaureaza valorile initiale (de la īnceputul executiei apelului sistem) ale semafoarelor pe care deja le-a modificat, trecānd apoi īn asteptare pāna la producerea evenimentului pe care īl asteapta, dupa care reia de la īnceput executia apelului sistem. Astfel, operatiile cu semafoare sunt executate atomic, adica fie toate odata, fie nici una.

Nucleul modifica valoarea unui semafor īn conformitate cu valoarea operatiei. Daca aceasta este pozitiva, atunci nucleul incrementeaza valoarea semaforului si trezeste toate procesele care asteaptau ca valoarea semaforului sa creasca.

Daca valoarea operatiei este zero, atunci nucleul verifica valoarea semaforului daca aceasta este zero, atunci se continua cu celelalte operatii din lista de operatii, altfel nucleul incrementeaza numarul de procese care asteapta ca valoarea semaforului sa devina zero, si trece īn asteptare. Daca valoarea operatiei este negativa, iar īn valoare absoluta este mai mica sau egala cu valoarea semaforului, atunci nucleul aduna acea valoare (negativa) la valoarea semaforului. Daca rezultatul acestei īnsumari este zero, nucleul trezeste toate procesele care asteaptau ca valoarea semaforului sa devina zero. Daca valoarea semaforului este strict mai mica decāt valoarea absoluta a operatiei, atunci nucleul pune procesul īn asteptare pe evenimentul: valoarea semaforului creste. Ori de cāte ori un proces asteapta īn mijlocul executiei unei operatii cu semafoarele, el asteapta la un nivel de prioritate deci se va trezi la primirea unui semnal.

ainclude <sys/types.h>

ainclude <sys/ipc.h>

ainclude <sys/sem.h>

adefine SEMKEY 75

int semid;

unsigned int count;

/* definitia structurii de date sembuf din fisierul sys/sem.h> */

/* struct sembuf */

/* ; */

struct sembuf psembuf,vsembuf; /* operatii corespunzatoare lui P si V */

main(argc, argv)

int argc;

char *argvst;

else if(argvs1ts0t=='a')

else

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++)

}

cleanup()

Figura 10.14. Operatii de blocare si de deblocare

Algoritmul semop   /*operatii asupra semafoarelor */

intrari:(1)descriptorul de semafor

(2)lista de operatii asupra semafoarelor

(3)numarul de elemente din lista de semafoare

iesiri:valoarea initiala a ultimului semafor care a fost modificat

else if(operatia este negativa)

anuleaza toate operatiile deja facute cu semafoarele īn acest

apel sistem(iteratia anterioara);

if(indicatorii specifica sa nu astepte)

return(eroare);

sleep(eveniment:valoarea semaforului creste);

goto start; /* se reia ciclul */

}

else /* valoarea operatiei este zero */

}

}

/* toate operatiile cu semafoare au reusit */

actualizeaza īnregistrarile de timp, identificatorii proceselor;

return(valoarea initiala a ultimului semafor modificat);

Figura 10.15. Algoritmul pentru operarea asupra semafoarelor

Sa consideram programul din figura 10.14, (a.out) si sa presupunem ca un utilizator īl executa de trei ori īn urmatoarea secventa

a.out &

a.out a &

a.out b &

Atunci cānd programul este rulat fara parametri, procesul creaza un set de semafoare cu doua elemente, ale caror valori sunt initializate cu 1. Apoi, procesul executa apelul pause( ) si trece īn starea de asteptare pāna cānd este trezit de un semnal, si sterge semaforul īn cadrul functiei cleanup( ). Atunci cānd programul se executa cu parametrul 'a', procesul (A) face īn cadrul ciclului patru operatii distincte cu semafoarele decrementeaza valoarea semaforului 0, decrementeaza valoarea semaforului 1, executa instructiunea printf si apoi incrementeaza valoarea semaforului 1 si a semaforului 0. Daca un proces īncearca decrementarea valorii unui semafor care are deja valoarea 0, atunci este trecut īn starea de asteptare si īn consecinta semaforul se considera blocat. Deoarece semafoarele au fost initializate cu valoarea 1 si nici un alt proces nu le mai foloseste, procesul A nu va trece niciodata īn starea de asteptare, iar valorile semafoarelor vor bascula īntre valorile 0 si 1. Atunci cānd programul se executa cu parametrul 'b', procesul (B) decrementeaza semafoarele 0 si 1 īn ordinea inversa celei īn care a facut-o procesul A.

Atunci cānd procesele A si B se executa simultan, poate aparea situatia īn care procesul A a blocat semaforul 0 si doreste sa blocheze semaforul 1, iar procesul B a blocat semaforul 1 si doreste sa blocheze semaforul 0. Īn acest caz, ambele procese trec īn starea de asteptare, fiind īn imposibilitatea de a-si continua executia. Ele s-au blocat reciproc si īsi īncheie executia numai la primirea unui semnal.

Pentru a evita astfel de probleme, procesele pot executa simultan mai multe operatiuni asupra semafoarelor. Acest rezultat se poate obtine folosind urmatoarele date si instructiuni

struct sembuf psembufs t

psembufs t.sem num=0;

psembufs t.sem num=1;

psembufs t.sem op=-1;

psembufs t.sem op=-1;

semop(semid, psembuf, 2);

Structura psembuf reprezinta o lista de operatii care decrementeaza simultan semafoarele 0 si 1. Daca vreuna din operatii nu se poate executa, atunci procesul intra īn starea de asteptare pāna cānd ambele operatii se pot executa atomic. De exemplu, daca valoarea semaforului 0 este 1 si valoarea semaforului 1 este 0, atunci nucleul va lasa ambele valori nemodificate pāna īn momentul īn care le poate decrementa pe amāndoua.

Un proces poate activa indicatorul IPC NOWAIT īn cadrul apelului sistem semop daca nucleul ajunge īn situatia īn care procesul trebuie sa treaca īn starea de asteptare pāna cānd valoarea semaforului creste sau devine egala cu zero, atunci nucleul revine din apelul sistem cu eroare. Astfel, este posibila realizarea unui semafor conditional, la care un proces sa nu treca īn starea de asteptare daca nu poate executa operatia atomic.

Situatii periculoase pot aparea atunci cānd un proces executa o operatie asupra unui semafor, probabil pentru blocarea unei resurse, si apoi īsi īncheie executia fara sa restabileasca valoarea initiala a semaforului. Astfel de situatii pot aparea ca rezultat al unei greseli de programare, sau ca rezultat al receptionarii unui semnal care determina īncheierea imediata a executiei unui proces. Daca, īn figura 10.14., procesul receptioneaza un semnal kill dupa decrementarea valorilor semafoarelor, el nu va mai putea sa le incrementeze din nou, deoarece semnalele kill nu pot fi tratate explicit sau ignorate. Īn consecinta, alte procese vor gasi semaforul blocat chiar daca procesul care a realizat blocarea nu mai exista. Pentru a evita asemenea probleme, un proces poate seta valoarea indicatorului SEM UNDO īn cadrul apelului sistem semop atunci cānd procesul īsi īncheie executia, nucleul inverseaza efectul fiecarei operatii cu semafoare pe care a facut-o procesul respectiv. Pentru a implementa aceasta facilitate, nucleul memoreaza o tabela care contine o intrare pentru fiecare proces din sistem. Fiecare intrare indica un set de structuri undo, cāte una pentru fiecare semafor folosit de catre proces (Figura 10.16.). Fiecare structura undo este o lista de tripleti care contin un identificator de semafor, un numar de semafor din setul determinat de identificatorul de semafor si o valoare de corectie.


Figura 10.16. Structurile Undo pentru semafoare

Atunci cānd un proces executa primul sau apel semop, avānd setat indicatorul SEM UNDO, nucleul aloca dinamic structuri de tip undo. La urmatoarele apeluri semop care seteaza indicatorul SEM UNDO, nucleul cauta printre structurile de tip undo pe aceea care contine acelasi indentificator de semafor si acelasi numar ca si cele din apelul semop daca gaseste o asemenea structura, atunci nucleul scade valoarea operatiei din valoarea de corectie. Astfel, structura undo va contine suma cu semn schimbat a valorilor tuturor operatiilor pe care procesul le-a executat asupra semaforului pentru care a fost setat indicatorul SEM UNDO. Daca nu exista o asemenea structura pentru semafor, atunci nucleul va crea una, gestionānd totodata o lista de structuri de tip undo ordonate dupa identificatorul de semafor si dupa numar. Daca o valoare de corectie devine zero, atunci nucleul elimina structura de tip undo care o contine. Atunci cānd un proces īsi īncheie executia, nucleul apeleaza o rutina speciala care parcurge toate structurile undo asociate procesului modificānd semafoarele corespunzatoare.

Revenind la figura 10.14., nucleul creaza o structura undo ori de cāte ori un proces decrementeaza valoarea unui semafor si elimina o structura de tip undo ori de cāte ori procesul incrementeaza valoarea unui semafor, deoarece valoarea de corectie din structura undo devine zero.

Figura 10.17. Secventa de structuri Undo

Figura 10.17 prezinta succesiunea evolutiei structurilor de tip undo īn cazul executiei programului cu parametrul 'a'. Dupa prima operatie, procesul contine o singura structura de tip undo corespunzatoare numarului de semafor 0 si valorii de corectie 1, iar dupa cea de-a doua operatie procesul contine si o doua structura corespunzatoare numarului de semafor 1 si valorii de corectie 1. Daca procesul si-ar īncheia executia īn acest moment, atunci nucleul ar parcurge fiecare structura, adaugānd valoarea 1 la fiecare semafor, readucāndu-le la valoarea la zero. Īn mod obisnuit, nucleul decrementeaza valoarea de corectie a semaforului 1 pe timpul celei de-a treia operatii, corespunzator cresterii valorii semaforului, si elimina structura, deoarece valoarea sa de corectie a devenit zero. Dupa cea de-a patra operatie, procesul nu mai contine nici o structura de tip undo, deoarece toate valorile de corectie vor fi zero.

Operatiile din lista de operatii asupra semafoarelor permit proceselor sa evite blocarile, dupa cum a fost exemplificat mai sus, dar sunt complicate si majoritatea aplicatiilor nu au nevoie de īntreaga lor capacitate. Aplicatiile care necesita utilizarea mai multor semafoare vor trebui sa rezolve problema conditiilor de blocare reciproca a proceselor la nivel utilizator, pentru ca nucleul sa nu contina astfel de apeluri sistem complicate.

Apelul sistem semctl contine o multime de operatii de control relative la semafoare, si are urmatoarea sintaxa

semctl(id, number, cmd, arg)

Parametrul arg este declarat de tip union

union semunion

arg;

Nucleul interpreteaza parametrul arg pe baza valorii parametrului cmd, īn mod asemanator celui īn care interpreteaza comenzile ioctl. Efectele comenzii se produc pentru valori ale parametrilor cmd care restaureaza sau seteaza parametrii de control (drepturile de acces si alti parametri), initializeaza una sau toate valorile semafoarelor dintr-un set de semafoare, sau realizeaza citirea valorilor semafoarelor. Pentru comanda de stergere a unui semafor, IPC RMID, nucleul gaseste toate procesele care au structuri de tip undo pentru semaforul respectiv si sterge aceste structuri. Apoi, reinitializeaza structura de date pentru semafoare si trezeste toate procesele care asteapta producerea unui eveniment relativ la semafoare. Atunci cānd procesele īsi reiau executia, vor constata ca identificatorul de semafor nu mai este valabil si vor īntoarce o eroare catre apelant.

10.2.4.Concluzii generale

Exista cāteva asemanari īntre sistemul de fisiere si mecanismele de comunicare īntre procese (mecanismele IPC). Apelurile sistem "get" sunt similare apelurilor sistem creat si open, iar apelurile sistem "control" contin o optiune de stergere a descriptorilor din sistem, ca si apelul sistem unlink. Dar nu exista nici o operatiune analoaga apelului sistem close, din cadrul sistemului de fisiere. Astfel, nucleul nu are o evidenta a proceselor care pot accesa vreunul din mecanismele de comunicare, si īntradevar, procesele pot avea acces la unul din mecanismele de comunicare, cu conditia sa cunoasca identificatorul corect si sa īndeplineasca restrictia privind drepturile de acces, desi aceste procese nu au invocat vreodata un apel sistem de tip "get". Nucleul nu poate sterge automat structurile de date ale mecanismelor de comunicare, deoarece el nu stie daca acestea mai sunt sau nu folosite. Īn acest fel, procesele care nu sunt programate foarte minutios pot lasa la voia īntāmplarii, dupa īncheierea executiei lor, strucuri de date care nu mai sunt necesare nimanui si, īn consecinta, care nu mai sunt folosite de nimeni. Cu toate ca, dupa īncheierea executiei unui proces, nucleul poate salva informatiile de stare si datele īn cadrul structurilor de date ale mecanismelor de comunicare, totusi este mai indicata folosirea fisierelor īn acest scop.

Mecanismele de comunicare īntre procese introduc un termen nou, chei, īn locul arhicunoscutelor si traditionalelor fisiere. Este dificila extinderea semanticii cheilor la nivelul unei retele, deoarece acestea pot descrie obiecte diferite de pe masini diferite. Pe scurt, cheile au fost gāndite pentru a opera īntr-un mediu concentrat (de tip "o singura masina"). Numele de fisiere sunt mai potrivite pe un mediu distribuit. Utilizarea cheilor īn locul numelor de fisiere, mai implica si faptul ca facilitatile IPC sunt entitati date ca atare, folositoare īn cadrul aplicatiilor dedicate, dar carora le lipsesc posibilitatile de construire de instrumente, esentiale īn cazul fisierelor si al pipe-urilor, de exemplu. Mare parte din functionalitatea lor poate fi emulata prin folosirea celorlalte facilitati ale sistemului, astfel ca, din punctul de vedere al elegantei īn lucru, ele nu ar trebui incluse īn nucleu. Totusi, īn cazul aplicatiilor care coopereaza, ele asigura performante superioare fata de facilitatile oferite de sistemul de fisiere.

10.3 Comunicare prin socket-uri

Modul īn care pot comunica procesele care se executa pe masini diferite difera īn functie de protocol si de mediul de transmisie. Mai mult, aceste metode nu pot permite comunicarea īntre procese de pe aceeasi masina, deoarece ele presupun existenta unui proces server care se afla īn asteptare īn cadrul unui apel sistem open sau read, relativ la un driver. Pentru a furniza metode generale de comunicare īntre procese si pentru a permite utilizarea protocoalelor de retea sofisticate, sistemul BSD pune la dispozitie un mecanism cunoscut sub numele de comunicare prin socket-uri. Īn acest subcapitol sunt descrise unele aspecte, la nivel utilizator, ale socket-urilor.

Structura acestui mecanism la nivelul nucleului, contine trei parti nivelul socket, nivelul protocol si nivelul dispozitiv (Figura 10.18). Nivelul socket furnizeaza interfata dintre apelurile sistem si nivelurile inferioare, nivelul protocol contine modulele de protocoale folosite pentru comunicatie (īn figura, TCP si IP), iar nivelul dispozitiv contine driverele de care controleaza lucrul dispozitivelor de retea. La configurarea sistemului se precizeaza combinatiile de protocoale acceptate si driverele, metoda care nu este la fel de flexibila ca lucrul cu stream-uri. Procesele comunica dupa modelul client-server un proces server asculta la un socket, unul din capetele liniei bidirectionale de comunicatii, iar procesul client comunica cu serverul prin alt socket, adica celalalt capat al liniei, care poate fi pe o alta masina. Nucleul pastreaza conexiunile interne si directioneaza datele de la client catre server.

Figura 10.18. Modelul comunicarii prin socket-uri

Socket-urile care au aceleasi proprietati de comunicatie, cum ar fi conventiile de nume si formatele de adresa, sunt grupate īn domenii. Sistemul BSD 4.2 suporta domeniul UNIX de comunicare īntre procesele de pe aceeasi masina, si domeniul Internet de comunicare īntre procese de pe masini diferite prin retea utilizānd protocoalele de comunicatie DARPA(Defense Advanced Research Project Agency). Fiecare socket are un tip, si anume circuit virtual (stream socket, īn terminologia Berkeley) sau datagrama. Un circuit virtual permite accesul secvential si functionarea corecta a datelor. Datagramele nu garanteaza accesul secvential la date si integritatea acestora, īn schimb sunt mai putin costisitoare decāt circuitele virtuale, pentru ca nu necesita operatiuni de initializare deosebite prin urmare ele sunt utile pentru anumite tipuri de comunicari. Sistemul contine un protocol implicit pentru fiecare combinatie acceptata de tip domeniu-socket. De exemplu, protocolul TCP (Transport Connect Protocol) furnizeaza serviciul de circuit virtual, iar protocolul UDP (User Datagram Protocol) furnizeaza serviciul de datagrame, īn domeniul Internet.

Mecanismul de comunicare prin socket-uri contine cāteva apeluri sistem. Apelul sistem socket stabileste punctul final al unei legaturi de comunicatie.

sd = socket(format, type, protocol);

unde parametrul format precizeaza domeniul de comunicatie (domeniul UNIX, sau domeniul Internet), type indica tipul de comunicatie prin socket (circuit virtual, sau datagrama), iar protocol precizeaza un anumit protocol pentru controlul comunicatiei. Īn cadrul altor apeluri sistem, procesele vor īntrebuinta descriptorul de socket, sd. Apelul sistem close īnchide socket-urile.

Apelul sistem bind asociaza un nume descriptorului de socket

bind(sd, address, length)

unde sd este descriptorul de socket, iar address este adresa unei structuri care precizeaza un indicator al domeniului si protocolului de comunicatie, precizate īn cadrul apelului sistem socket. Parametrul length reprezinta lungimea structurii de date address fara acest parametru nucleul nu ar sti cāt de lunga este adresa, deoarece lungimea acesteia poate diferi de la un domeniu (sau protocol) la altul. De exemplu, īn cadrul domeniului UNIX, o adresa este un nume de fisier. Procesul server ataseaza adresele din apelul bind unor socket-uri si face publice numele lor pentru a fi identificate de catre procesele client.

Apelul sistem connect cere nucleului sa faca o conexiune cu un socket existent

connect(sd, address, length)

unde semnificatia parametrilor este aceeasi ca la apelul bind, cu deosebirea ca parametrul address reprezinta adresa socket-ului destinatie care va constitui celalalt capat al liniei de comunicatie. Ambele socket-uri trebuie sa foloseasca acelasi domeniu si protocol de comunicatie, ramānānd īn sarcina nucleului initializarea corecta a legaturilor de comunicatie. Daca tipul socket-ului este datagrama, atunci apelul connect informeaza nucleul asupra adresei de utilizat īn cadrul apelurilor send ulterioare prin socket-ul respectiv īn momentul apelului nu se realizeaza nici o legatura.

Atunci cānd un proces server accepta legaturile printr-un circuit virtual, nucleul trebuie sa puna īntr-o coada de asteptare cererile care sosesc, pāna īn momentul īn care va putea sa le satisfaca. Apelul sistem listen precizeaza lungimea maxima a cozii de asteptare

listen(sd, qlength)

unde sd este descriptorul de socket si qlength reprezinta numarul maxim de cereri care vor fi luate īn consideratie.

Figura 10.19. Acceptarea unui apel de catre un proces server

Apelul sistem accept primeste cererile de conectare la un proces server

nsd = accept(sd, address, addrlen)

unde sd este descriptorul de socket, address indica o zona de date utilizator pe care nucleul o completeaza cu adresa de retur a procesului client care se conecteaza, iar addrlen precizeaza dimensiunea acestei zone. La revenirea din apelul accept, nucleul scrie īn addrlen un numar care semnifica dimensiunea spatiului ocupat īn zona de date. Apelul accept īntoarce un nou descriptor de socket, nsd, diferit de descriptorul sd. Un proces server poate continua sa asculte la socket-ul anuntat, īn timp ce comunica cu un proces client pe un canal separat de comunicatie (Figura 10.19).

Apelurile sistem send si recv permit transferul datelor printr-un socket. Sintaxa apelului sistem send este

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

unde sd este descriptorul de socket, msg este un pointer catre datele care urmeaza sa fie transmise, length reprezinta lungimea datelor de transmis, iar count este numarul de octeti efectiv transmisi. Parametrului flags i se poate atribui valoarea SOF OOB pentru a realiza o transmitere out-of-band a unor date, īntelegānd prin aceasta ca datele trimise nu fac parte din schimbul obisnuit de date īntre procesele care comunica. De exemplu, un program de deschidere de sesiune la distanta poate trimite un mesaj out-of-band pentru a simula apasarea tastei Del de catre un utilizator, la un terminal. Sintaxa apelului sistem recv este

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

unde buf este locul unde se memoreaza datele care sosesc, length este lungimea asteptata a datelor, iar count este numarul de octeti efectiv copiati īn programul utilizatorului. Parametrul flags poate primi valoarea peek , pentru un mesaj care soseste, īn scopul examinarii continutului sau fara a-l scoate din coada de asteptare, sau valoarea out-of-band pentru cazul explicat mai sus. Versiunile pentru datagrame ale acestor apeluri sistem, sendto si recvfrom, au īn plus parametri pentru adrese. Procesele pot utiliza apelurile sistem read si write īn cazul circuitelor virtuale, īn locul apelurilor send si recv, dupa initializarea legaturii. Astfel, procesele server se pot īngriji de negocierea protocoalelor de retea si de crearea proceselor care utilizeaza numai apeluri sistem read si write, ca si cānd acestea ar lucra cu fisiere obisnuite.

Apelul sistem shutdown īnchide o conexiune cu un socket

shutdown(sd, mode)

unde mode indica daca partea care trimite datele, partea care le receptioneaza, sau ambele parti īncheie transmisia datelor. Acest apel informeaza protocoalele de nivel inferior sa īncheie comunicarea prin retea, īnsa cu mentinerea intacta a descriptorilor de socket-uri. Apelul sistem close elibereaza si descriptorul de socket.

Apelul sistem getsockname furnizeaza numele asociat unui socket, printr-un apel bind anterior

getsockname(sd, name, length)

Apelurile getsockopt si setsockopt obtin si respectiv seteaza diferite optiuni asociate socket-ului, īn concordanta cu domeniul si protocolul de comunicatie prin socket.

Sa consideram programul server din figura 10.20. Procesul creaza un circuit virtuak īn cadrul domeniului UNIX si printr-un apel bind īi asociaza numele sockname. Apoi invoca apelul sistem listen, pentru a preciza lungimea cozii de asteptare pentru mesajele care sosesc si intra īntr-o bucla, asteptānd cererile care sosesc.

Apelul accept asteapta pāna cānd protocolul de nivel inferior notifica sosirea unei cereri de legatura prin socket-ul care are numele respectiv apoi, apelul accept īntoarce un nou descriptor pentru cererea care soseste.

Procesul server creaza un proces care sa comunice cu procesul client procesele parinte si fiu īsi īnchid descriptorii initiali pentru a evita interferarea cu comunicarile celorlalte procese. Procesul fiu īsi continua dialogul cu procesul client, īncheindu-si executia (īn acest exemplu) dupa apelul read. Procesul server reintra īn bucla si asteapta o alta cerere de legatura, īn cadrul apelului accept.

ainclude <sys/types.h>

ainclude <sys/socket.h>

main()

close(ns);

}

Figura 10.20. Un proces server din cadrul domeniului UNIX

Figura 10.21 prezinta procesul client corespunzator procesului server. Procesul creaza un un socket īn acelasi domeniu ca si serverul si emite o cerere connect pentru numele sockname, asociat unui socket anume de catre procesul server. La iesirea din apelul connect, procesul client dispune de un circuit virtual catre un proces server. Īn acest exemplu, procesul client scrie un singur mesaj si īsi īncheie executia. Daca procesul server furnizeaza servicii proceselor dintr-o retea, atunci īn cadrul apelurilor sale sistem se specifica faptul ca socket-ul apartine domeniului Internet, prin socket(AF INET, SOCK STREAM, 0) si i se asociaza o adresa de retea obtinuta dintr-un nume de server. Sistemul BSD dispune de apeluri de biblioteca, care īndeplinesc aceste functii.

ainclude <sys/types.h>

ainclude<sys/socket.h>

main()

Figura 10.21 Un proces client din cadrul domeniului UNIX

Similar, al doilea parametru al apelului connect facut de procesul client, trebuie sa contina informatia de adresa necesara identificarii masinii īn retea (sau adresa de rutare pentru transmiterea mesajelor la masina de destinatie, prin masini intermediare), precum si informatii suplimentare de identificare a unui anumit socket de pe masina de destinatie. Daca procesul server doreste sa asculte atāt īn retea, cāt si la procesele locale, el trebuie sa foloseasca doua socket-uri si apelul sistem select pentru a determina care din procesele client se conecteaza.


Document Info


Accesari: 2631
Apreciat: hand-up

Comenteaza documentul:

Nu esti inregistrat
Trebuie sa fii utilizator inregistrat pentru a putea comenta


Creaza cont nou

A fost util?

Daca documentul a fost util si crezi ca merita
sa adaugi un link catre el la tine in site


in pagina web a site-ului tau.




eCoduri.com - coduri postale, contabile, CAEN sau bancare

Politica de confidentialitate | Termenii si conditii de utilizare




Copyright © Contact (SCRIGROUP Int. 2024 )