Differences

This shows you the differences between two versions of the page.

Link to this comparison view

so:cursuri:curs-06 [2014/03/17 18:16]
razvan.deaconescu removed
so:cursuri:curs-06 [2019/03/24 14:10] (current)
razvan.deaconescu
Line 1: Line 1:
-====== Curs 06 - Gestiunea memoriei ​======+====== Curs 06 - Memoria virtuală ​======
  
-<​html>​ +  * [[http://​prezi.com/​qshrq7oeytj2/?utm_campaign=share&utm_medium=copy&rc=ex0share | Curs 06 - Memoria virtuală (vizualizare Prezi)]] 
-<iframe src="http://​prezi.com/​embed/​gfswijyf3vsk/?bgcolor=ffffff&amp;​lock_to_path=0&amp;​autoplay=no&​amp;​autohide_ctrls=0&​amp;​features=undefined&​amp;​disabled_features=undefined"​ width="​550"​ height="​400"​ frameBorder="​0"></​iframe>​ +  * [[http://​elf.cs.pub.ro/​so/​res/​cursuri/​SO_Curs-06.pdf | Curs 06 - Memoria virtuală (PDF)]]
-</html>+
  
-  * [[http://prezi.com/gfswijyf3vsk/​so-curs-6/​?kw=view-gfswijyf3vsk&​rc=ref-31844697 ​Curs 06 - Gestiunea memoriei (vizualizare Prezi)]] +  * [[https://drive.google.com/open?id=1jsFIY2bG2QssLX1X9AWn4_0Ec2x_hK9oOGohmFqKSRM|Notițe de curs]]
-  * [[http://​elf.cs.pub.ro/​so/​res/​cursuri/​SO_Curs-06.pdf | Curs 06 - Gestiunea memoriei (PDF)]]+
  
   * Suport curs   * Suport curs
-    * Operating ​System ​Concepts Essentials +    * Operating ​Systems ​Concepts Essentials 
-      * Capitolul ​Main Memory +      * Capitolul ​Virtual ​Memory 
-    * Modern Operating Systems+    * Modern Operating Systems, 2nd Ed.
       * Capitolul 4 - Memory Management       * Capitolul 4 - Memory Management
-        * Secțiunile 4.1, 4.24.34.8 +        * Secțiunile 4.4, 4.
-    * [[http://lwn.net/Articles/250967| Ulrich Drepper ​What every programmer should know about memory]]+    * Modern Operating Systems3rd Ed. 
 +      * Capitolul ​- Memory Management 
 +        * Secțiunile 3.4, 3.5 
 +    * [[http://duartes.org/gustavo/blog/post/​anatomy-of-a-program-in-memory/|Anatomy of a Program in Memory]] 
 + 
 +<​html>​ 
 +  <​center>​ 
 +    <iframe src="​https://​prezi.com/​embed/​qshrq7oeytj2/?​bgcolor=ffffff&​amp;​lock_to_path=0&​amp;​autoplay=0&​amp;​autohide_ctrls=0&​amp;​features=undefined&​amp;​disabled_features=undefined"​ width="​550"​ height="​400"​ frameBorder="​0"></​iframe>​ 
 +  </​center>​ 
 +</​html>​ 
 + 
 +===== Demo-uri ===== 
 + 
 +Pentru parcurgerea demo-urilor,​ folosim [[http://​elf.cs.pub.ro/​so/​res/​cursuri/​curs-06-demo.zip|arhiva aferentă]]. Demo-urile rulează pe Linux. Descărcăm arhiva folosind comanda<​code bash> 
 +wget http://​elf.cs.pub.ro/​so/​res/​cursuri/​curs-06-demo.zip 
 +</​code>​ și apoi decomprimăm arhiva<​code bash> 
 +unzip curs-06-demo.zip 
 +</​code>​ și accesăm directorul rezultat în urma decomprimării<​code bash> 
 +cd curs-06-demo/​ 
 +</​code>​ 
 + 
 +Acum putem parcurge secțiunile cu demo-uri de mai jos. 
 + 
 +==== Alocarea variabilelor într-un executabil ==== 
 + 
 +Dorim să investigăm modul în care se alocă variabilele într-un executabil. Pentru aceasta accesăm subdirectorul ''​exec-vars/'';​ urmărim conținutul fișierului ''​exec-vars.c''​. În acest fișier definim/​alocăm variabile în diverse forme: dinamic, static, global. Vrem să vedem cum sunt acestea alocate în executabil și în proces. 
 + 
 +Compilăm programul folosind ''​make''​. 
 + 
 +Investigăm zonele de memorie din executabil în care sunt salvate variabilele,​ folosind comanda<​code bash> 
 +objdump -t addr-space | grep var 
 +</​code>​ 
 + 
 +De ce apar doar unele variabile?​ 
 + 
 +<spoiler Răspuns>​ 
 +Pentru variabilele alocate dinamic (pe stivă, heap) se alocă la runtime, adică în momentul în care procesul rulează. Executabilul nu are legătură cu datele alocate la runtime. 
 +</​spoiler>​ 
 + 
 +Ce semnfică ''​l''​ și ''​g''​ în output-ul obținut? 
 + 
 +<spoiler Răspuns>​ 
 +Arată că variabilele respective sunt simboluri globale (''​g''​ - la nivelul întregului modul) sau locale (''​l''​ - locale unei funcții). 
 +</​spoiler>​ 
 + 
 +Pentru a afișa conținutul zonei ''​.rodata''​ folosind comanda<​code bash> 
 +readelf -x .rodata addr-space 
 +</​code>​ 
 +Secțiunea ''​.rodata''​ conține variabile read-only, în cazul de față literalul ''​rulz''​. Literalii și constantele se stocează în secțiunea ''​.rodata''​ a unui executabil. 
 + 
 +==== Investigarea mapării folosind pmap ==== 
 + 
 +Vrem să vizualizăm spațiul virtual de adrese al unui proces. Pentru aceasta, accesăm subdirectorul ''​pmap/'';​ urmărim conținutul fișierului ''​pmap.c'';​ facem mapări (folosind apelul ''​mmap''​) folosind diverse flag-uri. 
 + 
 +Compilăm programul folosind ''​make''​ și apoi îl rulăm:<​code bash> 
 +./pmap 
 +</​code>​ 
 + 
 +Într-o altă consolă urmărim schimbările din spațiul virtual de adrese al procesului creat folosind comanda<​code bash> 
 +watch -d pmap $(pidof pmap) 
 +</​code>​ Comanda de mai sus urmărește spațiul virtual de adrese. Ca să generăm schimbări în spațiul virtual de adrese, apăsăm ''​ENTER''​ în consola în care rulează programul pentru a continua pașii din cod. 
 + 
 +Urmărim modificările care apar în urma diferitelor tipuri de mapare din cod. Observăm că se mapează câte o singură pagină; la fiecare operație de mapare spațiul total crește cu 4K, iar la fiecare operație de demapare spațiul total scade cu 4K. Observăm că în cazul mapării partajate permisiunile sunt ''​rw-s'';​ ''​s''​ înseamnă shared. Tot în cazul memoriei partajate apare mapat dispozitivul ''/​dev/​zero'',​ unul dintre modurile uzuale de a face mapare partajată. 
 + 
 +De ce unele biblioteci sunt mapate cu permisiuni de scriere? 
 + 
 +<spoiler Răspuns>​ 
 +Bibliotecile au mai multe secțiuni, similar unui executabil, mapate cu permisiunile corespunzătoare. Secțiunea de cod/text este mapată cu permisiuni de citire și execuție (''​r-x''​),​ cea de date este mapată cu permisiuni de citire și scriere (''​rw-''​) iar cea de date read-only este mapată cu permisiuni doar de citire (''​r--''​). 
 +</​spoiler>​ 
 + 
 +==== Alocarea de memorie virtuală ==== 
 + 
 +Vrem să urmărim modul în care se alocă memorie virtuală în spațiul virtual de adrese al unui proces. Pentru aceasta, accesăm subdirectorul ''​allocation/'';​ urmărim conținutul fișierului ''​allocation.c'';​ în cadrul fișierului se fac alocări de memorie virtuală folosind pe rând apelul ''​malloc''​ și apelul ''​mmap''​. 
 + 
 +Compilăm fișierul folosind comanda ''​make''​. 
 + 
 +Deschidem o consolă nouă și rulăm în cele două console astfel: 
 +    * Într-o consolă rulăm executabilul aferent:<​code>​ 
 +./​allocation 
 +</​code>​ 
 +    * Într-o altă consolă vizualizăm dimensiunea spațiului fizic ocupat și a a spatiului virtual ocupat, folosind comanda<​code bash> 
 +watch -n 1 ps -o pid,​rss,​vsz,​cmd -p $(pidof allocation) 
 +</​code>​ 
 + 
 +Observați cum crește dimensiunea spațiului fizic și a spatiului virtual în cazul folosirii ''​malloc''​ și **doar** a spațiului virtual folosind ''​mmap''​. 
 + 
 +Pentru a explica acest comportament,​ rulăm executabilul prin ''​strace'':<​code>​ 
 +strace ./​allocation 
 +</​code>​ 
 + 
 +Observăm că în cazul funcției de bibliotecă ''​malloc''​ se realizează apelul de sistem ''​brk'',​ în timp ce în cazul funcției de bibliotecă ''​mmap''​ se realizează apelul de sistem ''​mmap''​. Apelul de sistem ''​mmap''​ alocă **doar** memorie virtuală. 
 + 
 +Observăm de asemenea, că se realizează un număr redus de apeluri de sistem ''​brk''​ raportat la cele ''​1024''​ de apeluri de bibliotecă ''​malloc''​. Un apel ''​brk''​ alocă un pool mai mare de memorie care va fi apoi folosit la apeluri viitoare ''​malloc'';​ realizează o prealocare. 
 + 
 +Pentru a vedea comportamentului funcției de bibliotecă ''​malloc'',​ actualizăm codul astfel încât ''​malloc''​ să aloce, la fel ca ''​mmap''​ calupuri de ''​1MB''​ de memorie. Adică bucla ''​for''​ aferentă să arate așa:<​code c> 
 + for (cnt = 0; cnt < NUM_ROUNDS; cnt++) { 
 + puts("​Using malloc to allocate 1024 sets of 1024 bytes."​);​ 
 + p = malloc(1024*1024);​ 
 + DIE(p == NULL, "​malloc"​);​ 
 + sleep(2);​ 
 +
 +</​code>​ 
 + 
 +Compilăm noua sursă folosind comanda ''​make''​. La fel ca mai devreme rulăm executabilul într-o consolă și comanda de vizualizare în altă consolă. Observăm că acum atât funcția de bibliotecă ''​malloc'',​ cât și funcția de bibliotecă ''​mmap''​ alocă **doar** memorie virtuală. 
 + 
 +Folosim în continuare ''​strace''​ pentru a investiga:<​code bash> 
 +strace ./​allocation 
 +</​code>​ 
 + 
 +Observăm că acum și funcția de bibliotecă ''​malloc''​ folosește în spate tot apelul de sistem ''​mmap''​. Acesta alocă doar memorie virtuală de unde și comportamentul. De la o valoare dată, alocarea cu ''​malloc''​ folosește apelul de sistem ''​mmap''​. 
 + 
 +<spoiler Despre apelul malloc>​ 
 +Valoarea de la care funcția de bibliotecă ''​malloc''​ folosește ''​mmap''​ este definită de ''​MMAP_THRESHOLD'',​ în mod implicit configurat la ''​128KB''​. Detalii se găsesc în [[http://​man7.org/​linux/​man-pages/​man3/​malloc.3.html#​NOTES|pagina de manual a malloc]]:<​code>​ 
 +       ​Normally,​ malloc() allocates memory from the heap, and adjusts the 
 +       size of the heap as required, using sbrk(2). ​ When allocating blocks 
 +       of memory larger than MMAP_THRESHOLD bytes, the glibc malloc() 
 +       ​implementation allocates the memory as a private anonymous mapping 
 +       using mmap(2). ​ MMAP_THRESHOLD is 128 kB by default, but is 
 +       ​adjustable using mallopt(3). ​ Allocations performed using mmap(2) are 
 +       ​unaffected by the RLIMIT_DATA resource limit (see getrlimit(2)). 
 +</​code>​ 
 +</​spoiler>​ 
 + 
 +==== Paginare la cerere (demand paging) ==== 
 + 
 +Vrem să urmărim modul în care se alocă pagini de memorie fizică la cerere, proces care se numește ''​(on) demand paging''​. Pentru aceasta, accesăm subdirectorul ''​demand-paging/'';​ urmărim conținutul fișierului ''​demand-paging.c'';​ în cadrul fișierului alocăm memorie virtuală folosind ''​mmap''​ și apoi accesăm primul octet al fiecărei pagini alocate. 
 + 
 +Compilăm codul sursă folosind comanda ''​make''​. 
 + 
 +Avem nevoie de două console: 
 +  * Într-o consolă rulăm executabilul aferent:<​code>​ 
 +./​demand-paging 
 +</​code>​ 
 +  * Într-o altă consolă vizualizăm dimensiunea spațiului fizic ocupat și a a spatiului virtual ocupat, folosind comanda:<​code bash> 
 +watch -n 1 ps -o pid,​rss,​vsz,​cmd -p $(pidof demand-paging) 
 +</​code>​ 
 + 
 +Observăm cum crește dimensiunea spațiului spatiului virtual (coloana ''​VSZ''​) fără a crește dimensiunea spațiului fizic în prima parte. Se face alocare de memorie virtuală, fără paginare - adică fără alocare de spațiu fizic aferent. În partea a doua, observați cum crește dimensiunea spațiului fizic (coloana ''​RSS''​) în a doua parte (fără a crește dimensiunea spațiului virtual). Aceasta este //(on) demand paging//, cu alocarea paginilor fizice necesare la nevoie. 
 + 
 +Ca să detaliem, urmărim page fault-urile realizate pe parcursul rulării programului. Folosim, la fel, două console: 
 +  * Într-o consolă rulăm executabilul aferent:<​code>​ 
 +./​demand-paging 
 +</​code>​ 
 +  * Într-o altă consolă vizualizăm numărul de page fault-uri generate de program (''​min_flt''​ este coloana de interes, ''​maj_flt''​ este pentru interacțiuni cu discul -- swapping):<​code bash> 
 +watch -n 1 ps -o pid,​min_flt,​maj_flt,​cmd -p $(pidof demand-paging) 
 +</​code>​ 
 +Observați cum nu există page fault-uri în prima pare a rulării programului,​ în momentul în care facem mapări de memorie. Dar apar page fault-uri în a doua parte a rulării programului. 
 + 
 +Câte page fault-uri sunt generate la o "​trecere prin chunk"?​ De ce? 
 + 
 +<spoiler Răspuns>​ 
 +Se generează 256 page fault-uri. Asta se întâmplă pentru ca la fiecare "​trecere prin chunk" se accesează 256 pagini, fiecare pagină având câte 4KB, pentru un total de 1MB. Un page fault înseamnă alocarea (on demand) a unei pagini fizice. 
 +</​spoiler>​ 
 + 
 +==== Page fault-uri la fork (copy-on-write) ==== 
 + 
 +Vrem să urmărim realizarea page fault-urilor în urma unui apel ''​fork'';​ page fault-urilor vor fi cauzate de mecanismul de copy-on-write în momentul în care unul dintre cele două procese (copil sau părinte) scrie în zona respectivă. Pentru aceasta, accesăm subdirectorul ''​fork-faults'';​ urmărim conținutul fișierului ''​fork-faults.c''​. În cadrul fișierului ''​fork-faults.c''​ se execută următorii pași: 
 +  - se alocă memorie virtuală folosind ''​mmap''​ 
 +  - se alocă memorie fizică pentru paginile de mai sus, folosind //(on) demand paging// prin accesarea primului octet al fiecărei pagini 
 +  - se creează un proces nou 
 +  - procesul copil citește valoarea din prima jumătate a numărului de pagini (**doar** citește) 
 +  - procesul copil scrie o valoare în cadrul fiecărei pagini din a două jumătate 
 +  - procesul părinte scrie o valoare în toate paginile 
 + 
 +Ca să urmărim ce se întâmplă,​ compilăm fișierul folosind ''​make''​. Apoi rulăm programul obținut:<​code bash> 
 +./​fork-faults 
 +</​code>​ Folosim ''​ENTER''​ pentru a continua programul, dar după rularea ''​pidstat''​ (mai jos). 
 + 
 +Într-o altă consolă folosim utilitarul ''​pidstat''​ din pachetul ''​sysstat''​ care permite monitorizarea page fault-urilor unui proces (prin intermediul argumentului ''​-r''​). Dacă nu există comanda ''​pidstat''​ trebuie să instalăm pachetul ''​sysstat''​ folosind comanda:<​code bash> 
 +apt-get install sysstat 
 +</​code>​ 
 + 
 +Pentru a rula ''​pidstat''​ și a urmări page fault-urile,​ folosim, pe a doua consolă, comanda<​code bash> 
 +pidstat -r -T ALL -p $(pidof fork-faults) 5 100 
 +</​code>​ Comanda de mai sus afișează câte un mesaj la fiecare 5 secunde. În prima consolă apăsăm ''​ENTER''​ pentru a continua rularea și urmărim informațiile afișate de ''​pidstat'',​ apoi continuăm apăsarea ''​ENTER''​ etc. 
 + 
 +Urmărim, în outputul comenzii ''​pidstat'',​ evoluția numărului de page fault-uri pentru cele două procese: părinte și copil. Page fault-urile care apar în cazul unui copy-on-write în procesul copil vor fi vizibile ulterior și în procesul părinte. Observăm că procesul copil generează page fault-uri doar pe jumătate din pagini (cele în care scrie), iar procesul părinte generează page fault-uri pe toate paginile. Asta pentru că un proces creează o copie a paginilor inițiale, dar lasă acele pagini ''​read-only'',​ iar alt proces primește page fault dar doar schimbă permisiunile din read-only în read-write.
so/cursuri/curs-06.1395072996.txt.gz · Last modified: 2014/03/17 18:16 by razvan.deaconescu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0