Curs 10 - Dispozitive de intrare/ieșire

  • Suport curs
    • Operating Systems Concepts Essentials
      • Capitolul 11 – Mass Storage Structure
        • Secțiunile 11.2, 11.4, 11.7
      • Capitolul 12 – I/O Systems
    • Modern Operating Systems
      • Capitolul 5 – Input/Output
        • Secțiunile 5.1, 5.2, 5.3
        • Subsecțiunile 5.4.1, 5.4.3

Demo-uri

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-10-demo.zip

și apoi decomprimăm arhiva

unzip curs-10-demo.zip

și accesăm directorul rezultat în urma decomprimării

cd curs-10-demo/

Acum putem parcurge secțiunile cu demo-uri de mai jos.

Investigare întreruperi

Dorim să investigăm livrarea întreruperilor hardware către sistemul local. Vom urmări întreruperea de ceas (timer) și întreruperea de tastatură.

Î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ă:

$ 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
[...]

Prima coloană este linia de întrerupere (IRQ line) pe care se livrează semnalul de întrerupere către procesor. Este 0 în cazul timer-ului programabil (8253) și 1 în cazul controller-ului de tastatură (8042).

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

cat /proc interrupts

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).

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.1)

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:

$ 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

Șirul de comenzi de mai sus trebuie tastat pentru a genera întreruperi. Nu dăm copy-paste la șir.

Î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.

Utilitate disk cache

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.

Creăm un fișier in.dat de 100MB de date aleatoare folosind comanda dd:

$ 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

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:

razvan@einherjar:~$ /usr/bin/time -v cp in.dat out.dat
	Command being timed: "cp in.dat out.dat"
	User time (seconds): 0.00
	System time (seconds): 0.09
	Percent of CPU this job got: 100%
	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
        [...]

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.

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:

$ 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

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.2)

Copiem din nou fișierul in.dat într-un nou fișier out2.dat și folosim comanda time pentru a măsura durata:

$ /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
	[...]

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.

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ă.

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:

$ dd if=/dev/cdrom of=file.iso bs=1M

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.

Pentru a urmări evoluția comenzii, dintr-un alt terminal putem rula comanda:

$ kill -USR1 $(pidof dd)

Livrarea semnalului USR1 procesului dd înseamnă afișarea unui raport de stare din partea comenzii dd (adică în primul terminal).

Folosire operație de control (ioctl)

Dorim să urmărim câteva cazuri de folosire a funcției 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 read sau 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 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:

$ ./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

Î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 sursele nucleului Linux): CDROMEJECT și CDROM_LOCKDOOR.

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:

$ ./hwaddr-ioctl 
Hardware address for interface eth0 is 00:21:cc:68:d0:53

Adresa hardware a fost obținută într-o structură struct ifreq cu ajutorul unui apel ioctl aplicat pe un descriptor de socket. Parametrul ioctl a fost SIOCGIFHWADDR.

so/cursuri/curs-10.txt · Last modified: 2019/04/20 20:24 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