This shows you the differences between two versions of the page.
so:cursuri:curs-10 [2013/04/26 19:29] mihai.barbulescu [Curs 10 - Implementarea sistemelor de fișiere] Typo - in loc de Curs 10 Virtualizare (care este de fapt 11) --> Curs 10 Implementare sistem de fisiere |
so:cursuri:curs-10 [2019/04/20 20:24] (current) razvan.deaconescu |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Curs 10 - Implementarea sistemelor de fișiere ====== | + | ====== Curs 10 - Dispozitive de intrare/ieșire ====== |
- | <html> | + | * [[http://prezi.com/z_hkmewcaqm_/?utm_campaign=share&utm_medium=copy&rc=ex0share|Curs 10 - Dispozitive de Intrare/Iesire (Prezi)]] |
- | <iframe src="http://prezi.com/embed/i7gfiypqqpje/?bgcolor=ffffff&lock_to_path=0&autoplay=0&autohide_ctrls=0&features=undefined&disabled_features=undefined" width="550" height="400" frameBorder="0"></iframe> | + | * [[http://elf.cs.pub.ro/so/res/cursuri/SO_Curs-10.pdf|Curs 10 - Dispozitive de Intrare/Iesire (PDF)]] |
- | </html> | + | |
- | * [[http://prezi.com/i7gfiypqqpje/curs-10-so/?kw=view-i7gfiypqqpje&rc=ref-31844697 | Curs 10 - Implementarea sistemelor de fișiere (PREZI)]] | + | * [[https://docs.google.com/document/d/1dc5Kaamjo6WPy7LABkBGTcxjVwgl3JbnlHnE0iREmwM/edit?usp=sharing|Notițe de curs]] |
- | * [[http://elf.cs.pub.ro/so/res/cursuri/SO_Curs-10.pdf | Curs 10 - Implementarea sistemelor de fișiere (PDF)]] | + | |
* Suport curs | * Suport curs | ||
- | * Operating System Concepts | + | * Operating Systems Concepts Essentials |
- | * Capitolul 11 – File-System Implementation | + | * Capitolul 11 -- Mass Storage Structure |
- | * Secțiunile 11.1 – 11.8 | + | * Secțiunile 11.2, 11.4, 11.7 |
+ | * Capitolul 12 -- I/O Systems | ||
* Modern Operating Systems | * Modern Operating Systems | ||
- | * Capitolul 6 – File Systems | + | * Capitolul 5 -- Input/Output |
- | * Secțiunile 6.3, 6.4 | + | * Secțiunile 5.1, 5.2, 5.3 |
+ | * Subsecțiunile 5.4.1, 5.4.3 | ||
+ | |||
+ | <html> | ||
+ | <center> | ||
+ | <iframe src="https://prezi.com/embed/z_hkmewcaqm_/?bgcolor=ffffff&lock_to_path=0&autoplay=0&autohide_ctrls=0&features=undefined&disabled_features=undefined" width="550" height="400" frameBorder="0"></iframe> | ||
+ | </center> | ||
+ | </html> | ||
===== Demo-uri ===== | ===== Demo-uri ===== | ||
- | Pentru parcurgerea demo-urilor, folosiți [[http://elf.cs.pub.ro/so/res/cursuri/curs-10.zip|arhiva aferentă]]. | + | Pentru parcurgerea demo-urilor, folosim [[http://elf.cs.pub.ro/so/res/cursuri/curs-10-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-10-demo.zip | ||
+ | </code> și apoi decomprimăm arhiva<code bash> | ||
+ | unzip curs-10-demo.zip | ||
+ | </code> și accesăm directorul rezultat în urma decomprimării<code bash> | ||
+ | cd curs-10-demo/ | ||
+ | </code> | ||
- | - Folosiți comanda ''stat'' pentru a afișa informații despre inode-ul corespunzător unui fișier, director, link simbolic, char device, block device:<code bash> | + | Acum putem parcurge secțiunile cu demo-uri de mai jos. |
- | stat /etc/passwd | + | |
- | stat /usr/lib/ | + | ==== Investigare întreruperi ==== |
- | stat /usr/bin/cc | + | |
- | stat /dev/null | + | Dorim să investigăm livrarea întreruperilor hardware către sistemul local. Vom urmări întreruperea de ceas (timer) și întreruperea de tastatură. |
- | stat /dev/loop0 | + | |
+ | În Linux fișierul care afișează informații despre tastatură este ''/proc/interrupts''. În acest fișier sunt afișate informații statistice despre întreruperilor livrate, câte o coloană pentru fiecare procesor. În cadrul fișierului găsim informații și despre întreruperea de ceas și cea de tastatură:<code> | ||
+ | $ cat /proc/interrupts | ||
+ | CPU0 CPU1 CPU2 CPU3 | ||
+ | 0: 14 0 0 0 IR-IO-APIC-edge timer | ||
+ | 1: 1358967 0 0 0 IR-IO-APIC-edge i8042 | ||
+ | [...] | ||
+ | LOC: 96023026 47877718 90900926 54219891 Local timer interrupts | ||
+ | [...] | ||
</code> | </code> | ||
- | * Afișați informații despre un socket Unix, după ce îl căutați în ierarhia ''/var/run/'':<code bash> | + | |
- | sudo find /var/run -type s | + | Prima coloană este linia de întrerupere (''IRQ line'') pe care se livrează semnalul de întrerupere către procesor. Este 0 în cazul [[http://en.wikipedia.org/wiki/Intel_8253|timer-ului programabil (8253)]] și 1 în cazul [[http://en.wikipedia.org/wiki/Keyboard_controller_(computing)|controller-ului de tastatură (8042)]]. |
- | sudo stat /var/run/udev/control | + | |
+ | Din output-ul comenzii de mai sus am selectat doar intrările referitoare la întreruperea de ceas (''timer'' și //Local timer interrupts//) și cea de tastatură (''i8042''). La noi rulări ale comenzii<code> | ||
+ | cat /proc interrupts | ||
</code> | </code> | ||
- | * Aflați dimensiunea fișierului ''/sbin/init'' folosind ''stat'':<code bash> | + | vom observa actualizări ale valorilor întreruperilor. În figura de mai sus întreruperea de tastatură (''i8042'') este tratată doar pe primul procesor (''CPU0'') în vreme ce există întreruperi de ceas locale pentru fiecare procesor (de unde și denumirea de //local//). |
- | stat -c %s /sbin/init | + | |
+ | Pe sistemele multi-core/multiprocesor se folosesc întreruperile de ceas locale (cele indicate de //Local timer interrupts//). Acestea sunt livrate de controller-ul de întreruperi (APIC) către fiecare procesor în parte. Din acest motiv există actualizări pentru linia identificată de ''LOC:'' dar nu și pentru timer-ul programabil de pe linia identificată cu ''timer''. Pe sisteme uniprocesor, pentru livrarea întreruperii de ceas se folosește timer-ul programabil.((What are Linux local timer interrupts? http://stackoverflow.com/questions/10567214/what-are-linux-local-timer-interrupts)) | ||
+ | |||
+ | Informațiile despre întreruperea de ceas sunt actualizate periodic în fișierul ''/proc/interrupts'' pentru că la un interval dat se livrează o nouă întrerupere. | ||
+ | |||
+ | Întreruperile de tastatură sunt livrate la fiecare apăsare de tastă. Pentru a investiga evoluția numărului de întreruperi de tastatură livrate, tastăm șirul de comenzi de mai jos:<code> | ||
+ | $ cat /proc/interrupts | grep ' 1:' | ||
+ | 1: 1366012 0 0 0 IR-IO-APIC-edge i8042 | ||
+ | $ echo 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' | ||
+ | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | ||
+ | $ cat /proc/interrupts | grep ' 1:' | ||
+ | 1: 1366184 0 0 0 IR-IO-APIC-edge i8042 | ||
</code> | </code> | ||
- | * Urmăriți modificările timpilor de acces pe fișier (//access time//, //modify time//, //change time//) folosind comenzile de mai jos:<code bash> | + | |
- | cd /tmp | + | <note important> |
- | touch a.txt | + | Șirul de comenzi de mai sus trebuie tastat pentru a genera întreruperi. **Nu** dăm copy-paste la șir. |
- | stat a.txt | + | </note> |
- | cat a.txt | + | |
- | stat a.txt | + | Între prima afișare și a doua afișare a conținutului fișierul ''/proc/interrupts'' am tastat circa 70-80 de taste pentru comanda ''echo ...'' și a doua comandă ''cat'' (am mai apăsat și pe tasta Backspace când am greșit). Observăm că diferența între cele două rezultate (întreruperi livrate) este ''1336184 - 1336012 = 172''. Au fost livrate circa dublu de întreruperi față de tastele apăsate. Acest lucru se întâmplă pentru că se livrează o întrerupere pentru fiecare apăsare de tastă (//key press//) și una pentru fiecare eliberare de tastă (//key release//); de unde și numărul dublu de întreruperi livrate față de taste apăsate. |
- | chmod a+rw a.txt | + | ==== Utilitate disk cache ==== |
- | stat a.txt | + | |
- | echo "abc" > stat.txt | + | Dorim să urmărim efectul cache-ul de disk (numit și //page cache//). Acesta stochează datele frecvent accesate în memorie astfel că viitoare folosiri ale acestora să fie mai rapide. Vom copia de două ori un fișier și vom observa că a doua oară copierea este mai rapidă datorită cache-ului. |
- | stat a.txt | + | |
+ | Creăm un fișier ''in.dat'' de 100MB de date aleatoare folosind comanda ''dd'':<code bash> | ||
+ | $ dd if=/dev/urandom bs=1M count=100 of=in.dat | ||
+ | 100+0 records in | ||
+ | 100+0 records out | ||
+ | 104857600 bytes (105 MB) copied, 7.14143 s, 14.7 MB/s | ||
</code> | </code> | ||
- | * //access time// este timpul de acces al fișierului, deschis pentru citire. | + | |
- | * //modify time// este timpul de modificare a datelor fișierului. | + | Apoi copiem fișierul ''in.dat'' în fișierul ''out.dat'' folosind comanda ''cp'' și măsurând durata de execuție cu ajutorul comenzii ''time'':<code bash> |
- | * //change time// este timpul de modificare a metadatelor fișierului (informații din inode). | + | razvan@einherjar:~$ /usr/bin/time -v cp in.dat out.dat |
- | - Creați un fișier simplu și urmăriți numărul de link-uri ale acestuia:<code bash> | + | Command being timed: "cp in.dat out.dat" |
- | cd /tmp | + | User time (seconds): 0.00 |
- | touch first.txt | + | System time (seconds): 0.09 |
- | ls -i first.txt | + | Percent of CPU this job got: 100% |
- | ls -l first.txt | + | Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.09 |
+ | [...] | ||
+ | Voluntary context switches: 1 | ||
+ | Involuntary context switches: 2 | ||
+ | Swaps: 0 | ||
+ | File system inputs: 0 | ||
+ | File system outputs: 204800 | ||
+ | [...] | ||
</code> | </code> | ||
- | * A doua coloana de la rularea ''ls -l'' este numărul de link-uri, inițial 1. | + | Observăm ca a durat foarte puțin copierea, sub ''10'' milisecunde în rularea de mai sus. Aceasta s-a întâmplat pentru că fișierul ''in.dat'' a fost cache-uit în page cache de la rularea comenzii ''dd'', când a fost generat. |
- | * Creați un fișier ca hard link la primul:<code bash> | + | |
- | ln first.txt second.txt | + | Ca să urmărim ce se întâmplă când fișierul nu se găsește în page cache, facem flush la page cache și apoi invalidăm intrările folosind comenzile de mai jos:<code bash> |
+ | $ free -m | ||
+ | total used free shared buffers cached | ||
+ | Mem: 7893 4715 3178 251 728 1687 | ||
+ | -/+ buffers/cache: 2299 5593 | ||
+ | Swap: 3813 427 3386 | ||
+ | $ sudo sync | ||
+ | $ echo 1 | sudo tee /proc/sys/vm/drop_caches | ||
+ | 1 | ||
+ | $ free -m | ||
+ | total used free shared buffers cached | ||
+ | Mem: 7893 2653 5240 251 0 397 | ||
+ | -/+ buffers/cache: 2255 5637 | ||
+ | Swap: 3813 427 3386 | ||
</code> | </code> | ||
- | * Observați că inode-ul este același iar numărul de link-uri este egal cu doi:<code bash> | + | |
- | ls -i first.txt second.txt | + | Am folosit comanda ''free'' doar pentru a raporta folosirea bufferelor și a cache-ului. După rularea comenzilor ''sync'' și ''echo ...'' observăm eliberarea completă a bufferelor și aproape compleă a cache-ului.((Linux memory: buffer vs cache: http://stackoverflow.com/questions/6345020/linux-memory-buffer-vs-cache)) |
- | ls -l first.txt second.txt | + | |
+ | Copiem din nou fișierul ''in.dat'' într-un nou fișier ''out2.dat'' și folosim comanda ''time'' pentru a măsura durata:<code bash> | ||
+ | $ /usr/bin/time -v cp in.dat out2.dat | ||
+ | Command being timed: "cp in.dat out2.dat" | ||
+ | User time (seconds): 0.00 | ||
+ | System time (seconds): 0.22 | ||
+ | Percent of CPU this job got: 11% | ||
+ | Elapsed (wall clock) time (h:mm:ss or m:ss): 0:02.02 | ||
+ | [...] | ||
+ | Voluntary context switches: 451 | ||
+ | Involuntary context switches: 11 | ||
+ | Swaps: 0 | ||
+ | File system inputs: 205632 | ||
+ | File system outputs: 204800 | ||
+ | [...] | ||
</code> | </code> | ||
- | * Creați un al treilea link și observați numărul de link-uri egal cu 3:<code bash> | + | Observăm că acum operația durează semnificativ mai mult (2 secunde). De asemenea încărcarea pe procesor este mai mică (''11%'' față de ''100%'' pentru că acum procesorul așteaptă după disc. Observăm diferența mare de schimbări de context voluntare (procesul se blochează) și numărul diferit de zero de citiri din sistemul de fișiere: acum întreg fișierul de intrare este citit de pe disc, nu mai este cache-uit în memorie. |
- | ln second.txt third.txt | + | |
- | ls -i first.txt second.txt third.txt | + | Cache-ul de disk este esențial pentru funcționarea eficientă a sistemului. În absența acestuia procesorul ar trebui să solicite informații de pe disc, dispozitiv foarte lent comparativ cu memoria și procesor. Trebuie avut grijă la flush-ul/sincronizarea periodică a informațiilor din memorie pe disc pentru ca acestea să nu se piardă la o închidere bruscă a sistemului, memoria fiind volatilă. |
- | ls -l first.txt second.txt third.txt | + | ==== Copiere conținut CD/DVD în fișier .iso ==== |
+ | |||
+ | Dorim să creăm facil un fișier format .iso conținând imaginea unui CD/DVD. Pentru aceasta comanda de creare este una foarte simplă ce folosește ''dd'':<code bash> | ||
+ | $ dd if=/dev/cdrom of=file.iso bs=1M | ||
</code> | </code> | ||
- | * Ștergeți primele două link-uri/nume și obsevați păstrarea inode-ului și reducerea numărului de link-uri la 1 pentru ultimul nume:<code bash> | + | |
- | unlink first.txt | + | Comanda copiază octet cu octet conținutul CD-ului/DVD-ului, indicat de dispozitivul ''/dev/cdrom'' și îl scrie în fișierul ''file.iso''. Întrucât comanda ''dd'' acționează și pe dispozitive, va reieși un fișier în format ''.iso'', copia sector cu sector a discului. |
- | rm second.txt | + | |
- | ls -i third.txt | + | Pentru a urmări evoluția comenzii, dintr-un alt terminal putem rula comanda:<code bash> |
- | ls -l third.txt | + | $ kill -USR1 $(pidof dd) |
</code> | </code> | ||
- | * ''unlink'' și ''rm'' pot fi folosite interschimbabil. | + | Livrarea semnalului ''USR1'' procesului ''dd'' înseamnă afișarea unui raport de stare din partea comenzii ''dd'' (adică în primul terminal). |
- | - Urmăriți numărul de link-uri/nume aferente unui director gol. Numărul este 2: un link este numele efectiv al directorului, iar altul este referința '.' (punct, //dot//) către director însuși:<code bash> | + | |
- | cd /tmp | + | ==== Folosire operație de control (ioctl) ==== |
- | mkdir /tmp/test-d | + | |
- | ls -ld /tmp/test-d | + | Dorim să urmărim câteva cazuri de folosire a funcției [[http://man7.org/linux/man-pages/man2/ioctl.2.html|ioctl]]. Funcția este utilizată pentru operații de control a unui dispozitiv sau de citire a unei stări sau a configurații, acolo unde funcțiile [[http://man7.org/linux/man-pages/man2/read.2.html|read]] sau [[http://man7.org/linux/man-pages/man2/write.2.html|write]] nu pot fi folosite. Apelurile ''ioctl'' sunt folosite în special pe fișiere speciale (dispozitive, sockeți, terminale etc.); dezavantajul este că nu există o formă standard a operațiilor aferente și în general nu sunt portabile. O listă **incompletă** de operații se găsește în [[http://man7.org/linux/man-pages/man2/ioctl_list.2.html|pagina de manual ioctl_list]]. |
+ | |||
+ | Pentru a urmări folosirea apelului ''ioctl'' accesăm subdirectorul ''ioctl/''; urmărim conținutul fișierelor ''cdrom-ioctl.c'' și ''hwaddr-ioctl.c''. Fișierele implementează, respectiv, operații pe CD-ROM drive și pe socket pentru aflarea adresei hardware (MAC). | ||
+ | |||
+ | Pentru fișierul sursă ''cdrom-ioctl.c'' înlocuim valoarea macro-ului ''CDROM_DEV_PATH'' cu șirul ce reprezintă calea către CD-ROM. La fel, în fișierul ''hwaddr-ioctl.c'' înlocuim valoarea macro-ului ''IFNAME'' cu șirul ce reprezintă numele interfeței a cărei adresă hardware dorim să o determinăm. | ||
+ | |||
+ | Compilăm fișierele folosind ''make''. Rezultă două fișiere în format executabil: ''cdrom-ioctl'' și ''hwaddr-ioctl''. | ||
+ | |||
+ | Cu ajutorul executabilului ''cdrom-ioctl'' putem executa trei tipuri de operații pe CD-ROM așa cum este indicat prin rularea sa fără argumente:<code> | ||
+ | $ ./cdrom-ioctl | ||
+ | You must provide exactly one argument. | ||
+ | |||
+ | Usage: ./cdrom-ioctl e|l|u | ||
+ | e - eject CD-ROM | ||
+ | l - lock CD-ROM drive | ||
+ | e - unlock CD-ROM drive | ||
</code> | </code> | ||
- | * Creați un subdirector al directorului ''/tmp/test-d/''. Numărul de link-uri va crește la 3, cu ajutorul referinței '..' (punct punct, //dot dot//) de la subdirector:<code bash> | + | |
- | mkdir /tmp/test-d/test-subd | + | În continuare putem rula comanda ''cdrom-ioctl'' cu exact unul dintre argumentele ''e'', ''l'', ''u'' pentru a deschide trapa CD-ROM drive-ului saua bloca/debloca deschiderea manuală a acestuia (din buton). După cum vedem din codul sursă, aceste trei operații sunt realizate cu un apel ''ioctl'' și cu parametrii aferenți (documentați în [[http://lxr.free-electrons.com/source/Documentation/ioctl/cdrom.txt?v=3.13|sursele nucleului Linux]]): ''CDROMEJECT'' și ''CDROM_LOCKDOOR''. |
- | ls -ld /tmp/test-d | + | |
- | </code> | + | Cu ajutorul executabilului ''hwaddr-ioctl'' putem determina adresa hardware (MAC) a unei interfețe de rețea așa cum se întâmplă mai jos:<code> |
- | * Creați un subfișier al directorul ''/tmp/test-d/''. Numărul de link-uri nu se va modifica, întrucât fișierul nu are o referință la directorul părinte:<code bash> | + | $ ./hwaddr-ioctl |
- | touch /tmp/test-d/test-f | + | Hardware address for interface eth0 is 00:21:cc:68:d0:53 |
- | ls -ld /tmp/test-d | + | |
</code> | </code> | ||
- | - Creați un director simplu și vedeți ce spațiu ocupă pe disc, folosind comanda ''stat'':<code bash> | + | |
- | cd /tmp | + | Adresa hardware a fost obținută într-o structură [[http://lxr.free-electrons.com/source/include/uapi/linux/if.h?v=3.13#L170|struct ifreq]] cu ajutorul unui apel ''ioctl'' aplicat pe un descriptor de socket. Parametrul ''ioctl'' a fost ''SIOCGIFHWADDR''. |
- | mkdir /tmp/fill-d | + | |
- | stat -c %s /tmp/fill-d | + | |
- | </code> | + | |
- | * Copiați scriptul ''create-long-filenames'' din arhivă și rulați-l în directorul proaspăt creat:<code bash> | + | |
- | cd /tmp/fill-d | + | |
- | # TODO: copy script | + | |
- | ./create-long-filenames | + | |
- | </code> | + | |
- | * Observați conținutul directorului și noua dimensiune a acestuia:<code bash> | + | |
- | ls -l /tmp/fill-d | + | |
- | stat -c %s /tmp/fill-d | + | |
- | </code> | + | |
- | * Dimensiunea directorului a crescut datorită numărului de intrări în director. | + | |
- | * Dimensiunea directorului este multiplu de dimensiunea blocului sistemului. | + |