Documente Academic
Documente Profesional
Documente Cultură
PROCESE I THREAD-URI
A.2.1 Procese
n esen, un proces este o instan de execuie a unui segment de cod (o parte
a unui program). Pentru acelai concept, unii autori, ca de exemplu, proiectanii
limbajului de programare Ada, folosesc denumirea de task. Termenul de task are, n
cele mai multe lucrri, un neles mai general, de sarcin de lucru, care se poate atribui
unui proces, unui thread sau chiar unei singure instruciuni, i va fi utilizat cu aceast
semnificaie.
Procesele sunt create de ctre sistemul de operare, ca urmare a execuiei unei
comenzi de tip RUN pentru un fiier executabil, care conine codul (sau o parte a
codului) programului, sau pot fi creeate n mod dinamic, de ctre alte procese n cursul
execuiei acestora.
Sistemul de operare grupeaz toate informaiile despre un proces ntr-o
structur de date numit blocul de control al procesului (Process Control Block PCB). Atunci cnd este creat un proces nou, sistemul creaz un bloc de control nou,
pentru a-l utiliza ca descriptor de-a lungul existenei procesului. Cnd procesul se
termin, blocul lui de control este distrus i eliminat din memorie. n starea dormant,
un proces nu are un bloc de control i nu poate intra n competiie pentru obinerea de
resurse. n mod tipic, blocul de control menine urmtoarele informaii despre proces:
CALCUL PARALEL
Fiecare proces are un spaiu de adrese al memoriei i poate trece prin mai
multe stri n cursul execuiei.
A.2.1.1 Spaiul virtual de adrese al procesului
Spaiul virtual de adrese al unui proces este reprezentat de mulimea tuturor
adreselor pe care procesul le poate utiliza (accesa). De exemplu, n sistemele
Unix/Linux cu adresare pe 32 de bii, adresele pot fi cuprinse ntre 0 i 0x7fffffff, ceea
ce reprezint 2 GB, restul de 2 GB, adresele de la 0x80000000 la 0xffffffff, fiind n
spaiul de adrese al sistemului de operare.
Spatiul de adrese al unui proces formeaz ceea ce se numete imaginea
procesului (Fig. A.2.1) i cuprinde mai multe segmente cu rol diferit, n funcie de
natura informaiilor pe care le contin, i anume:
Segmentul de cod (numit i segment de text), conine imaginea executabil a
programului i este ntotdeauna cu tip de acces citete-numai (read-only), i poate fi
partajat de mai multe procese (printe-fii).
Segmentul de date al unui proces conine variabilele alocate static (iniializate
sau nu) ale programului i variabilele alocate dinamic (heap sau memoria liber).
Acest segment nu poate fi partajat cu alte procese, dar poate fi partajat de toate threadurile componente ale acelui proces.
Segmentul de stiv memoreaz variabilele locale ale funciilor i adresele de
revenire din funcii. Nu poate fi partajat cu alte proceses, iar dac sunt definite mai
multe thread-uri, fiecare thread are propria lui zon de stiv n cadrul segmentului de
stiv, i aceste zone nu pot fi partajate de mai multe thread-uri.
Proces
Spatiul
virtual de
memorie al
procesului
Segment de date
Segment de stiva
posibil ca numai o parte din spaiul virtual de adrese al unui proces s fie ncrcat n
memoria fizic. Memoria virtual poate fi implementat ca o extensie a modului de
administrare al memoriei (memory management), prin paginare, prin segmentare, sau
o combinaie a acestora dou. n acest din urm caz, care este cel mai frecvent utilizat,
segmentarea este folosit din punct de vedere al utilizatorului, dar fiecare segment este
divizat n pagini de dimensiune fix, pentru a fi alocate n memorie.
Operaia de asignare a adreselor (address mapping) n sistemele de memorie
virtual poate fi definit astfel. Fie spaiul virtual de adrese al unui proces
V = { 0, 1,,v-1 }, i spaiul de adrese al memoriei fizice (reale) M = { 0, 1, , m-1
}.
n general, n sistemele mari, spaiul virtual de adrese este mai mare dect
memoria fizic (v > m), dar, n sistemele mai mici, se poate ntlni i situaia invers.
Sistemul de operare aloc n mod dinamic zone de memorie fizic unor pri
din spaiul de adrese virtuale ale proceselor. Mecanismul de translaie a adreselor
trebuie s asocieze fiecare adres virtual cu o locaie fizic. Cu alte cuvinte, n orice
moment hardware-ul de asignare trebuie s realizeze funcia f : V M , astfel nct:
r, daca x se afla in memorie la locatia r
f ( x) =
semnalare exceptie daca x nu se afla in memoria reala
CALCUL PARALEL
(f)
Dormant
(b)
(c)
Suspendat
Legend:
(a) Creare
(b) Planificare
(c) Prelevare
(d) Ateptare eveniment
(e) Apariie eveniment
(f) Terminare
(e)
(a)
Gata
Procesele aflate n strile de execuie sau gata de execuie sunt procese active,
de care sistemul de operare se ocup pentru a asigura o anumit politic de planificare.
Un proces rmne n starea de execuie pn cnd sistemul de operare l ntrerupe
(preleveaz) i l trece n starea gata de execuie, pentru a planifica un alt proces gata
de execuie.
Tranziia ntre dou procese active ntr-un sistem de operare cu multitasking
sau cu multiprocesare se numete comutarea proceselor (process switch), i are loc ca
rspuns la un eveniment din sistem.
Comutarea proceselor este o operaie complex, care implic un cost
(overhead) important i, datorit frecvenei cu care are loc ntr-un sistem, poate
influena semnificativ performanele acestuia. Costul de comutare apare, n principal,
datorit salvrii strii procesului atunci cnd este prelevat i a refacerii strii atunci
cnd este planificat. Eficiena operaiei de comutare a proceselor poate fi crescut prin
prevederea unor faciliti hardware (ca, de exemplu, seturi de registre multiple), sau
printr-o modalitate de structurare a proceselor, utiliznd thread-uri.
Un proces n execuie poate deveni suspendat prin apelul unei operaii de
intrare/ieire, sau prin ateptarea unui eveniment. Sistemul de operare memoreaz
cauza suspendrii procesului, astfel nct s-l poat relua atunci cnd condiia de
suspendare a disprut. La reluare, procesul suspendat este trecut n starea gata de
execuie. Un proces suspendat este un proces inactiv, care nu consum timp de
execuie al procesorului i aceast stare suspendat mbuntete performanele
sistemului de operare.
Implementarea proceselor i a thread-urilor difer de la un sistem de operare
la altul. n continuare se va prezenta modul de implementare a proceselor i threaduruilor n mai multe sisteme de operare, i anume n sistemele de operare multitasking
Aceast funcie returneaz pid-ul unuia din fiii care s-au terminat, iar n
locaia indicat de pointerul dat ca argument se obine un cod de terminare al
procesului fiu. Dac se ateapt mai muli fii, trebuie apelat funcia wait pentru
fiecare fiu. Dac se apeleaz funcia wait i nu mai exist procese fii, atunci wait
returneaz valoarea -1. Dac procesul printe se termin nainte ca s se termine un
fiu, acel fiu devine proces zombie, care primete ppid = 1 (pid-ul procesului init).
n exemplul urmtor un proces printe creeaz un fiu i valoarea returnat de
funcia fork este folosit pentru diferenierea execuiei n procesele printe i fiu.
// Program Hello_PROCESS.c
#include <stdio.h>
int main () {
if (fork() == 0 ) printf(Hello FIU pid=%d ppid=%d\n,
getpid(), getppid());
else { printf(Hello PARINTE pid=%d ppid=%d\n,
getpid(), getppid());
wait(0);
}
return 0;
}
CALCUL PARALEL
..................,
// ultimul argument
// inchiderea listei de arg.
n cele mai multe programe, funciile fork() i exec() sunt utilizate mpreun:
procesul printe creaz un proces fiu i execuia acestuia este supranscris, prin apelul
funciei exec.
n exemplul urmtor, procesul printe (dat n programul Parinte.c) creeaz un
proces fiu prin apelul funciei fork; procesul fiu astfel creat nlocuiete imaginea
motenit cu o nou imagine creeat prin programul fiu.c, transmitndu-i ca argument
pid-ul procesului iniial (procesul printe). Procesul fiu afieaz aceste valori i revine.
// Program Parinte.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(){
char arg1[4]; pid_t pid = getpid();
sprintf (arg1,"%4d",pid);
printf("Proces parinte pid: %d\n", pid);
if (fork() == 0) execlp("/calea_abs/Fiu", "Fiu", arg1,
(char*)0);
wait(0);
return 0;
}
// Program Fiu.c
#include <stdio.h>
// Argumentul primit in argv[1] este pid-ul parintelui
int main(int argc, char** argv){
int arg1 = atoi(argv[1]);
printf("Proces fiu pid: %d ppid: %d arg. primit:
%d\n", getpid(), getppid(), arg1);
return 0;
}
CALCUL PARALEL
Un proces aflat n starea de execuie poate crea un proces nou (proces fiu) prin
apelul funciei CreateProcess(). Se poate considera c, n mare, funcia CreateProcess
are funcionalitatea combinaiei de apeluri fork exec din sistemele Unix: creaz un
proces nou, mpreun cu thread-ul lui primar, care execut un segment de cod
executabil coninut n fiierul dat ca prim argument. Prototipul funciei CreateProcess
este urmtorul:
BOOL CreateProcess (
LPCTSTR lpAppName,
//
LPTSTR lpCmdLine,
//
LPSECURITY_ATTRIBUTES lpPSA, //
LPSECURITY_ATTRIBUTES lpTSA, //
BOOL bInheritHandles,
//
DWORD dwCreationFlags,
//
LPVOID lpEnvironment,
//
LPCTSTR lpCurrentDir,
//
LPSTARTINFO lpStartInfo,
LP_PROCESS_INFORMATION lpPInfo
);
//
//
//
//
cuvinte de memorie n afara acestui spaiu, dup cum nici alte procese nu pot avea
acces la spaiul acestuia. Modalitatea prin care dou sau mai multe procese pot accesa
o zon de memorie comun o reprezint crearea unui segment de memorie partajat
(shared memory segment).
n general, un proces creeaz (aloc) un segment de memorie partajat, pentru
care definete dimensiunea i drepturile de acces, dup care l amplaseaz n propriul
spaiu de adrese, prin stabilirea unei corespondene (mapping) ntre adresele din
segmentul partajat i adresele din spaiul su de adrese. De aceea, de multe ori,
segmentele de memorie partajate mai sunt denumite segmente (sau obiecte) amplasate
(mapped segments, mapped objects).
O dat creat un segment de memorie partajat, alte procese pot s-l ataeze i
s-l amplaseze n spaiul lor virtual de adrese. Fiecare proces acceseaz memoria
partajat relativ la adresele de ataare proprii, aa cum se poate vedea n Fig. A.2.3.
Cnd un proces nu mai necesit accesul la segmentul de memorie partajat, el
se poate detaa de acesta. Cnd toate procesele s-au detaat de la un segment de
menorie partajat, el poate fi ters din memorie, operaie pe care, n general, o execut
procesul care l-a creat. Localizarea n memoria fizic a unui segment de memorie
partajat depinde de mecanismul de memorie virtual al sistemului de operare. ntr-un
sistem cu memorie virtual prin paginare, segmentul de memorie partajat este alctuit
dintr-un numr ntreg de pagini. Aceste pagini se pot afla n memoria fizic (RAM), la
adrese stabilite prin mecanismul de memorie virtual, adrese care variaz n cursul
derulrii execuiei, i nu neaprat contigue (alturate), sau pot fi stocate pe disc
Proces A
Segment
de text
Segment
de date
...
Segment de memorie
partajat atasat
0x0060 0000
Segment
de stiv
0x0068 0000
Memorie
Spaiul de memorare
a datelor
Proces B
Segment
de text
0x0050 0000
Segment
de date
Segment de memorie
partajat atasat
0x0058 0000
Segment de
stiv
10
CALCUL PARALEL
(deci centralizat, cum este n cazul arhitecturilor UMA), sau distribuit (deci ataat
unor module combinate procesor-memorie, cum este cazul arhitecturilor NUMA).
Ceea ce este important, este posibilitatea ca toate procesoarele sistemului s poat
accesa (prin intermediul reelei de interconectare) un spaiu unic de adrese partajate.
n astfel de sisteme, segmentul de memorie partajat, creat de un proces i
ataat de acesta i de alte procese, trebuie s fie amplasat n memoria (fizic) partajat
a sistemului, i nu ntr-o memorie local (dac aceasta exist).
Crearea segmentelor de memorie partajat n Unix. Pentru crearea sau
ataarea segmentelor de memorie partajat n sistemele Unix tradiionale, se folosete
apelul funciei sistem shmget(), care creaz un segment de memorie partajat i
genereaz structurile de date de sistem asociate. Funcia shmget() returneaz
identificatorul unui segment de memorie partajat, n cazul execuiei corecte, sau 1 n
caz de eroare. Prototipul acestei funcii este:
int shmget (
key_t key,
int size,
int shm);
Dup apelul acestei funcii, segmentul este creat sau ataat de ctre un proces,
dar nu va putea fi utilizat, pn ce nu este amplasat n spaiul de adres propriu, prin
apelul funciei de sistem shmat():
void *shmat (
int shmid,
void *shmaddr,
int shmflg);
11
Valoarea returnat, de tip HANDLE, are valoarea NULL n caz de eroare, iar
tipul erorii se poate afle prin apelul funciei GetLastError(). Dac valoarea returnat
este diferit de NULL, atunci obiectul de memorie partajat a fost creat sau ataat
(deschis) corect. Obiectul este creat ca un obiect nou atunci cnd, la apelul funciei
CreateFileMapping, nu exist nici un alt obiect cu aceleai nume. Dac un astfel de
obiect exit, handle-ul returnat este un handle valid i identific obiectul deja existent.
Aceast situaie este detectat prin apelul, imediat dup funcia CreateFileMapping, a
funciei GetLastError, care returneaz 0, dac obiectul nu fusese creat mai nainte, sau
constanta simbolic ERROR_ALREADY_EXISTS, dac obiectul exista deja.
Apelul unei funcii CreateFileMapping pentru un obiect existent este o
operaie de ataare (deschidere) a obiectului de memorie partajat de ctre procesul
apelant i este echivalent cu apelul funciei OpenFileMapping(), al crui prototip este
urmtorul:
HANDLE OpenFileMapping(
DWORD dwAcces,
BOOL bInheritFlag,
LPCTSTR lpName);
// atribute de acces
// flag de motenire
// numele ob. memorie partajat
12
CALCUL PARALEL
DWORD
DWORD
DWORD
DWORD
dwAccess,
dwOffHigh,
dwOffLow,
dwNumber);
//
//
//
//
mod de acces
cel mai semnif. cuv. offset
cel mai puin semnif. offset
numar de octeti de amplasat
A.2.3 Thread-uri
Thread-urile reprezint o modalitate software de a mbunti performanele
sistemelor de calcul prin reducerea timpului de comutare a proceselor. Un thread este
un fir de executie n cadrul unui proces, iar n fiecare proces se pot defini mai multe
thread-uri.
Fiecare thread are o stare mai redus (constituit din starea registrelor i a
flag-urilor procesorului i stiva proprie) i toate thread-urile unui proces partajeaz
resursele de calcul ale acestuia (ca de exemplu, memoria, fiierele, canale I/O etc.).
n sistemele bazate pe thread-uri, thread-ul este cea mai mic entitate de
planificare, iar procesul servete ca un mediu de execuie a thread-urilor.
De vreme ce toate celelalte resurse, cu excepia procesorului, sunt gestionate
de ctre procesul care le nglobeaz, comutarea ntre thread-urile care aparin aceluiai
proces, care implic doar salvarea, respectiv restaurarea, strii hardware (nu i a stivei,
care este separata, proprie fiecarui thread), este rapid i eficient. Totui, comutarea
ntre thread-urile care aparin unor procese diferite implic tot costul de comutare a
proceselor.
Thread-urile sunt un mecanism eficient de exploatare a concurenei
programelor. Un program poate fi mprit n mai multe thread-uri, fiecare cu o
execuie mai mult sau mai puin independent, i pot comunica ntre ele prin accesul la
spaiul de adres a memoriei procesului, pe care l partajeaz ntre ele.
Toate thread-urile unui proces partajeaz segmentul de cod i segmentul de
date al procesului care le-a creeat. Fiecare thread are propriul segment de stiv care nu
este partajat cu alte thread-uri.
Dac thread-urile aparin unor procese diferite, trebuie s fie definit n mod
explicit un segment de memorie partajat ntre procese, la fel cum se procedeaz
pentru partajarea memoriei ntre procese.
A.2.3.1 Thread-uri POSIX
n sistemul de operare Unix tradiional nu sunt definite thread-uri; fiecare
proces conine un singur thread, iar alocarea timpului de execuie al procesorului se
face ntre procesele active, dup diferii algoritmi de planificare.
n sistemele de operare moderne Unix/Linux, dezvoltate pentru
multiprocesoare i multicalculatoare, s-au implementat diferite faciliti pentru
controlul i partajarea resurselor multiple (procesoare, memorie), inclusiv thread-uri.
13
14
CALCUL PARALEL
15
(thread-ul principal) i poate crea thread-uri noi, care partajeaz spaiul de adres al
procesului care le-a creat, precum i resursele acestuia (fiiere, obiecte de
sincronizare).
Un thread Windows este identificat att printr-un instrument de acces numit
handle (n continuare se va folosi aceast denumire, netradus, dar mai apropiat de
informaiile pe care un programator le utilizeaz n mod obinuit), ct i printr-un
identificator (id), care este un numr unic, atribuit unui thread, care l caracterizeaz
pe toat durata de existen a acestuia. Aceast dualitate de identificare ngreuneaz,
oarecum, lucrul cu thread-urile Windows, unele funcii cer ca parametru handle-ul
thread-ului, altele identificatorul acestuia (id-ul).
Crearea unui thread nou ntr-un proces necesit, mai nti definirea funciei pe
care thread-ul urmeaz s o execute, urmat de apelul unei funcii CreateThread(),
care are urmtorul prototip:
HANDLE CreateThread (
LPSECURITY_ATTRIBUTES lpTSA,
DWORD dwStackSize,
// dimensiunea stivei
LPTHREAD_START_ROUTINE lpThreadFunc,
LPVOID lpParam,
// parametrii noului thread
DWORD dwCreationFlags,
// flag de creare
LPDWORD lpThreadID );
// pointer la id. returnat
16
CALCUL PARALEL