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-03-demo.zip
și apoi decomprimăm arhiva
unzip curs-03-demo.zip
și accesăm directorul rezultat în urma decomprimării
cd curs-03-demo/
Acum putem parcurge secțiunile cu demo-uri de mai jos.
Pentru a afișa informații despre procesul shell curent, folosim comenzile de mai jos:
ps -f $$ ps -F $$ lsof -p $$ pmap $$ ls /proc/$$/ cat /proc/$$/status
Aceste comenzi ne afișează infomații precum PID-ul procesului, comanda de la care a pornit, PID-ul procesului părinte, timpul de rulare, fișierele deschise (lsof
), spațiul de adresă al procesului (pmap
).
Dorim să aflăm informații complete despre un proces creat. Pentru aceea pornim în shell o comandă (find
) care să creeze un proces:
/usr/bin/time -v find /usr/share > /dev/null
Comanda time
trebuie dată în cale completă (/usr/bin/time
) pentru a nu rula comanda time
internă shell-ului.
Comanda permite rularea altei comenzi și afișarea de informații despre procesul creat de aceasta. Astfel de informații sunt cel de timp:
Procesul rulează pe procesor timp de User time + System time. Observăm că Elapsed time este mai mare decât timpul efectiv de rulare a procesului pe procesor. Acest lucru se întâmplă întrucât în acest timp au mai rulat și alte procese procesor, timp în care procesul curent a așteptat.
Pentru a afișa ierarhia de procese a sistemului folosim comenzile de mai jos:
pstree ps -H
Rădăcina ierarhiei este procesul init
(PID-ul 1), procesul părinte al sistemului.
Vrem să vizualizăm evoluția arborelui de procese. Vom vizualiza subarborele unui proces shell.
Pentru început, aflăm PID-ul procesului shell curent:
echo $$
Fie $PID
valorea afișată de comanda de mai sus. Deschidem un shell nou și rulăm comanda:
watch -n 1 pstree -a -p $PID
Comanda de mai sus afișează, cu refresh de o secundă, ierarhia de procese începând cu shell-ul inițial.
Pentru a altera ierarhia, rulăm comenzile de mai jos:
sleep 20 & bash sleep 30 # asteptati terminarea comenzii exit
Observăm evoluția ierarhiei în al doilea shell:
sleep 20
și procesul bash
nou creat.sleep
și așteaptă încheierea sa. În acest moment shell-ul inițial are un proces copil sleep
, un proces copil bash
și un proces “nepot” sleep
.
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:
cat /proc/$$/status
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 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.
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.
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
make
și obținem executabilele cpu
și io
.
Întâi rulăm executabilul cpu
:
./cpu
Într-o altă consolă urmărim numărul de schimbări de context realizate de procesul nou creat:
cat /proc/$(pidof cpu)/status
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 majorir la expirarea cuantei (nu face operații blocante). Un astfel de proces poartă numele de CPU hog.
Apoi rulăm executabilul io
:
./io
Într-o altă consolă urmăriți numărul de schimbări de context realizate de procesul nou creat:
cat /proc/$(pidof io)/status
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.
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.
fork-file-pointer/
din arhiva cu demo-uri a cursului.fork-file-pointer.c
.make
fork-file-pointer
.fork-file-pointer
:./fork-file-pointer
lsof -a -o -d 0-1023 -p $(pidof fork-file-pointer | cut -d ' ' -f 1)
OFFSET
.f.txt
după rularea programului:cat f.txt
fork()
, se partajează cursorul de fișier al fișierelor deschise înainte de fork()
.orphan-zombie/
din arhiva cu demo-uri a cursului.orphan.c
și zombie.c
.make
orphan
și zombie
.orphan
:./orphan
watch -n 1 ps -f -C orphan
init
.zombie
:./zombie
watch -n 1 ps -f -C zombie
ps
apare șirul <defunct>
.