This is an old revision of the document!
Pentru parcurgerea demo-urilor, folosim arhiva aferentă. Demo-urile rulează pe Linux. Descărcăm arhiva folosind comanda
wget http://elf.cs.pub.ro/so/res/cursuri/curs-08-demo.zip
și apoi decomprimăm arhiva
unzip curs-08-demo.zip
și accesăm directorul rezultat în urma decomprimării
cd curs-08-demo/
Acum putem parcurge secțiunile cu demo-uri de mai jos.
Dorim să investigăm timpul de creare a proceselor și thread-urilor; timpul de creare al proceselor va fi mai mare, ne interesează cu cât. Pentru aceasta accesăm subdirectorul creation-time/
; urmărim conținutul fișierelor process-overhead.c
și thread-overhead.c
. În aceste fișiere se creează, în 100 de runde, câte 100 de procese, respectiv thread-uri. Vor fi create, respectiv, 100 de procese și 100 de thread-uri.
Compilăm cele două programe folosind make
. Rezultă două fișiere în format executabil: process-overhead
și thread-overhead
.
Pentru a măsura timpul de creare vom rula cele două executabile sub comanda /usr/bin/time
:
/usr/bin/time -v ./process-overhead > /dev/null /usr/bin/time -v ./thread-overhead > /dev/null
Acum urmărim diferențele dintre output-ul celor două comenzi.
Observăm că timpul de creare al proceselor este 2 până la 3 ori mai mare decât în cazul thread-urilor. Și este încă foarte mic ținând cont de faptul că am creat 10000 de procese: 100 runde a câte 100 de procese. Deși timpul de creare a thread-urilor este mai mic, în termeni absoluți ambii timpi sunt neglijabili. Desigur, contează faptul că se realizează doar fork
, fără exec
. Mecanismul de copy-on-write aferent fork
reduce semnificativ timpul de creare.
Observăm, de asemenea, că dimensiunea maximă a spațiului de memorie alocată (Maximum resident set size) este mai mare în cazul thread-urilor. Un thread nou creat ocupă spațiu suplimentar în memorie. Un proces nou creat va avea spațiul virtual creat propriu, dar, pentru început, va folosi spațiul de memorie fizică al primului proces, prin intermediul copy-on-write.
Diferențe sesizabile se observă în cazul numărului de page fault-uri și al schimbărilor de context involuntare. Schimbările de context involuntare (la schimbarea cuantei) sunt mai numeroase în cadrul proceselor întrucât se schimbă contextul procesului curent cu procesele pe care le creează. În cazul thread-urilor nu există schimbări de context între thread-uri, deoarece aparțin aceluiași proces. Numărul de page fault-uri în cadrul proceselor este cauzat de accesele de scriere proceselor noi peste zonele marcate copy-on-write. Sunt relativ puține accese, dar se resimt la nivelul numărului de page fault-uri.
Dorim să vedem cum afectează crearea thread-urilor spațiul de adresă al procesului. Pentru aceasta accesăm subdirectorul address-space/
; urmărim conținutul fișierului address-space.c
. În cadrul fișierului se creează 5 thread-uri. La începutul programului și după fiecare creare de thread se așteaptă apăsarea tastei ENTER
de utilizator; în această vreme utilizatorul poate inspecta spațiul de adresă. Thread-urilor afișează un mesaj simplu și apoi așteaptă 100
de secunde.
Compilăm codul sursă folosind comanda make
. Rezultă executabilul address-space
.
Rulăm executabilul:
./address-space
Pentru a vizualiza spațiul de adrese al procesului, folosim comanda pmap
. Deschidem o altă consolă și rulăm comanda pmap
din output-ul căreia eliminăm referințele la biblioteci:
pmap -p $(pidof address-space) | grep -v '/lib/'
Acum se afișează zonele de memorie aferente procesului înainte de crearea unui thread.
Apoi apăsăm ENTER
în prima consolă. Acum se va crea un thread.
Investigăm din nou spațiul de adrese al procesului prin rularea comenzii pmap
în forma de mai sus în a doua consolă. Observăm apariția unei zone de 8192K
(adică 8MB). Apăsăm iarăși ENTER
în prima consolă și afișăm spațiul de adrese. Repetăm procesul de încă 3 ori până la crearea tuturor celor 5 thread-uri. Observăm că pentru fiecare thread au fost create două zone: o zonă de 8192K
cu permisiuni de citire și scriere și încă o pagină de gardă (4K
) fără permisiuni.
Zona de 8192K
creată pentru fiecare thread este stiva acelui thread. Se rezervă spațiu virtual pentru stiva fiecărui thread la creare. Observăm că pe un sistem pe 32 de biți, cu dimensiune relativ scăzută a spațiului de adrese, o valoare implicită de 8192K
a stivei unui thread va limita numărul de thread-uri care pot fi create, întrucât se umple spațiul de adrese.1)
Ne propunem să investigăm modul în care thread-urile partajează datele, iar procesele nu. Pentru aceasta accesăm subdirectorul creation-time/
; urmărim conținutul fișierelor process.c
și thread.c
. În ambele fișiere se incrementează o variabilă globală (data_var
) în cadrul unui proces nou și într-un thread nou. Variabila globală este inițializată la 0
.
Compilăm cele două programe folosind make
. Rezultă două fișiere în format executabil: process și thread.
Rulăm cele două executabile:
$ ./process data_var = 1 data_var = 1 $ ./thread data_var = 1 data_var = 2
Observăm că procesele au o secțiune de date proprie; fiecare incrementare s-a făcut de la 0
la 1
. Thread-urile (aceluiași proces) partajează însă secțiunea de date. În acest caz un thread-ul nou creat incrementează variabila de la 0
la 1
, pe când thread-ul inițial incrementează variabila de la 1
la 2
.
TODO
TODO