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-02-demo.zip
și apoi decomprimăm arhiva
unzip curs-02-demo.zip
și accesăm directorul rezultat în urma decomprimării
cd curs-02-demo/
Acum putem parcurge secțiunile cu demo-uri de mai jos.
Pentru a afișa tabela de descriptor de fișier a procesului shell current (PID-ul său este reținut în construcția $$
), folosim comanda
lsof -a -d 0-1023 -p $$
Ce referă descriptorii 0, 1, 2?
De ce (pe unele sisteme) descriptorul 0 este marcat 0r
(adică read-only)?
0u
adică este read-write; dar, în mod practic, este folosit doar pentru citire.
Vrem să investigăm descriptorii de fișiere pentru procesele daemon din sistem. Pentru început vrem să aflăm procesele daemon din sistem; procesele daemon au ca proces părinte procesul init
(procesul cu PID-ul 1
) și vom folosi comanda de mai jos pentru a le afla:
ps --ppid 1
Pentru unul dintre procesele daemon descoperite prin rularea comenzii anterioare, afișăm tabela de descriptori (e nevoie de drept de root
) folosind comanda lsof
:
sudo lsof -a -d 0-1023 -p $PID
Mai sus, construcția $PID
referă PID-ul procesului daemon inspectat.
Ce referă descriptorii 0, 1, 2? De ce?
Ce alți descriptori sunt folosiți? Ce referă acești descriptori?
Vrem să vedem cum se modifică descriptorii de fișier în cazul redirectării. Pentru a putea vedea acest lucru vom rula o comandă de durată (sleep
) și vom redirecta intrarea și ieșirea standard:
sleep 100 < /etc/passwd > f.txt
Pentru a investiga procesul sleep
proaspăt pornit, trebuie să știm PID-ul său. Deschidem o altă consolă și aflăm PID-ul procesului sleep
creat folosind comanda:
pidof sleep
Vom folosi construcția $PID
referă PID-ul procesului sleep
pe care-l investigăm. Ca și până acum, afișam tabela de descriptori de fișier a procesului folosind comanda:
lsof -a -o -d 0-1023 -p $PID
Ce referă acum descriptorul 0, respectiv 1?
Vrem să urmărim modificarea cursorului de fișier (numit și file pointer sau file offset). Pentru aceasta vom folosi un program C în care, la cererea utilizatorului folosim apeluri care alterează cursorul de fișier: write
și lseek
,
Pentru început intrăm în subdirectorul c-file-ops/
din directorul cu demo-uri și urmărim fișierul c-file-ops.c
. Observăm că în program se deschide fișierul f.txt
și apoi se scrie (folosind write
) și se parcurge fișierul (folosind lseek
). Fiecare operație este precedată de apăsarea tastei ENTER din parte utilizatorului. Compilăm programul folosind comanda
user@host:~$ make
și obținem executabilul c-file-ops
. Rulăm executabilul c-file-ops
:
./c-file-ops
Pentru a urmări evoluția tabelei de descriptori și a cursorului de fișier, vom folosi comanda lsof
. Într-o altă consolă rulăm comanda
lsof -a -o -d 0-1023 -p $(pidof c-file-ops)
Pentru început sunt afișați doar descriptorii standard.
Pentru a urmări evoluția tabelei de descriptori de fișier și a cursorului de fișier, vom folosi ENTER
în consola în care am rulat executabilul c-file-ops
. Vom urmări evoluția programului în consola în care am rulat lsof
.
OFFSET
indică poziția cursorului de fișier.
Inițial cursorul de fișier are valoarea 0
întrucât a fost proaspăt deschis și trunchiat. La apăsarea tastei ENTER
în prima consolă, care indică programul să facă o nouă acțiune, observăm modificarea cursorului de fișier în a doua consolă.
Parcurgem întreg programul pentru a urmări evoluția completă a cursorului de fișier.
La finalul rulării programului urmărim dimensiunea fișierului f.txt
:
stat -c "%s" f.txt 256
Observăm că fișierul are dimensiunea de 256 octeți, atât cât a primit ca argument apelul ftruncate
.
Ce efect are apelul ftruncate
asupra cursorului de fișier?
Vrem să vedem cum este alterat cursorul de fișier în cazul unui program Python. Pentru aceasta, accesăm subdirectorul py-file-ops/
din directorul cu demo-uri al cursului și parcurgem fișierul py-file-ops.py
. Observăm că structura este similară celei de la demo-ul anterior: se scriu date în fișier, se parcurge fișierul, se așteaptă apăsarea tastei ENTER
din partea utilizatorului.
Rulăm programul folosind comanda:
python py-file-ops.py
Pentru a urmări evoluția tabelei de descriptori și a cursorului de fișier, deschidem o altă consolă în care rulăm comanda
lsof -a -o -d 0-1023 -p $(pgrep -f py-file-ops)
Sunt afișați doar descriptorii standard pentru că încă nu a fost deschis fișierul.
Pentru a deschide fișierul și a efectua oprații asupra sa, vom apăsa ENTER
în consola în care am rulat programul py-file-ops
. Urmărim evoluția programului în consola în care ați rulat lsof
.
OFFSET
indică poziția cursorului de fișier.
Folosim de mai multe ori ENTER
pentru a parcurge toți pașii din program și pentru a investiga evoluția cursorului de fișier. La fel ca la demo-ul anterior, fișierul va avea în final dimensiunea de 256
octeți:
stat -c "%s" f.txt 256
De ce nu există tot timpul o corespondență între operațiile Python și modificarea cursorului de fișier? De exemplu, în cazul f.read()
, deși trebuia să se incrementeze contorul cu 256
octeți, a fost incrementat cu 512
.
Ce se întâmplă dacă nu se apelează f.flush()
(după ciclul de scriere cu f.write()
)?
Ne propunem să investigăm diferențele dintre apelurile de tip buffered I/O și cele de tip system-level I/O. Intrăm în subdirectorul buffered-system-io/
din directorul cu demo-uri ale cursului și urmărim fișierele buffered.c
și system.c
. Observăm că operațiile efectuate sunt similare; diferă doar funcțiile folosite, unele de tip buffered I/O, altele de tip system-level I/O.
Compilăm cele două programe folosind comanda:
make
Pentru a investiga apelurile de bibliotecă ale programului buffered
executăm rapid pașii:
buffered
:./buffered
buffered
folosind comanda:ltrace -e putchar,fputc -p $(pidof buffered)
Pentru a investiga apelurile de sistem ale programului buffered
executăm rapid pașii:
buffered
:./buffered
buffered
folosind comanda:strace -e write -p $(pidof buffered)
Care este numărul de apeluri de bibliotecă și numărul de apeluri de sistem realizate de programul buffered
în cadrul buclelor for
?
Realizați pașii de mai sus și pentru programul system
. Doar că pentru a detecta apelurile de bibliotecă realizate folosiți comanda
ltrace -e write -p $(pidof system)
Care este numărul de apeluri de bibliotecă și numărul de apeluri de sistem realizate de programul system
?
Ne propunem să urmărim efectul apelului dup()
asupra cursorului de fișier. Pentru aceasta accesăm subdirectorul open-dup/
din directorul cu demo-uri a cursului și parcurgem fișierele open.c
și dup.c
. În ambele fișiere se deschid doi descriptori de fișier (fd1
și fd2
): în cazul fișierului open.c
descriptorul fd2
este creat folosind apelul open()
, iar în cazul fișierului dup.c
descriptorul fd2
este creat folosind apelul dup()
.
Compilăm programele folosind comanda
make
și obținem executabilele open
și dup
.
Rulăm executabilul open
:
./open
Pentru a urmări tabela de descriptori de fișier și cursorul de fișier pentru procesul creat, deschidem o altă consolă și rulăm comanda:
watch -d lsof -a -o -d 0-1023 -p $(pidof open)
În consola în care am rulat executabilul open
folosim comanda ENTER
pentru a parcurge pașii din program. Urmărim în a doua consolă evoluția tabelei de descriptori și a cursorului de fișier.
OFFSET
indică poziția cursorului de fișier.
Realizați aceeași pași pentru executabilul dup
.
Ce diferențe apar la nivelul tabelei de descriptori de fișier și cursor de fișier între programul open
și programul dup
?