Sunteți pe pagina 1din 5

Notiuni introductive

Firele de executie fac trecerea de la programarea secventiala la programarea concurenta. Un program secvential reprezinta modelul clasic de program: are un inceput, o secventa de executie a instructiunilor sale si un sfarsit. Cu alte cuvinte, la un moment dat programul are un singur punct de executie. Un program aflat in executie se numeste proces. Un sistem de operare monotasking, cum ar fi fi MS-DOS, nu este capabil sa execute decat un singur process la un moment dat, in timp ce un sistem de operare multitasking, cum ar fi UNIX sau Windows, poate rula oricate procese in acelasi timp (concurent), folosind diverse strategii de alocare a procesorului fiecaruia dintre acestea. Am reamintit acest lucru deoarece notiunea de fir de executie nu are sens decat in cadrul unui sistem de operare multitasking. Un fir de executie este similar unui proces secvential, in sensul ca are un inceput, o secventa de executie si un sfarsit. Diferenta dintre un fir de executie si un proces consta in faptul ca un fir de executie nu poate rula independent ci trebuie sa ruleze in cadrul unui proces. Un program isi poate defini insa nu doar un fir de executie ci oricate, ceea ce inseamna ca in cadrul unui proces se pot executa simultan mai multe fire de executie, permitand efectuarea concurenta a sarcinilor independente ale acelui program. Un fir de executie poate fi asemanat cu o versiune redusa a unui proces, ambele ruland simultan si independent pe o structura secventiala formata de instructiunile lor. De asemenea, executia simultana a firelor in cadrul unui proces este similara cu executia concurenta a proceselor: sistemul de operare va aloca procesorul dupa o anumita strategie fiecarui fir de executie pana la terminarea lor. Din acest motiv firele de executie mai sunt numite si procese usoare. Care ar fi insa deosebirile intre un fir de executie si un proces ? In primul, rand deosebirea majora consta in faptul ca firele de executie nu pot rula decat in cadrul unui proces. O alta deosebire rezulta din faptul ca fiecare process are propria sa memorie (propriul sau spatiu de adrese) iar la crearea unui nou proces (fork) este realizata o copie exacta a procesului parinte: cod si date, in timp ce la crearea unui fir nu este copiat decat codul procesului parinte, toate firele de executie avand acces la aceleasi date, datele procesului original. Asadar, un fir mai poate fi privit si ca un context de executie in cadrul unui proces.

Fire de executie
Firele de executie sunt niste fluxuri de instructiuni care se executa in interiorul unui proces si pot fi vazute ca un program ce nu are spatiu de adresa propriu desi se afla in executie. Ele ruleaza in cadrul unui proces si ii partajeaza spatiu de adresa al acestuia. Fiecare proces are cel putin un fir de executie care ruleaza in interiorul sau. Uneori exista mai multe fire de executie in interiorul aceluiasi proces cand apare nevoia de lucru in paralel asupra acelorasi date. Desi firele de executie partajeaza spatiul de adresa al aceluiasi proces, ele au nevoie de resurse individuale cum ar fi stiva, registrii si contorul program care permit mai multor fire de executie din cadrul aceluiasi proces sa urmeze cai diferite de instructiuni sunt utile in multe privinte, insa uzual ele sunt folosite pentru executarea unor operatii consumatoare de timp fara a bloca procesul principal: calcule matematice, asteptarea eliberarii unei resurse, desenarea componentelor unei aplicatii GUI, etc. De multe ori ori, firele isi desfasoara activitatea in fundal insa, evident, acest lucru nu este obligatoriu.

In Linux procesul init este parintele tuturor proceselor ce ruleaza pe acel sistem. Toate procesele care au rezultat dintr-un parinte se numesc procese fiu. Fiecare proces din sistem are un proces care l-a creat, numit proces parinte (sau tata) si de la care "mosteneste" un anumit ansamblu de caracteristici (cum ar fi proprietarul, drepturile de acces, s.a.), si poate crea, la randul lui, unul sau mai multe procese fii. Fiecare proces are asignat un PID (process identification), ce este un numar intreg pozitiv si care este unic pe durata vietii acelui proces (In orice moment, nu exista In sistem doua procese cu acelasi PID). In UNIX pentru a afla PID-ul unui proces fiu utilizata functia fork() care ii este adresata procesului parinte. In programe, de regula parinte, PID-ul poate fi folosit ca atribut de catre functii de control (waitpid() sau kill()) pentru a executa anumite actiuni asupra procesului specificat. Daca sistemul de operare are implementat suport pentru procfs, atunci in fisierul /proc/pid se gasesc informatii despre procese. In Linux procesul idle denota ca un process nu se mai termina niciodata avand un PID 0 iar init process nu va mai face nimic asteptand terminarea procesului fiu sa i se dea KILL avand un PID 1 din acest process se trag toate procesele din acel sistem. In Windows folosim procese echivalente numite System Idle Process si System.

Procese
Procesul are in componenta sa mai multe fire de executie care sunt executate in paralalel avand in comun toate resursele caracterisiticile si resursele principale a procesului, fire de executie ruleaza in paralel si au acces la aceasi zona de date dar executa portiuni diferite de cod. Aceasta punere in comun a zonei de date are ca si consecinta faptul ca firele de executie vad toate variabile procesului si odata cu modificarea unui fir fiind vazuta si de celelante fire. Un aspect foarte important al proceselor este ca acestea au intotdeauna spatii de adresa distincte, fiecare dintre ele ruland ca si cum toata memoria ar fi a sa. Din aceasta rezulta ca procesele sunt izolate intre ele si nu se pot accesa unul pe celalalt in mod direct. Totusi exista comunicare intre ele prin mecanisme IPC (Inter-Process Communication) precum: memorie partajata, socketuri, coada de mesaje sau semafoare. Pentru a se folosii eficient un procesor, acesta trebuie sa execute concomitent mai multe procese. Deoarece un procesor nu poate sa execute la un moment dat decat un singur proces, fiecarui proces activ in acel moment ii este alocata o bucata de timp (time slice) prin intermediul unui planificator (scheduler). Daca un proces, de exemplu, foloseste o operatie I/O pentru o perioada indelungata si nu pune procesorul in mod idle intr-un anumit interval, se porneste un cronometru care, atunci cand expira, intrerupe procesul consumator de resurse si aloca resursele unui nou proces. Astfel este asigurat faptul ca un proces nu poate sa detina monopol asupra procesorului. Din ce am zis mai sus rezulta doua idei fundamentale despre procese: Ruleaza independent pentru ca au zone de cod, stiva si date distinct Trebuie sa fie planificate la executie astfel ca ele sa ruleze in paralel la nivelul aplicatiei

Functionalitatea unui fir de executie


Firele de executie in comparatie cu programarea modularizata duce la o programare flexibila si aduce o imbunatatire in timp, prin incapsulare se reduce accesul utilizatorului direct la varibile(aflandu-se la in spatii de adrese diferite) si odata cu acestea are loc o securitatea mai buna. In timp ce firele de executie au un dezavantaj deoare acestea folosesc bucati de cod si memori comune, o eroare poate duce la propagarea in mai multe directi si odata cu acestea o vulnerabilitate in sistem. Un avantaj net superior al firelor de executie in fata proceselor este paralelismul. Acest avantaj consta in posibilitatea de folosire mult mai eficienta a resurselor sistemului pentru o prelucrare mult mai rapida a datelor.

Process switching
Process switching este operatia prin care trebuie comutate toate resursele care apartin proceselor, salvalvarea registrului procesorului si se face o remaparea a zonelor de memorie care apartin noului proces. La nivelul sistemului de operare, executia in paralel a firelor de executie este obtinuta in mod asemanator cu cea a proceselor, realizandu-se o comutare intre fire, conform unui algoritm de planificare. Spre deosebire de cazul proceselor, insa, aici comutarea poate fi facuta mult mai rapid, deoarece informatiile memorate de catre sistem pentru fiecare fir de executie sunt mult mai putine decat in cazul proceselor, datorita faptului ca firele de executie au foarte putine resurse proprii. Practic, un fir de executie poate fi vazut ca un numarator de program, o stiva si un set de registri, toate celelalte resurse (zona de date, identificatori de fisier etc) apartinand procesului in care ruleaza si fiind exploatate in comun.

Kernel
Kernel este mediatorul intre programe si hardware, locul unde sunt depozitate driverele. Kernel-ul ofera suport pentru filtrarea pachetelor ce trec prin retea si gestioneaza procesele ce ruleaza in memorie. Functiile kernel-ului sunt foarte ample, iar scopul acestui turorial este sa trateze in linii mari subiectul. Exista mai multe tipuri de kernel: monolitice, hibride, microkerneluri, nanokerneluri si exokerneluri. Kernel-ul Linux-ului este modular. Asta inseamna ca unele drivere pot fi compilate sub forma de module. Avantajul obtinut astfel consta in faptul ca driverul va sta in memorie numai cand este necesar. Asta inseamna o functionare mai rapida a sistemului si o boot-are mai rapida. De aceea, este bine sa folositi module pentru driverul de placa de retea, placa de sunet, partitii vfat, dos, NTFS, smbfs, iptables si multe alte drivere care ofera posibilitatea de a fi compilate ca modul. Datorita utilitarului kerneld, introdus odata cu versiunea 1.3 a kernel-ului, modulele sunt incarcate in mod automat cand sunt solicitate de un program, asta insemnand ca nu mai e nevoie de comanzi ca insmod si modprobe. Kernelul stocheaza lista de procese intr-o lista circulara dublu inlantuita numita task list (lista de procese). Fiecare element din task list este un descriptor al fiecarui proces si contine toate informatiile despre respectivul proces. Descriptorul de proces este de tipul struct task_struck care este definit in linux/sched.h. [1] Fiecare proces este reprezentat in memorie printr-un context= descriptor de procescare contine:

Starea procesului Adresa instructiunii urmatoare Starea registrilorCPU Inf relative la planficare: prioritatea. Informatii relative la paginile de memorie alocate. Informatii legate de contabilizarea res. Utilizate Caracteristici I/O lista fisierelor deschise. Un descriptor de fisiere este un index pentru intrarile dintr-o structura de date ce este rezidenta in kernel si contine detalii despre toate fisierele deschise de catre sistem. Acest descriptor este necesar pentru ca in acelasi moment doua aplicatii sa nu acceseze acelasi fisier si astfel sa se ajunga la coruperi de fisiere sau la pierderi de date. Exista trei valori pentru descriptorul de fisiere: #0 Standard input (stdin); #1 Standard output (stdout); #2 Standard error (stderr).

Starile proceselor
1. Ready (Pregatit pentru executie) 2. Running (In executie) Procesul este pregatit pentru executie daca, cu toate ca

instructiunile sale sunt gata pentru a fi executate, este lasat intr-o coada de asteptare din cauza ca un alt proces este in executie la momentul respectiv de timp. 3. Waiting/Blocked (asteapta/este blocat) Un proces poate fi blocat deoarece in setul sau de instructiuni exista instructiunea de suspendare a executiei sau pentru ca efectueaza o operatie in afara procesorului (adresare memorie, etc) care este foarte consumatoare de timp. 4. Dead (Kill) are loc atuncea cand utilizatorul/programul are o eroare in executie si este nevoita intreruperea firului de exectie. Acesta stare nu este reversibila fata de celelante 3 stari enumeratate mai sus Procesele care se gasesc in Starile Ready si Blocked sunt introduse in cozi de procese: procesele Ready sunt introduse in coada Ready, procesele Blocked sunt introduse in coada Blocked care sunt cozi de I/O. Procesele Ready si Running, Blocked poate fi reflexive adica se poate produce o trecere Ready->Running sau Ready->Blocked si vice versa, iar executia lor poate fi planificata.

Comunicarea intre procese


Comunicarea intre procese se poate face folosind pipe-uri (conducte). Conducta este o cale de legatura care poate fi stabilita intre doua procese inrudite. Ea, bineinteles, are doua apete: unul in care se pot scrie date si altul prin care datele pot fi citite. Pipe-urile permit o comunicare unidirectionala. Sistemul de operare permite conectarea a unuia sau mai multor procese la fiecare din capetele unui pipe, deci este posibil sa existe mai multe procese care scriu, respectiv mai multe procese care citesc din pipe. Astfel se formeaza comunicarea intre procesele care scriu si procesele care citesc din pipe-uri. Comunicarea intre procese se poate realiza si folosind semnalele (o exprimare a evenimentelor care apar asincron in sistem). Un proces este capabil de a genera sau a primi

semnale. In cazul in care un proces primeste un semnal, el poate alege sa reactioneze la semnalul receptionat intr-unul dintre urmatoarele moduri: 1. sa execute actiunea implicita la primirea unui semnal. 2. sa capteze semnalul si sa-l trateze (signal handler) 3. sa ignore semnalul respectiv Semnalele pot fi de mai multe tipuri, care corespund in general unor actiuni specifice. Fiecare semnal are asociat un numar, iar acestor numere le corespund unele constante simbolice definite in bibliotecile sistemului de operare. Standardul POSIX.1 defineste cateva semnale care trebuie sa existe in orice sistem UNIX.

Semofarizare si monitorizare
Definitie: Situatiile in care rezultatul executiei unui sistem format din mai multe procese (fire de executie) depinde de viteza lor relativa de executie se numesc conditii de cursa (in engleza: race conditions). Pentru a evita ca la iesirea din functia go() [in cazul windows] sau pthread_exit (linux) toate thread-urile sa-si termine executia setului de instructiuni in acelasi timp, trebuie implementat un algoritm de sincronizare si planificare. Problema sincronizarii nu se pune numai la thread-uri, ci si la procese. In cazul in care mai multe procese/fire de executie folosesc resurse comune, rezultatul final al executiei lor poate sa nu fie foarte stabil deoarece conteaza ordinea in care procesele /firele de executie returneaza rezultatele executarii seturilor de instructiuni. Conditiile de cursa apar atunci cand trebuiesc executate/modificate parti din program care sunt puse la comun (sunt folosite si de alte procese/thread-uri). Aceste portiuni care acceseaza parti din program puse la comun se numesc zone (sectiuni critice critical sections). Daca ne asiguram ca threadurile care ruleaza nu executa cod in acelasi timp in zonele critice, problema sincronizarii este rezolvata. Acest procedeu poarta denumirea de excluziune mutuala. O alta metoda de sincronizare este metoda Semafoarelor. Semaforul este un tip de date abstract ce poate avea o valoare intreaga nenegativa si asupra caruia se pot efectua operatiile: init, up si down. Init initializeaza semaforul, down realizeaza o operatie de decrementare, daca valoarea este pozitiva. Dacavaloarea este nula, procesul se blocheaza. Operatia up incrementeaza valoarea semaforului, daca nu exista un process blocat pe acel semafor. Daca exista, valoarea semaforului ramane zero iar unul dintre procese care este blocat va fi deblocat. Ca si metode de sincronizare mai avem mecanisme System sau VIPC. Semafoarele si memoria partajata fac parte din mecanismele de comunicare si sincronizare a proceselor cunoscute ca System VIPC. Prin mecanismul de memorie partajata doua sau mai multe procese pot pune in comun o zona de memorie. Fiind o resursa pusa la comun, accesul la zona de memorie partajata trebuie sa fie sincronizat.