This shows you the differences between two versions of the page.
so:cursuri:curs-03 [2014/03/04 02:21] razvan.deaconescu [Demo-uri] |
so:cursuri:curs-03 [2019/03/02 09:07] (current) razvan.deaconescu |
||
---|---|---|---|
Line 1: | Line 1: | ||
====== Curs 03 - Procese ====== | ====== Curs 03 - Procese ====== | ||
- | <html> | + | /* |
- | <iframe src="http://prezi.com/embed/q-s8q3mrycz5/?bgcolor=ffffff&lock_to_path=0&autoplay=no&autohide_ctrls=0&features=undefined&disabled_features=undefined" width="550" height="400" frameBorder="0"></iframe> | + | * [[http://prezi.com/q-s8q3mrycz5/so-curs-3/?kw=view-q-s8q3mrycz5&rc=ref-31844697|Curs 03 - Procese (vizualizare Prezi)]] |
- | </html> | + | */ |
+ | * [[http://elf.cs.pub.ro/so/res/cursuri/SO%20-%20Curs%2003%20-%20Procese.pdf|Curs 03 - Procese (PDF)]] | ||
- | * [[http://prezi.com/q-s8q3mrycz5/so-curs-3/?kw=view-q-s8q3mrycz5&rc=ref-31844697 | Curs 03 - Procese (vizualizare Prezi)]] | + | * [[https://docs.google.com/document/d/1m5viP2CYF9P-ETMFPogiPsV1c8MlrFOAdBZVNemGNNY/edit?usp=sharing|Notițe de curs]] |
- | * [[http://elf.cs.pub.ro/so/res/cursuri/SO_Curs-03.pdf | Curs 03 - Procese (PDF)]] | + | |
* Suport curs | * Suport curs | ||
* Operating System Concepts Essentials | * Operating System Concepts Essentials | ||
* Capitolul 3 - Processes | * Capitolul 3 - Processes | ||
- | * Modern Operating Systems | + | * Modern Operating Systems (2nd Ed., 3rd Ed.) |
* Capitolul 2 - Processes and Threads - Secțiunea 1 | * Capitolul 2 - Processes and Threads - Secțiunea 1 | ||
* Linux System Programming | * Linux System Programming | ||
Line 20: | Line 20: | ||
* Capitolul 6 - Process Management | * Capitolul 6 - Process Management | ||
* Capitolul 11 - Interprocess Communication | * Capitolul 11 - Interprocess Communication | ||
+ | |||
+ | /* | ||
+ | <html> | ||
+ | <center> | ||
+ | <iframe src="https://prezi.com/embed/q-s8q3mrycz5/?bgcolor=ffffff&lock_to_path=0&autoplay=no&autohide_ctrls=0&features=undefined&disabled_features=undefined" width="550" height="400" frameBorder="0"></iframe> | ||
+ | </center> | ||
+ | </html> | ||
+ | */ | ||
+ | |||
+ | <html> | ||
+ | <center> | ||
+ | <iframe src="https://docs.google.com/viewer?url=https://elf.cs.pub.ro/so/res/cursuri/SO%20-%20Curs%2003%20-%20Procese.pdf&embedded=true" width="600" height="480" style="border: none;"> | ||
+ | </iframe> | ||
+ | </center> | ||
+ | </html> | ||
+ | |||
===== Demo-uri ===== | ===== Demo-uri ===== | ||
Line 92: | Line 108: | ||
==== Informații despre schimbările de context ==== | ==== Informații despre schimbările de context ==== | ||
- | Pentru a urmări numărul de schimbări de context ale unui proces, consultați fișierul ''/proc/$PID/status'', și anume câmpurile ''voluntary_ctxt_switches'' și ''nonvoluntary_ctxt_switches''. Urmăriți aceste valori pentru shell-ul curent:<code bash> | + | Pentru a urmări numărul de schimbări de context ale unui proces, consultăm fișierul ''/proc/$PID/status''. Ne interesează câmpurile ''voluntary_ctxt_switches'' și ''nonvoluntary_ctxt_switches''. Pentru a urmări aceste valori pentru shell-ul curent folosim comanda:<code bash> |
cat /proc/$$/status | cat /proc/$$/status | ||
</code> | </code> | ||
- | * Ce semnifică fiecare dintre cele două câmpuri de mai sus? | + | |
- | * Rulați de mai multe ori comanda de mai sus și observați alterarea celor două câmpuri. De ce se alterează proponderent câmpul ''voluntary_ctxt_switches''? | + | Câmpul ''voluntary_ctxt_switches'' (schimbări de context voluntare) se referă la schimbările în care procesul lasă de bună voie procesorul, de obicei acțiuni blocante de intrare/ieșire. Câmpul ''nonvoluntary_ctxt_switcher'' (schimbări de context nevoluntar) se referă la schimbările în care procesul este dat la o parte de pe procesor; de obicei acest lucru înseamnă expirarea cuantei curente de rulare; poate fi vorba și de apariția unui proces de prioritate mai bună. |
+ | |||
+ | Dacă rulăm de multe ori comanda de mai sus (''cat /proc/$$/status'') vom observa alterarea celor două câmpuri. Se alterează preponderent câmpul ''voluntary_ctxt_switches'' întrucât procesul curent, shell-ul, așteaptă în general intrare de la utilizator. Făcând în majoritate acțiuni de intrare/ieșire (blocante), cele mai dese schimbări de context sunt cele voluntare. | ||
==== I/O bound vs. CPU bound ==== | ==== I/O bound vs. CPU bound ==== | ||
- | Intrați în directorul ''ctxt-switch/'' din arhiva cu demo-uri a cursului. | + | Vrem să observăm diferența tipurilor de schimbări de context pentru procese I/O bound și procese CPU bound. Vom folosi directorul ''ctxt-switch/'' din arhiva cu demo-uri a cursului. |
- | * Parcurgeți fișierele ''cpu.c'' și ''io.c''. | + | |
- | * Compilați cele două programe folosind comanda:<code bash> | + | Parcurgem fișierele ''cpu.c'' și ''io.c''. Observăm că ''cpu.c'' face multe acțiuni (consumă procesor), în timp ce ''io.c'' face operații blocante. Compilăm cele două programe folosind comanda<code bash> |
make | make | ||
- | </code> | + | </code> și obținem executabilele ''cpu'' și ''io''. |
- | * Veți obține executabilele ''cpu'' și ''io''. | + | |
- | * Rulați executabilul ''cpu'':<code bash> | + | Întâi rulăm executabilul ''cpu'':<code bash> |
./cpu | ./cpu | ||
</code> | </code> | ||
- | * Într-o altă consolă urmăriți numărul de context switch-uri realizate de procesul nou creat:<code bash> | + | Într-o altă consolă urmărim numărul de schimbări de context realizate de procesul nou creat:<code bash> |
cat /proc/$(pidof cpu)/status | cat /proc/$(pidof cpu)/status | ||
</code> | </code> | ||
- | * Observați că se modifică preponderent valoarea câmpului ''nonvoluntary_ctxt_switches''. Cum explicați? | + | Observăm că se modifică preponderent valoarea câmpului ''nonvoluntary_ctxt_switches''. Acest lucru se întâmplă pentru că avem un proces CPU-bound. Acesta va consuma timp de procesor ori de câte ori prinde ocazia și va fi dat afară în majoritar la expirarea cuantei (nu face operații blocante). Un astfel de proces poartă numele de //CPU hog//. |
- | * Rulați executabilul ''io'':<code bash> | + | |
+ | Apoi rulăm executabilul ''io'':<code bash> | ||
./io | ./io | ||
</code> | </code> | ||
- | * Într-o altă consolă urmăriți numărul de context switch-uri realizate de procesul nou creat:<code bash> | + | Într-o altă consolă urmăriți numărul de schimbări de context realizate de procesul nou creat:<code bash> |
cat /proc/$(pidof io)/status | cat /proc/$(pidof io)/status | ||
</code> | </code> | ||
- | * Observați că se modifică preponderent valoarea câmpului ''voluntary_ctxt_switches''. Cum explicați? | + | Observăm că se modifică preponderent valoarea câmpului ''voluntary_ctxt_switches''. Acest lucru se întâmplă pentru că avem un proces IO-bound. Acesta va executa multe operații blocante în cadrul cărora va ceda de bună voie procesorul. |
- | * De ce în modificarea câmpului ''voluntary_ctxt_switches'' se face doar în cazul apelului ''sleep()'', nu și în cazul apelului ''write()'' (apel de I/O)? | + | |
+ | La o privire mai atentă la câmpul ''voluntary_ctxt_switches'' pentru procesul creat din executabilul ''io'', observăm că acesta se modifică doar la apelul ''sleep'' nu și la apelul ''write'', deși ''write'' este un apel I/O cu probabilitate de a se bloca. În realitate însă un apel ''write'' nu va copia date pe disc (sau la alt dispozitiv I/O) ci într-un buffer intern al nucleului. După copierea datelor în buffer-ul intern al nucleului, apelul ''write'' se întoarce, neexistând partea de blocare. | ||
==== Cursorul de fișier la fork ==== | ==== Cursorul de fișier la fork ==== | ||
- | - Intrați în directorul ''fork-file-pointer/'' din arhiva cu demo-uri a cursului. | + | Pentru a urmări comportamentului cursorului de fișier (//file pointerului//) vom folosi directorul ''fork-file-pointer/'' din arhiva cu demo-uri a cursului. |
- | * Parcurgeți fișierele ''fork-file-pointer.c''. | + | |
- | * Compilați cele două programe folosind comanda:<code bash> | + | Vom parcurge fișierul ''fork-file-pointer.c''. Observăm că același descriptor de fișier este folosit și de procesul copil și de procesul părinte. Vrem să vedem dacă aceste două procese partajează cursorul de fișier. |
+ | |||
+ | Pentru început compilăm programul folosind comanda:<code bash> | ||
make | make | ||
</code> | </code> | ||
- | * Veți obține executabilele ''fork-file-pointer''. | + | Obținem executabilul ''fork-file-pointer''. Rulăm executabilul:<code bash> |
- | * Rulați executabilul ''fork-file-pointer'':<code bash> | + | |
./fork-file-pointer | ./fork-file-pointer | ||
</code> | </code> | ||
- | * Într-o altă consolă urmăriți evoluția cursorului de fișier pentru procesul creat (procesul părinte) folosind comanda:<code bash> | + | |
+ | Într-o altă consolă urmărim evoluția cursorului de fișier pentru procesul creat (procesul părinte) folosind comanda:<code bash> | ||
lsof -a -o -d 0-1023 -p $(pidof fork-file-pointer | cut -d ' ' -f 1) | lsof -a -o -d 0-1023 -p $(pidof fork-file-pointer | cut -d ' ' -f 1) | ||
</code> | </code> | ||
- | * Cursorul de fișier este indicat de coloana ''OFFSET''. | + | Observăm modificarea cursorului de fișier de la valoarea ''0'' la valoarea ''10'' apoi la valoarea ''20'' apoi la valoarea ''30''. |
- | * Vizualizați conținutul fișierului ''f.txt'' după rularea programului:<code bash> | + | |
+ | <note tip> | ||
+ | Cursorul de fișier este indicat de coloana ''OFFSET''. | ||
+ | </note> | ||
+ | După încheierea procesului urmărim conținutul fișierului ''f.txt'':<code bash> | ||
cat f.txt | cat f.txt | ||
</code> | </code> | ||
- | * Se observă că, în urma ''fork()'', se partajează cursorul de fișier al fișierelor deschise înainte de ''fork()''. | ||
+ | Observăm că fiecare proces (părinte sau copil) a scris în continuarea celuilalt. Adică, în urma ''fork()'', se partajează cursorul de fișier al fișierelor deschise înainte de ''fork()''. Procesul părinte partajează cursorul de fișier cu procesele copil. | ||
==== Procese orfane și procese zombie ==== | ==== Procese orfane și procese zombie ==== | ||
- | - Intrați în directorul ''orphan-zombie/'' din arhiva cu demo-uri a cursului. | + | Vrem să urmărim apariția proceselor orfane și a proceselor zombie și să le investigăm. Pentru aceasta vom folosi directorul ''orphan-zombie/'' din arhiva cu demo-uri a cursului. |
- | * Parcurgeți fișierele ''orphan.c'' și ''zombie.c''. | + | |
- | * Compilați cele două programe folosind comanda:<code bash> | + | Parcurgem fișierele ''orphan.c'' și ''zombie.c''. Primul program creează un proces orfan, adică își încheie execuția procesul părinte, dar procesul copil continuă execuția. Al doilea program creează un proces zombie, adică procesul copil își încheie execuția, dar procesul părinte continuă execuția fără a aștepta procesul copil. |
+ | |||
+ | Compilăm cele două programe folosind comanda<code bash> | ||
make | make | ||
- | </code> | + | </code> și obținem executabilele ''orphan'' și ''zombie''. |
- | * Veți obține executabilele ''orphan'' și ''zombie''. | + | |
- | * Rulați executabilul ''orphan'':<code bash> | + | Rulăm executabilul ''orphan'':<code bash> |
./orphan | ./orphan | ||
</code> | </code> | ||
- | * Într-o altă consolă urmăriți PID-urile și PPID-urile celor două procese (părinte și copil):<code bash> | + | Într-o altă consolă urmărim PID-urile și PPID-urile celor două procese (părinte și copil):<code bash> |
watch -n 1 ps -f -C orphan | watch -n 1 ps -f -C orphan | ||
</code> | </code> | ||
- | * Urmăriți mesajele afișate de procesul copil. Mesajele vor fi afișate și după terminarea procesului părinte. | + | Urmărim mesajele afișate de procesul copil. Mesajele vor fi afișate și după terminarea procesului părinte. Observăm actualizarea PID-ului procesului părinte (PPID) pentru procesul copil: după ce a rămas orfan, procesul copil a fost adoptat de ''init'' (procesul cu PID-ul ''1''). |
- | * Observați actualizarea PID-ului procesului părinte (PPID) pentru procesul copil. Observați înfierea acestuia de procesul ''init''. | + | |
- | * Rulați executabilul ''zombie'':<code bash> | + | Rulăm executabilul ''zombie'':<code bash> |
./zombie | ./zombie | ||
</code> | </code> | ||
- | * Într-o altă consolă urmăriți cele două procese (părinte și copil):<code bash> | + | Într-o altă consolă urmărim cele două procese (părinte și copil):<code bash> |
watch -n 1 ps -f -C zombie | watch -n 1 ps -f -C zombie | ||
</code> | </code> | ||
- | * Observați trecerea procesului copil în starea //zombie//; în ieșirea comenzii ''ps'' apare șirul ''<defunct>''. | + | Observăm trecerea procesului copil în starea //zombie//; în ieșirea comenzii ''ps'' apare șirul ''<defunct>''. Observăm eliminarea procesului //zombie// după așteptarea sa de procesul părinte. |
- | * Observați eliminarea procesului //zombie// după așteptarea sa de procesul părinte. | + | |
+ | Un proces este //zombie// pentru că reține niște informație reziduală legată de modul în care și-a încheiat execuția; această informație reziduală este utilă procesului părinte. În momentul în care procesul părinte citește această informație reziduală folosind un apel din familia ''wait'', procesul copil, acum //zombie//, își încheie complet execuția și dispare din sistem. |