Documente online.
Zona de administrare documente. Fisierele tale
Am uitat parola x Creaza cont nou
 HomeExploreaza
upload
Upload




APELURI SISTEM PENTRU FISIERE

Informatica


APELURI SISTEM PENTRU FIsIERE



În capitolul precedent au fost descrise structurile interne de date pentru sistemul de fisiere si algoritmii cu care acestea pot fi manipulate. Acest capitol trateaza apelurile sistem utilizate de sistemul de fisiere folosindu-se algoritmii studiati în capitolul anterior. Sunt prezentate apeluri pentru:

accesarea fisierelor existente: open, read, write, lseek, close;

crearea de noi fisiere: creat si mknod;

manipularea inodurilor sau a sistemului de fisiere: chdir, chroot, chown, chmod, stat, fstat;

implementarea pipe-urilor: pipe si dup;

extinderea sistemului de fisiere vazut de utilizatori: mount si umount;

modificarea ierarhiei sistemului de fisiere: link si unlink.

Capitolul introduce trei noi structuri de date utilizate de nucleu:

tabela de fisiere (File Table sau Global File Table-GFT) se afla în nucleu si are câte o intrare pentru fiecare fisier deschis în sistem;

tabela descriptorilor de fisier utilizator (User File Descriptor Table-UFDT) se afla în zona alocata procesului si are câte o intrare pentru fiecare descriptor de fisier utilizat de un proces;

tabela de montare (Mount Table-MT) se afla în nucleu si contine informatii referitoare la sistemele de fisiere active.

Apelurile sistem sunt împartite în mai multe clase cum ar fi:

apeluri sistem care returneaza descriptori de fisiere care sunt utilizati de alte apeluri sistem;

apeluri sistem care folosesc algoritmul namei pentru a analiza un nume de cale;

apeluri sistem care asigneaza si elibereaza inoduri, folosind algoritmii ialloc si ifree;

apeluri sistem care stabilesc sau modifica atributele unui fisier;

apeluri sistem care executa operatiuni de I/O pentru un proces, folosind algoritmii alloc, free si algoritmii de alocare ai buffer-ului cache;

apeluri sistem care modifica structura sistemului de fisiere;

apeluri sistem care permit unui proces sa-si modifice modul în care percepe sistemul de fisiere.

Dupa cum se poate observa, unele apeluri sistem apar în mai multe clase.

În figura 5.1 sunt prezentate relatiile dintre apelurile sistem si algoritmii prezentati în capitolul anterior.

Figura 5.1. Apelurile sistem si relatiile cu ceilalti algoritmi

5.1 Apelul sistem open

Apelul sistem open este primul pas ce trebuie efectuat de catre un proces pentru a putea accesa datele unui fisier.

algoritm open

intrari: numele fisierului

tipul de deschidere

permisiuni de acces /* precizate doar la creare */

iesire: descriptorul de fisier

Figura 5.2. Algoritmul pentru deschiderea unui fisier

Sintaxa apelului sistem open este:

fd = open (numeÎcale, bitul de semnalizare, moduri);

unde numeÎcale este un nume de fisier, bitul de semnalizare indica tipul de deschidere (pentru scriere sau citire), iar moduri specifica permisiunile de acces daca fisierul este în curs de creare. Apelul sistem returneaza un întreg numit descriptor de fisier (fd).

Toate celelalte operatii cu fisierul, cum ar fi citirea, scrierea, pozitionarea indicatorului de citire/scriere, duplicarea descriptorului de fisier, modificarea parametrilor de I/O ai fisierului, determinarea starii fisierului si închiderea fisierului, folosesc descriptorul de fisier pe care îl returneaza apelul sistem open. Nucleul cauta fisierul în cadrul sistemului de fisiere folosind algoritmul namei (vezi figura 5.2). Dupa ce gaseste inodul din memoria interna, nucleul verifica permisiunile de acces la fisier, iar daca acestea sunt corespunzatoare, aloca o intrare în tabela de fisiere pentru fisierul care va fi deschis. O intrare în tabela de fisiere contine un pointer catre inodul fisierului deschis si deplasamentul de la care nucleul urmeaza sa execute urmatoarea citire sau scriere. Nucleul initializeaza aceasta valoare cu 0 pe durata aplelului open, ceea ce înseamna ca prima citire sau scriere se va face, implicit, de la începutul fisierului. De asemenea, un proces poate deschide un fisier pentru adaugare, caz în care nucleul va initializa deplasamentul cu valoarea dimensiunii fisierului.

Nucleul aloca o intrare în tabela descriptorilor de fisiere utilizator, iar valoarea indexului din tabela corespunzator acestei intrari este descriptorul de fisier returnat utilizatorului. Intrarea din tabela descriptorilor de fisiere utilizator contine un pointer catre intrarea corespunzatoare din tabela de fisiere.

Figura 5.3. Structurile de date dupa executia apelurilor open

Sa presupunem ca un proces executa urmatorul cod: deschide fisierul "/etc/passwd" de doua ori, odata doar pentru citire, iar a doua oara pentru scriere, si fisierul "local" odata, pentru citire/scriere.

fd1 = open("/etc/passwd", OÎRDONLY);

fd2 = open("local", OÎWRONLY);

fd3 = open("/etc/passwd", OÎRDWR);

În figura 5.3. se prezinta relatia dintre tabela de inoduri, tabela de fisiere si tabela descriptorilor de fisiere utilizator. Fiecare apel open returneaza procesului un descriptor de fisier, iar intrarea corespunzatoare din tabela descriptorilor de fisiere utilizator pointeaza catre o intrare unica din tabela de fisiere din nucleu, chiar daca un fisier ("/etc/passwd") este deschis de doua ori. Intrarile din tabela de fisiere corespunzatoare aceluiasi fisier pointeaza catre o singura intrare din tabela de inoduri. Sa presupunem ca un al doilea proces executa urmatorul cod:

fd1=open("/etc/passwd",OÎRDONLY);

fd2=open("private",OÎRDONLY);

În figura 5.4 sunt prezentate relatiile dintre structurile de date, în situatia în care ambele procese (nu si altele) au deschis fisierele.

Figura 5.4. Structurile de date dupa deschiderea fisierelor de catre cele doua procese

S-ar putea concepe sistemul astfel ca intrarea din tabela descriptorilor de fisiere utilizator sa contina deplasamentul din fisier ce da pozitia urmatoarei operatii de I/O si pointerul direct catre intrarea corespunzatoare din tabela de inoduri, eliminându-se folosirea tabelei de fisiere. Implementarea tabelei de fisiere ca structura separata s-a facut pentru a se putea permite folosirea în comun a deplasamentului în fisier de catre mai multi descriptori de fisier. Astfel, mai multe procese si mai multi utilizatori pot efectua operatii pe un acelasi fisier utilizând un deplasament comun. Primii trei descriptori de fisier din tabela descriptorilor de fisiere utilizator sunt :

descriptorul fisierului standard de intrare (standard input) ;

descriptorul fisierului standard de iesire (standard output);

descriptorul fisierului standard de eroare (standard error).

Procesele din UNIX folosesc în mod conventional descriptorul standard de intrare pentru a citi datele de intrare, descriptorul standard de iesire pentru a transmite la iesire rezultatele, si descriptorul standard de eroare pentru a transmite mesajele de eroare. Utilizarea acestei conventii nu este obligatorie, însa adoptarea ei de catre toti utilizatorii face mai simpla comunicarea prin pipe-uri.

5.2 Apelul sistem read

Sintaxa apelului sistem read este:

numar = read(fd, buffer, contor);

unde fd este descriptorul de fisier returnat de catre open, buffer este adresa de memorie din spatiul procesului utilizator care va contine datele în caz de reusita a apelului, contor este numarul de octeti pe care utilizatorul doreste sa-i citeasca, iar numar este numarul de octeti cititi în urma apelului.

mode  indica tipul operatiei (citire sau scriere)

count  numarul de octeti ce urmeaza a fi cititi sau scrisi

offset  deplasamentul din fisier

address adresa din spatiul nucleu sau utilizator în care se vor

copia datele

flag  indica tipul adresei (utilizator sau nucleu)

Figura 5.5. Parametrii de I/O salvati în u area

În figura 5.6 este prezentat algoritmul read pentru citirea unui fisier obisnuit. Nucleul determina intrarea în tabela de fisiere corespunzatoare descriptorului de fisier prin intermediul pointerului pastrat în intrarea din tabela descriptorilor de fisiere utilizator (vezi figura 5.3). O parte din parametrii de I/O sunt scrisi în u area pentru a se elimina necesitatea transmiterii lor ca parametrii ai functiei (fig.5.5.).

Algoritm read

intrari: descriptorul de fisier

adresa buffer-ului din spatiul de memorie al procesului utilizator

numarul de octeti ce se doreste a fi citit

iesire: numarul de octeti copiati în spatiul utilizator

deblocheaza inodul;

actualizeaza deplasamentul din tabela de fisiere pentru urmatoarea

operatie de citire;

return (numarul total de octeti cititi);

}

Figura 5.6. Algoritmul pentru citirea unui fisier

Astfel, este setat modul de I/O pentru a indica ca se executa o citire, un bit de semnalizare care indica faptul ca datele vor fi transferate în spatiul de adrese utilizator, un contor care indica numarul de octeti ce vor fi cititi, adresa buffer-ului din spatiul utilizator, si un câmp de deplasament (copiat din tabela de fisiere) care precizeaza de unde va începe citirea datelor.

Dupa stabilirea parametrilor de I/O în u area, folosind pointerul din tabela de fisiere catre intrarea din tabela de inoduri, nucleul blocheaza inodul înainte de a începe citirea fisierului. Algoritmul intra apoi într-un ciclu pâna la citirea numarului dorit de octeti. Nucleul determina pe baza deplasamentului numarul blocului care va fi citit si îl memoreaza. Dupa citirea unui bloc în memorie, folosind algoritmul bread sau breada, nucleul copiaza datele din bloc în spatiul de adrese utilizator al procesului .

Sunt modificati parametrii din u area corespunzator numarului de octeti cititi incrementând deplasamentul si adresa din spatiul utilizator la care va avea loc urmatorul transfer, si decrementând contorul cu numarul de octeti care mai trebuie cititi. Daca cererea utilizatorului nu a fost satisfacuta, nucleul reia executia operatiilor din ciclu. Acesta se va încheia când nucleul satisface integral cererea utilizatorului, când se ajunge la sfârsitul fisierului, sau când se semnaleaza o eroare în desfasurarea unei operatii de citire de pe disc sau de scriere în spatiul utilizator.

#include <fcntl.h>

main()

Figura 5.7. Un exemplu de program de citire dintr-un fisier

În exemplul din figura 5.7, apelul open returneaza un descriptor de fisier care este atribuit variabilei fd si care va fi utilizata în secventa de apeluri de citire. La excutia apelului sistem read nucleul verifica daca descriptorul de fisier este legal si daca procesul a deschis anterior fisierul pentru citire. În u area sunt memorate valorile lilbuf, 20 si 0 în câmpurile adress, count, respectiv offset. Nucleul determina ca octetul cu deplasamentul 0 se afla în blocul cu numarul 0 din fisier si gaseste intrarea din inod corespunzatoare blocului. Presupunând ca un astfel de bloc exista, nucleul citeste întregul bloc (1ko) într-un buffer, dar copiaza doar 20 de octeti la adresa lilbuf din spatiu utilizator. Apoi, incrementeaza deplasamentul cu 20 si decrementeaza contorul cu numarul de octeti ramasi de citit la 0. Deoarece operatia de citire s-a încheiat cu succes, nucleul reseteaza valoarea deplasamentulului din tabela de fisiere la 20, urmatoarea operatie de citire începând de la octetul 20 al fisierului, iar apelul sistem returneaza numarul de octeti cititi, 20.

Dupa executarea tuturor verificarilor si initializarilor (efectuate si în cazul primei citiri), al doilea apel read va începe citirea de la octetul 20. Pe durata executiei algoritmului de citire a blocului disc, exista posibilitatea ca acesta sa fie regasit în buffer-ul cache fara a mai fi nevoie de o operatie cu discul. Se copiaza cei 1004 ultimi octeti la adresa bigbuf. Apoi printr-o noua operatie cu discul, se citeste urmatorul bloc din care se copiaza începând cu pozitia 1004 din bigbuf înca 20 de octeti din noul bloc. În mod asemanator se procedeaza si la cel de-al treilea apel read.

Exemplul arata ca cererile pentru operatii de I/O sunt avantajoase atunci când deplasamentul de start corespunde unui început de bloc disc, iar lungimea transferului este multiplu de dimensiunea unui bloc. În acest fel se utilizeaza integral datele din blocurile disc citite, eliminându-se o iteratie suplimentara a buclei din algoritmul read, si deci implicatiile ce decurg din acest lucru: accesul la inod pentru mai mult timp si concurenta alaturi de alte procese la buffer-ul cache.

Daca un proces citeste doua blocuri secvential, nucleul presupune ca toate citirile ulterioare vor fi secventiale pâna când acest lucru va fi infirmat. Pe durata fiecarei parcurgeri a buclei din apelul read, nucleul salveaza în inodul din memoria interna numarul urmatorului bloc logic, iar în cadrul urmatoarei parcurgeri compara numarul blocului logic curent cu valoarea anterior salvata. Daca sunt egale, nucleul calculeaza numarul blocului fizic pentru citirea în avans si salveaza valoarea sa în u area pentru a fi utilizata de algoritmul breada. Daca procesul nu va citi sfârsitul unui bloc, nucleul nu va apela citirea în avans pentru urmatorul bloc.

Daca se încearca citirea unor blocuri pentru care numerele de bloc din inod sau din blocurile de indirectare au valoarea 0, nucleul satisface cererea alocând un buffer arbitrar cu continut 0, pe care îl copiaza în spatiul utilizator.

Când un proces invoca apelul sistem read, nucleul blocheaza inodul corespunzator pe durata apelului, evitând astfel posibilitatea returnarii unor date inconsistente. Spre exemplu, daca procesul A trebuie sa citeasca mai multe blocuri, iar dupa citirea unui parti se pune în asteptare, iar în timp ce asteapta, un alt proces B acceseaza inodul si modifica portiunea din fisier pe care urmeaza sa o citeasca procesul A, atunci ceea ce va citi A va fi o mixtura de date vechi si noi. Blocarea inodului la intrarea în apelul sistem si deblocarea sa la sfârsitul acestuia rezolva problemele de acest fel.

Consideram în continuare cazul unui proces care citeste un anumit fisier, iar citirea se face prin doua apeluri distincte. Daca în intervalul dintre cele doua citiri un alt proces va executa o scriere în fisierul considerat, atunci datele întoarse la cea de-a doua citire a primului proces vor fi o combinatie între prima si cea de-a doua versiune a fisierului. Pentru a ilustra aceasta situatie se da exemplul din figura 5.8. Ordinea executiei apelurilor poate fi: read1, read2, write1, write2 sau read1, write1, read2, write2, etc. Pentru a evita aceasta situatie se foloseste facilitatea de blocare pe fisier si înregistrare (vezi paragraful 5.4) ceea ce permite unui proces sa garanteze consistenta fisierului cât timp acesta este deschis.

#include <fcntl.h>

/* process A */

main()

/* process B */

main();

;

fd = open("/etc/passwd", O_WRONLY);

write (fd, buf, sizeof(buf)); /* write 1 */

write (fd, buf, sizeof(buf)); /* write 2 */

}

Figura 5.8. Un proces care citeste si unul care scrie din/într-un fisier

Exista posibilitatea ca un proces sa poata deschide un fisier de mai multe ori si sa-l citeasca folosind descriptori de fisier diferiti.

În figura 5.9. este prezentat cazul unui proces care deschide un fisier de doua ori. Citirea se face prin manipularea în mod independent a deplasamentului din tabela de fisiere asociata fiecarui descriptor, astfel ca la încheierea procesului, sirurile buf1 si buf2 ar trebui sa fie identice, presupunând ca nici un alt proces nu scrie în acest timp în fisierul "/etc/passwd".

#include <fcntl.h>

main()

Figura 5.9. Citirea dintr-un fisier folosind doi descriptori

5.3 Apelul sistem write

Sintaxa apelului sistem write este:

numar= write(fd, buffer, contor);

unde semnificatia variabilelor numar, fd, buffer, contor este cea prezentata în descrierea apelului read.

Algoritmul pentru scrierea unui fisier obisnuit este similar celui de citire. Daca fisierul nu contine un bloc corespunzator deplasamentului la care trebuie efectuata scrierea, atunci nucleul aloca un nou bloc folosind algoritmul alloc si asigneaza numarul blocului în pozitia corespunzatoare a tabelei de cuprins din inod. Daca deplasamentul corespunde unui bloc de indirectare, nucleul va trebui sa aloce un numar corespunzator de blocuri ce vor fi utilizate ca blocuri de indirectare si blocuri de date. Pe durata operatiei de scriere inodul este blocat, deoarece nucleul poate schimba inodul când aloca blocuri noi.

Permitându-se altor procese accesul la fisier s-ar putea altera informatiile din inod daca mai multe procese ar aloca simultan blocuri pentru acelasi deplasament. Daca dimensiunea fisierului a fost modificata, la încheierea operatiei de scriere nucleul actualizeaza câmpul dimensiune fisier din inod.

Sa presupunem ca un proces scrie într-un fisier octetul cu numarul 10240, care este totodata octetul cu cel mai mare numar de ordine. Când acceseaza octetul (folosind algoritmul bmap), nucleul determina ca lipseste blocul de date si cel de indirectare corespunzatoare acelui octet. Astfel, el aloca un bloc disc ce va fi utilizat ca bloc de indirectare si scrie numarul blocului în inodul din memorie. Apoi, aloca un bloc disc ce va fi utilizat ca bloc de date si scrie numarul blocului în prima pozitie a blocului de indirectare anterior alocat.

Scrierea se face întru-un ciclu intern, asemanator celui din algoritmul pentru citire. Pe durata unei iteratii nucleul va scrie un singur bloc disc. În acest sens, va determina daca va scrie întregul blocul sau numai o parte din el. Daca scrie doar o parte, va trebui mai întâi sa citeasca blocul de pe disc, pentru a conserva datele ce nu se vor modifica. Citirea nu mai este necesara în cazul scrierii întregului bloc, deoarece se va modifica tot continutul sau. Nucleul utilizeaza scrierea întârziata a blocurilor pe disc (delayed write, vezi 3.4), pastrându-le în cache cât mai mult timp posibil, pentru a oferi si altor procese posibilitatea de a le scrie sau citi fara a mai executa operatii de I/O cu discul.

Folosirea scrierii întârziate este utila în cazul pipe-urilor deoarece un alt proces va citi datele si le va sterge (vezi 5.12), si chiar în cazul fisierelor obisnuite ce sunt create temporar. De exemplu, multe programe cum ar fi cele de posta electronica, editoarele de texte, creeaza fisiere temporare în directorul "/temp", pe care le sterg dupa putin timp. Utilizarea scrierii întârziate, poate reduce numarul de scrieri pe disc pentru fisierele temporare.

5.4 Blocarea fisierelor si înregistrarilor

Sistemul UNIX initial, dezvoltat de Thompson si Ritchie, nu dispunea de un mecanism intern care sa asigure unui proces accesul exclusiv la un fisier. Totusi, pentru a face UNIX-ul un sistem mult mai atractiv pentru cei ce realizeaza aplicatii cu baze de date, System V contine mecanisme ce permit blocarea la nivel fisier si înregistrare. Blocarea la nivel fisier permite unui proces sa interzica altor procese sa citeasca sau sa scrie un fisier pâna la deblocarea acestuia. Mecanismul de blocare la nivel înregistrare asigura unui proces accesul exclusiv la o anumita înregistrare (portiune din fisier între anumite adrese).

5.5 Accesul direct în fisiere

Apelurile read si write descrise anterior asigura un accesul secvential la un fisier. Prin utilizarea apelului sistem lseek procesele pot modifica valoarea deplasamentului din tabela de fisiere, asigurându-se astfel un acces aleator la fisier, utilizatorul având posibilitatea sa scrie sau sa citeasca de la orice adresa.

Sintaxa apelului sistem este:

pozitie= lseek(fd, offset, referinta);

unde fd este descriptorul de fisier (returnat de open), referinta indica originea fata de care se stabileste deplasamentul, offset este un numar care indica pozitia finala a deplasamentului fata de referinta, iar pozitie reprezinta deplasamentul de la care va începe urmatoarea operatie de citire/scriere. Referinta poate fi începutul fisierului (referinta=0), pozitia curenta (referinta=1) sau sfârsitul fisierului (referinta=2).

#include <fcntl.h>

main(argc,argv)

int argc;

char *argv[ ];

}

Figura 5.10. Exemplu de program care utilizeaza apelul sistem lseek

Spre exemplu, în programul din figura 5.10 un proces deschide un fisier, dupa care ciclic citeste un octet si apeleaza lseek pentru a incrementa valoarea deplasamentului din tabela de fisiere cu 1023 (cu referinta=1). Astfel, programul citeste din fisier octetii cu deplasamentul multiplu de1024. Apelul lseek nu are nimic de a face cu operatia seek care efectueaza pozitionarea bratului discului pe un anumit sector. Pentru a implementa apelul lseek nucleul modifica practic valoarea deplasamentului din tabela de fisiere.

5.6 Apelul sistem close

Un proces închide un fisier atunci când nu mai doreste sa îl acceseze. Sintaxa aplelui sistem close este:

close(fd);

unde fd este descriptorul fisierului returnat de open. Apelul modifica intrarile din tabela de fisiere si tabela de inoduri corespunzatoare descriptorului fd. Aceste intrari nu sunt sterse, ci doar eliberate (când contoarele ajung la 0 se invalideaza legaturile între tabele). Ele pot fi utilizate de acelasi fisier (daca se redeschide imediat) sau realocate altui fisier ce va fi deschis. Daca contorul de referinta din tabela de fisiere este mai mare ca 1 (datorita apelurilor sistem dup sau fork), înseamna ca si alti descriptori de fisier refera intrarea din tabela de fisiere. În acest caz nucleul va decrementa contorul din tabela de fisiere si cu aceasta operatia de închidere ia sfârsit. Daca contorul din tabela de fisiere este egal cu 1, nucleul elibereaza intrarea si decrementeaza contorul de referinta corespunzator intrarii din tabela de inoduri. Daca contorul este egal cu 0 (nici un proces nu mai refera inodul), nucleul elibereaza inodul din memoria interna (algoritmul iput) pentru a putea fi realocat. Înaintea încheierii apelului close, nucleul elibereaza intrarea corespunzatoare descriptorului din tabela descriptorilor de fisiere utilizator. Daca procesul va încerca sa acceseze descriptorul înainte ca acesta sa fie reasignat unui fisier, nucleul va returna o eroare. Când un proces se încheie (exit) nucleul închide fisierele corespunzatoare descriptorilor activi din tabela descriptorilor de fisiere utilizator.

În figura 5.11 se prezinta intrarile de interes din tabelele figurii 5.4 dupa încheierea procesului B. Astfel, sunt eliberate intrarile pentru descriptorii de fisier 3 si 4 din tabela descriptorilor de fisiere utilizator, si intrarile corespunzatoare din tabela de fisiere (contoarele fiind 0). Sunt decrementate contoarele de referinta din tabela inodurilor corespunzatoare fisierelor "/etc/passwd" si "private". Cum contorul pentru "private" este 0, inodul sau este pus în lista inodurilor libere, însa intrarea sa din tabela inodurilor nu este stearsa. Daca un alt proces acceseaza fisierul "private" cât timp inodul se mai afla în lista libera, el va fi utilizat de catre nucleu (vezi 4.1.2).

 

Figura 5.11 Structura tabelelor dupa închiderea unor fisiere

5.7 Crearea fisierelor

Daca apelul sistem open asigura accesul la un fisier existent, apelul sistem creat permite crearea unui nou fisier în sistem. Sintaxa apelului sistem creat este:

fd = creat (nume­_cale, moduri);

unde variabilele numeέcale , moduri si fd au semnificatia precizata la descrierea apelului open.

Daca nu exista nici un fisier cu numele specificat, atunci nucleul creaza un nou fisier cu numele si permisiunile de acces (modurile) precizate. Daca fisierul exista deja, si permisiunile de acces sunt corespunzatoare, nucleul trunchiaza fisierul (elibereaza toate blocurile sale si seteaza dimensiunea fisierului la 0). În acest caz nucleul nu modifica atributele de proprietate, iar permisiunile de acces furnizate ca parametru sunt ignorate, pastrându-se cele ale vechiului fisier.

În figura 5.12 este prezentat algoritmul pentru crearea unui fisier. Folosind algoritmul namei, nucleul analizeaza numele de cale si salveaza adresa primului slot liber din director în u area. Daca nucleul nu gaseste componenta corespunzatoare din cale în director, o va scrie în slotul determinat anterior. De asemenea, memoreaza în u area si tine blocat inodul directorului în care se cauta. Pentru continuarea executiei algoritmului, se verifica daca procesul apelant are dreptul de a scrie în director (acela unde va fi creat noul fisier).

În caz ca nu exista anterior un fisier cu acelasi nume, nucleul asigneaza un inod pentru noul fisier (folosind algoritmul ialloc). Apoi scrie numele fisierului si numarul inodului alocat în directorul parinte la adresa salvata în u area. Dupa aceea elibereaza inodul directorului parinte. Nucleul scrie noul inod alocat pe disc (algoritm bwrite), iar apoi directorul în noua configuratie. Scrierea pe disc are loc în aceasta ordine deoarece în cazul unei caderi a sistemului între cele doua scrieri vom avea un inod care nu este referit de nici un nume de fisier, dar sistemul va functiona normal.

algoritm creat

intrari: numele fisierului

permisiunile de acces

iesire: descriptorul de fisier

}

else /* fisierul nu exista înca */

aloca o intrare în tabela de fisiere pentru inod, initializeaza contorul;

if (fisierul exista în momentul crearii)

elibereaza toate blocurile fisierului (algoritmul free);

deblocheaza inodul;

return (descriptorul de fisier);

}

Figura 5.12. Algoritmul pentru crearea unui fisier

Daca ordinea de scriere este inversata si sistemul se cade între cele doua scrieri, sistemul de fisiere va contine o cale care refera un inod incorect. Daca fisierul exista deja înaintea apelului creat, nucleul gaseste inodul sau în timp ce cauta fisierul. Vechiul fisier trebuie sa ofere permisiunea de scriere procesului ce doreste sa creeze un "nou" fisier cu acelasi nume, deoarece continutul fisierului va fi modificat (se elibereaza toate blocurile fisierului, acesta aratând ca un fisier nou creat). Întrucât continutul directorului parinte nu va fi modificat, nucleul nu va mai verifica daca este permisa scrierea directorului.

5.8 Crearea fisierelor speciale

Apelul sistem mknod creeaza fisiere speciale în sistem cum ar fi directoarele, fisierele dispozitiv si pipe-urile numite. El este similar apelului creat din punctul de vedere al alocarii de catre nucleu a unui inod pentru un fisier.

algoritm make new node

intrari: numele fisierului

tipul fisierului

permisiunile de acces

numar major si minor al dispozitivului (pentru fisierele speciale bloc si

caracter)

iesire: niciuna

asigneaza un inod liber din sistemul de fisiere pentru noul nod (algoritmul

ialloc);

creeaza o noua intrare în directorul parinte si o completeaza cu numele

nodului si numarul de inod anterior asignat;

elibereaza inodul directorului parinte (algoritmul iput);

if (noul nod este fisier special bloc sau caracter)

scrie numarul major si minor al dispozitivului în structura inodului;

elibereaza inodul noului nod (algoritmul iput);

}

Figura 5.13 Algoritmul pentru crearea unui nou nod

Sintaxa apelului sistem mknod este:

mknod (nume_cale, tip si permisiuni, dev)

unde nume­Îcale este numele nodului care va fi creat, tip si permisiuni dau tipul nodului (de exemplu director) si permisiunile de acces pentru noul fisier ce va fi creat, iar dev specifica numerele major si minor ale dispozitivului pentru fisierele speciale bloc si caracter (vezi capitolul 6).Algoritmul mknod este prezentat în figura 5.13. Nucleul cauta în sistemul de fisiere fisierul cu numele ce urmeaza sa fie creat. Daca fisierul nu exista înca, nucleul asigneaza un nou inod pe disc si scrie numele noului fisier si numarul inodului alocat în directorul parinte. Initializeaza câmpul din inod care specifica tipul fisierului (pipe, director sau fisier special). Daca fisierul este de tip dispozitiv special caracter sau bloc, atunci se scriu în inod numerele major si minor de dispozitiv. Algoritmul prezentat mai trebuie completat în cazul crearii directoarelor, deoarece nu sunt create si initializate intrarile director pentru "." si "..".

5.9. Schimbarea directorului sau a radacinii

La încarcarea sistemului, procesul 0 stabileste ca radacina a sistemului de fisiere directorul sau curent din perioada initializarii. El executa algoritmul iget pentru a obtine inodul radacina, îl salveaza în u area ca director curent, si elibereaza. Atunci când se creaza un proces prin apelul sistem fork, acest proces mosteneste în zona sa u area directorul curent al procesului tata, iar nucleul incrementeaza valoarea contorului de referinta din inod.

algoritm change directory

intrare: numele noului director

iesire: niciuna

deblocheaza inodul;

elibereaza inodul corespunzator directorului curent anterior

(algoritm iput);

plaseaza informatiile corespunzatoare noului inod în slotul

corespunzator directorului curent din u area;

}

Figura 5.14. Algoritm pentru schimbarea directorului curent

Algoritmul chdir (figura 5.14) modifica directorul curent al unui proces.

Sintaxa apelului sistem chdir este:

chdir (nume­Îcale);

unde numeÎcale este numele noului director curent al procesului.

Nucleul analizeaza numele noului director folosind algoritmul namei si verifica daca acesta este într-adevar director, precum si permisiunile de acces. Noul inod este eliberat, dar ramâne alocat si cu valoarea contorului de referinta incrementata. Se elibereaza inodul vechiului director curent memorat în u area (algoritm iput), iar noul inod este salvat în u area. Dupa schimbarea directorului curent algoritmul namei va utiliza inodul din u area ca director de început pentru cautarea cailor ce nu încep din radacina. Dupa executia apelului chdir, contorul inodului noului director este cel putin 1, pe când cel al directorului anterior poate fi 0. Inodul alocat în timpul unui apel chdir este eliberat numai când procesul executa un alt apel chdir sau se termina.

Pentru fiecare utilizator se pastreaza în nucleu o variabila globala care pointeaza spre inodul radacinii sistemului. O copie a acestei variabile este în u area. Procesele pot schimba radacina curenta (cea din u area) prin intermediul apelului sistem chroot. Sintaxa este:

chroot (numeÎcale);

unde numeÎcale este numele noii radacini. Algoritmul pentru chroot este similar cu chdir.

5.10 Schimbarea proprietarului si a permisiunilor

Apelurile sistem corespunzatoare sunt:

chown (numeÎcale, proprietar, grup);

chmod (numeÎcale, mod);

Ele pot fi apelate doar de proprietar sau superutilizator. Apelul chown stabileste noul proprietar si grupul acestuia si pune pe zero bitii set uid si set gid,si elibereaza inodul (algoritmul iput). Dupa ce se efectueaza modificarile, vechiul proprietar pierde dreptul de proprietate asupra fisierului. În mod asemanator functioneaza si functia chmod, ea fiind folosita pentru a modifica permisiunile de acces la fisier.

5.11 Apelurile sistem stat si fstat

Apelurile stat si fstat permit proceselor sa obtina unele informatii de stare referitoare la un anumit fisier cum ar fi: tipul fisierului, permisiunile de acces, dimensiunea fisierului, numarul de legaturi, numarul inodului, timpii de acces. Pentru apelul stat se utilizeaza informatiile din inodul de pe disc, iar pentru apelul fstat cele din inodul din memoria interna.

Sintaxa apelurilor:

stat (numeÎcale, bufferÎstare);

fstat (fd, bufferÎstare);

unde numeÎcale este numele fisierului, fd este descriptorul de fisier întors de un apel open anterior, iar bufferÎstare este adresa unei structuri de date din procesul utilizatorului care va contine informatiile dupa terminarea apelului. Programul din figura 5.33 exemplifica utilizarea celor doua apeluri.

Figura 5.15 Arborele de procese si partajarea pipe-urilor

5.12 Fisiere pipe

Sunt fisiere de tip FIFO. Ele permit transferul de date între procese, cât si sincronizarea executiei proceselor. Modul de implementare permite proceselor sa comunice fara a cunoaste care sunt procesele de la capatul celalalt al pipe-ului. Exista doua tipuri de pipe-uri:

pipe-uri numite;

pipe-uri nenumite (anonime).

Cele numite ocupa un loc în ierarhia sistemului de fisiere, se deschid ca orice fisier (folosind aplelul sistem open) si pot fi folosite în comun de procese în functie de permisiunile lor de acces la fisier. Cele nenumite sunt create cu apelul sistem pipe si nu pot fi folosite în comun decât de procesele descendente din procesul ce a creat pipe-ul. În figura 5.15, de exemplu, procesul B creeaza un pipe obisnuit si procesele D si E. Aceste procese vor putea accesa pipe-ul, dar A si C nu. Dupa ce a fost creat, un pipe se acceseaza folosind apelurile read, write si close.În continuare se vor prezenta pipe-urile nenumite.

Apelul sistem pipe

Sintaxa pentru crearea unui pipe este:

pipe (fdptr) ;

unde fdptr este un pointer catre un sir de doi întregi ce va contine descriptorii de fisier pentru citirea si scrierea pipe-ului. Deoarece nucleul implementeaza pipe-urile în sistemul de fisiere, acestea neexistând înaintea utilizarii lor, nucleul va asigna un inod si va aloca doi descriptori în tabela descriptorilor de fisiere utilizator (un descriptor pentru citirea din pipe si altul pentru scrierea în pipe) si intrarile corespunzatoare în tabela de fisiere. Utilizarea tabelei de fisiere face ca interfata apelurilor sistem pentru scriere si citire a pipe-urilor sa fie aceeasi cu cea pentru fisierele obisnuite. Astfel, procesele nu trebuie sa stie daca citesc/scriu un pipe sau un fisier obisnuit.

algoritm pipe

intrare: nici una

iesiri: descriptorul pentru citire

descriptorul pentru scriere

Figura 5.16. Algoritmul de creare a unui pipe nenumit

Algoritmul pentru crearea pipe-urilor nenumite este prezentat în figura 5.16. Folosind algoritmul ialloc, nucleul asigneaza un inod pentru un pipe dintr-un sistem de fisiere folosit ca dispozitiv pipe (pipe device). Un dispozitiv pipe este un sistem de fisiere din care nucleul aloca inoduri si blocuri de date pentru pipe-uri. Specificarea dispozitivului pipe se face de catre administratori la configurarea sistemului, si poate fi identic cu un alt sistem de fisiere. Atunci când un pipe este activ, nucleul nu poate reasigna inodul si blocurile sale de date unui alt fisier.

Nucleul aloca apoi doua intrari în tabela de fisiere si actualizeaza informatiile din inodul aflat în memoria interna. Fiecare intrare în tabela de fisiere contine numarul de instante ale pipe-ului deschise pentru citire sau scriere (initial 1 pentru fiecare intrare în tabela de fisiere), iar contorul de referinta al inodului din memoria interna indica de câte ori pipe-ul a fost deschis (initial are valoarea 2). Valorile deplasamentului pentru citire si scriere sunt pastrate în inod, fapt ce permite accesul în mod FIFO la pipe. În cazul fisierelor regulate offsetul se pastreaza în tabela de fisiere. Utilizarea apelului lseek nu este permisa, ceea ce face imposibil accesul aleator la un pipe.

Deschiderea unui pipe numit

Un pipe numit este un fisier a carui semantica este asemanatoare cu cea a unui pipe nenumit. Diferenta între ele este ca cel numit are o intrare în director si este accesat prin nume. Deschiderea unui pipe numit este la fel cu cea a fisierelor obisnuite, fiind astfel permisa comunicarea între procese ce nu sunt înrudite. Pipe-urile numite exista în sistemul de fisiere si dupa încheierea utilizarii lor, pe când cele nenumite sunt tranzitorii (când nu mai sunt procese care utilizeaza pipe-ul, nucleul va elibera inodul alocat acestuia).

Algoritmul pentru deschiderea unui pipe numit este identic cu cel pentru deschiderea unui fisier obisnuit. Totusi, înaintea încheierii apelului sistem, nucleul incrementeaza contorul de citire sau cel de scriere din inod, indicând numarul proceselor ce au deschis pipe-ul numit pentru citire sau scriere. Un proces care deschide un pipe numit pentru citire va astepta pâna când un alt proces va deschide acest pipe pentru scriere, si invers. La deschiderea unui pipe numit (pentru citire sau scriere), nucleul trezeste toate procesele care asteptau acest lucru.

Daca un proces deschide un pipe numit pentru citire si exista un proces care sa scrie în acesta, atunci apelul open se încheie. Daca un proces deschide un pipe numit cu optiunea ano delaya(fara întarziere), apelul se va încheia fara ca procesul sa se puna în asteptare chiar daca nu exista un proces care sa scrie în acest pipe. În caz contrar procesul este blocat pâna când apare si procesul care sa scrie în pipe. Regulile sunt similare pentru un proces care deschide un pipe pentru scriere.

Citirea si scrierea pipe-urilor

Scrierea într-un pipe se face la unul din capete, iar citirea de la celalalt capat. Dupa cum s-a mentionat anterior, procesele acceseaza datele din pipe în maniera FIFO, ceea ce însemna ca ordinea în care datele sunt scrise într-un pipe este si ordinea în care vor fi citite din pipe.

Numarul de procese care scriu în pipe nu trebuie sa fie egal cu numarul de procese care citesc din acesta. Daca numarul proceselor care scriu sau citesc este mai mare decât 1, coordonarea utilizarii pipe-ului va trebui realizata prin alte mecanisme. Nucleul acceseaza datele unui pipe în acelasi fel cum acceseaza datele unui fisier obisnuit, cu diferenta ca pipe-urile utilizeaza pentru o mai mare eficienta doar blocuri directe, desi aceasta limiteaza cantitatea datelor ce pot fi stocate la un moment dat în pipe. Nucleul manipuleaza blocurile directe ale inodului ca o coada circulara si pastreaza intern pointerii de citire si scriere pentru a conserva modul de lucru FIFO (vezi figura 5.17). Nucleul nu scrie niciodata în pipe peste date care nu au fost citite.

Figura 5.17. Situatie posibila în utilizarea unui pipe.

Consideram în continuare patru situatii în care se poate executa citirea sau scrierea pipe-urilor:

scrierea într-un pipe în care este spatiu suficient pentru datele ce vor fi scrise;

citirea dintr-un pipe în care sunt date suficiente;

scrierea într-un pipe în care nu este spatiu suficient pentru datele ce vor fi scrise;

citirea dintr-un pipe care nu are date suficiente;

Notam:

Np = numarul de octeti ce se afla deja în pipe;

Nw= numarul de octeti ce vor fi scrisi în pipe;

Nr = numarul de octeti ce vor fi cititi din pipe;

Cp = capacitatea pipe-ului;

În primul caz (Np+Nw) Cp. Nucleul executa algoritmul de scriere pentru un fisier obisnuit, cu diferenta ca incrementeaza automat dimensiunea pipe-ului, deoarece cantitatea de date din pipe se mareste odata cu fiecare scriere. În cazul fisierelor obisnuite procesele incrementeaza dimensiunea fisierului numai când se scriu date dupa sfârsitul fisierului. Daca urmatorul deplasament din pipe iese din zona blocurilor directe, nucleul stabileste valoarea acestuia în u area la 0 (începutul pipe-ului). Când un proces termina de scris date în pipe, nucleul actualizeaza pointerul de scriere din inodul fisierului pipe, astfel încât urmatorul proces care va scrie în pipe sa o faca de unde a ramas procesul anterior. Apoi nucleul trezeste toate procesele care asteptau sa citeasca date din pipe.

Când un proces citeste dintr-un pipe, el verifica daca pipe-ul este sau nu gol. Daca pipe-ul contine date (Nr Np), nucleul le citeste folosind algoritmul read pentru fisiere obisnuite. Dupa citirea fiecarui bloc, nucleul decrementeaza dimensiunea pipe-ului conform numarului de octeti cititi, si actualizeaza valoarea deplasamentului din u area (daca este necesar poate fi readusa la 0). Apoi sunt trezite toate procesele care sunt în asteptare si care doresc sa scrie în pipe si se reactualizeaza deplasamentul curent de citire în inod.

Daca un proces încearca sa citeasca dintr-un pipe mai multe date decât exista (Nr>Np), se citesc toate datele din pipe, iar când acesta devine gol procesul se pune în asteptare pâna când va fi trezit de un alt proces care scrie în pipe. În cazul în care procesul a deschis pipe-ul cu optiunea ano delaya, citirea se va încheia fara ca procesul sa se puna în asteptare.

Daca un proces scrie în pipe mai multe date, iar pipe-ul nu le poate pastra pe toate, nucleul îl pune asteptare pâna când un alt proces va citi date din pipe. O exceptie o constituie cazul în care cantitatea de date ce trebuie scrise depaseste capacitatea pipe-ului (Nw>Cp). În aceasta situatie nucleul va scrie date în pipe pâna acesta se umple, dupa care pune procesul în asteptare pâna când în pipe va fi suficient spatiu disponibil. În acest caz exista posibilitatea ca datele scrise de proces în pipe sa nu fie contigue daca un alt proces scrie în pipe înainte ca procesul initial sa-si reia executia.

Studiind implementarea pipe-urilor reiese ca interfata este asemanatoare celei pentru fisiere obisnuite, dar implementarea difera deoarece nucleul pastreaza deplasamentul de citire si scriere în inod si nu în tabela de fisiere. Aceste valori trebuie pastrate în inod pentru pipe-urile numite astfel încât procesele sa le poata folosi în comun. Ele nu pot fi pastrate în tabela de fisiere pentru ca la fiecare deschidere se aloca o noua intrare în tabela.

Solutia adoptata nu ar fi neaparat necesara în cazul pipe-urilor nenumite (procesele utilizeaza în comun pipe-ul folosind intrari comune în tabela de fisiere) , însa s-a optat pentru ea deoarece codul este mai simplu.

Închiderea pipe-urilor

La închiderea unui pipe, un proces urmeaza aceeasi procedura ca si la închiderea unui fisier obisnuit, exceptând faptul ca nucleul executa unele prelucrari speciale înainte de a elibera inodul pipe-ului. Astfel, el decrementeaza contorul corespunzator numarului de procese care scriu sau citesc din pipe. Daca numarul proceselor care scriu în pipe este 0 si sunt procese care astepta sa citeasca date din pipe, nucleul le trezeste, iar ele vor încheia operatia de citire fara a citi date. Daca numarul proceselor care citesc a scazut la 0 si sunt procese care asteapta sa scrie, nucleul le trezeste si le trimite un semnal ce indica o conditie de eroare.

Desi în cazul pipe-urilor numite exista posibilitatea de a apare noi procese redactor sau cititor, nucleul le trateaza în aceeasi maniera ca pe cele nenumite. Când nici un proces cititor sau redactor nu mai acceseaza pipe-ul, nucleul elibereaza toate blocurile sale de date si actualizeaza inodul pentru a indica ca pipe-ul este gol. Când nucleul elibereaza inodul unui pipe obisnuit, el elibereaza si copia disc pentru a fi reasignata.

Exemple

char string[ ]= "hello";

main()

}

Figura 5.18 Scrierea si citirea unui pipe

În figura 5.18 este prezentat un program care ilustreaza modul de functionare al unui pipe. Procesul creeaza un pipe si intra într-un ciclu infinit scriind în pipe cuvântul "hello" si pe care-l citeste dupa aceea. Nucleul nu stie si nici nu este interesat sa stie daca un acelasi proces scrie si citeste pipe-ul.

Un proces care executa programul din figura 5.19 creeaza un nod(pipe numit) "fifo" în care scrie sau citeste într-un ciclu infinit functie de numarul de parametri cu care a fost apelat. Datorita permisiunilor cu care a fost creat pipe-ul, pot participa la comunicarea prin intermediul acestuia si alti utilizatori.

#include <fnctl.h>

char string[ ] = "hello"

int argc;

char *argv[ ];

main(argc,argv)

Figura 5.19 Scrierea si citirea dintr-un pipe numit

5.13 Apelul sistem dup

Apelul sistem dup copiaza un descriptor de fisier în primul slot (intrare) liber din tabela descriptorilor de fisier utilizator, returnând utilizatorului noul descriptor. Apelul poate fi folosit pentru toate tipurile de fisiere. Este utilizat în constructia programelor complexe pornind de la programe mai simple (vezi capitolul 7, construirea pipeline-urilor shell ). Sintaxa apelului este:

fdÎnou = dup(fd);

unde fd este descriptorul de fisier care va fi duplicat, iar fdÎnou este noul descriptor care va referi fisierul. Contorul intrarii din tabela de fisiere corespunzatoare lui fd va fi incrementat, deoarece si intrarea din tabela descriptorilor de fisier utilizator a lui fdÎnou va pointa catre aceeasi intrare în tabela de fisiere.

Examinând, de exemplu, structurile de date prezentate în figura 5.20 reies urmatoarele: un proces a deschis fisierul "/etc/passwd" (descriptor 3), apoi fisierul "local" (descriptor 4), si înca odata fisierul "/etc/passwd" (descriptor 5). În final a fost duplicat descriptorul 3 apelând dup care a returnat descriptorul 6.

5.20 Structurile de date dupa apelul sistem dup

#include <fcntl.h>

main()

Figura 5.21 Program C în care este folosit apelul dup

În programul din figura 5.21 variabila i contine descriptorul de fisier returnat la deschiderea fisierului "/etc/passwd", iar variabila j pe cel obtinut prin duplicarea lui i. Ambii descriptori pointeaza catre aceeasi intrare din tabela de fisiere, utilizând deci acelasi deplasament. Astfel, dupa primele doua citiri, buf1 si buf2 vor contine informatii diferite. Procesul poate închide unul dintre descriptori dar operatiile de I/O pot continua fara probleme prin intermediul celuilalt descriptor.

5.14 Montarea/demontarea sistemelor de fisiere

Dupa cum se cunoaste, un disc fizic poate contine mai multe partitii logice, realizate de catre driverul de disc. Fiecare partitie are un nume de fisier dispozitiv. Un proces poate accesa datele unei partitii deschizând fisierului asociat acesteia, fisier tratat ca o succesiune de blocuri disc în care se poate scrie sau citi (pentru detalii vezi capitolul 6). O astfel de partitie disc poate contine un sistem logic de fisiere ce consta din: un bloc de boot, superblocul, lista de inoduri, si blocuri de date. Prezentarea acestor structuri s-a facut în capitolul 2. Un sistem de fisiere poate fi conectat logic (montat) într-unul din nodurile arborelui unui alt sistem de fisiere prin intermediul apelului sistem mount. Demontarea se face folosind apelul sistem umount.

Sintaxa apelului sistem mount este:

mount (cale-speciala, cale-director, optiuni);

unde caleÎspeciala este numele fisierului dispozitiv special corespunzator partitiei disc ce contine sistemul de fisiere ce va fi montat, cale­Îdirector este directorul din ierarhia existenta în care se va efectua montarea, numit si punct de montare (mount point), iar optiuni indica daca sistemul de fisiere ar trebui montat "read-only" (sistemul de fisiere va putea fi accesat doar pentru citire, iar apeluri sistem cum ar fi write si creat care modifica sistemul de fisiere vor esua).

Daca un proces executa apelul sistem mount ("/dev/dsk1","/usr",0), nucleul ataseaza sistemul de fisiere continut în partitia disc numita "/dev/dsk1" în directorul "/usr" din arborele existent al unui sistem de fisiere (vezi figura 5.22). Fisierul "/dev/dsk1" este un fisier special bloc, adica el este numele unui dispozitiv bloc, uzual o partitie disc. Nucleul considera ca partitia disc indicata contine un sistem de fisiere. Dupa conectare, radacina sistemului de fisiere montat este accesata prin numele "/usr", iar procesele pot accesa fisierele partitiei în mod normal.

Figura 5.22 Sistemul de fisiere înainte si dupa montare

Nucleul dispune de o tabela de montare (Mount Table - MT) cu intrari pentru fiecare sistem de fisiere montat. Fiecare intrare contine:

un numar de dispozitiv care identifica sistemul de fisiere conectat (acesta este numarul logic al sistemului de fisiere mentionat anterior);

un pointer la un buffer ce contine superblocul sistemului de fisiere (orice sistem de fisiere activ are superblocul în memoria interna);

un pointer la inodul radacina al sistemului de fisiere ce va fi montat ("/" al sistemului de fisiere "/dev/dsk1" din figura 5.22);

un pointer la inodul directorului în care se va monta sistemul de fisiere ("usr" din sistemului de fisiere radacina, vezi figura 5.22)

Asocierea ce se realizeaza pe durata apelului sistem mount între inodul punctului de montare si inodul radacina al sistemului de fisiere montat permite nucleului sa traverseze cu usurinta noua structura de directoare creata.

În figura 5.23 este prezentat algoritmul pentru montarea unui sistem de fisiere. Nucleul acorda numai superutilizatorului dreptul de a monta si demonta sisteme de fisiere.

Nucleul gaseste inodul fisierului special corespunzator sistemului de fisiere ce trebuie montat, extrage numerele major si minor care identifica partitia disc corespunzatoare, iar apoi gaseste inodul directorului în care se va face montarea.

algoritm mount

intrari: numele fisierului bloc special

numele directorului în care se face montarea

optiuni de montare (read only)

iesire: niciuna

gaseste o intrare libera în MT;

/* pentru citirea superblocului */

apeleaza rutina de deschidere a fisierului special;

obtine un buffer liber din buffer-ul cache (varianta algoritm getblk);

initializeaza câmpurile superblocului;

obtine nodul pentru radacina al dispozitivului montat (algoritm iget) si-l

salveaza în MT;

marcheaza inodul directorului în care se face montarea ca punct de

montare;

elibereaza inodul fisierului special (algoritm iput);

deblocheaza inodul directorului punct de montare;

}

Figura 5.23 Algoritmul pentru montarea unui sistem de fisiere

Contorul de referinta al inodului nu trebuie sa fie mai mare decât 1 (trebuie sa fie cel putin 1). Apoi, nucleul aloca o intrare libera în tabela de montare, marcheaza intrarea ca fiind "în curs de folosire" si asigneaza câmpul care contine numarul de dispozitiv din tabela de montare. Asignarile de mai sus se fac imediat deoarece operatia de montare nu este atomica (procesul apelator se poate pune în asteptare în decursul executiei procedurii de deschidere a fisierului dispozitiv sau la citirea superblocului sistemului de fisiere, iar un alt proces poate încerca sa monteze un sistem de fisiere). Marcând intrarea din tabela de montare ca fiind în curs de folosire, nucleul previne ca doua operatii de montare sa utilizeze aceeasi intrare. Prin notarea în intrare a numarului de dispozitiv, se previne montarea unui sistem de fisiere de mai multe ori.

Procedura de deschidere a dispozitivului bloc ce contine sistemul de fisiere verifica daca dispozitivul este corespunzator, uneori initializând structurile de date ale driverului si transmitând comenzi de initializare catre hardware. Nucleul aloca apoi un buffer liber din sistemul de buffere pentru citirea superblocului sistemului de fisiere ce va fi montat. Citirea superblocului se face utilizând o varianta a algoritmului read. Pentru a permite traversarea punctului de montare în ambele sensuri se pastreaza în intrarea din tabela de montare un pointer catre inodul directorului din arborele de fisiere unde se executa montarea (pentru traversare spre radacina arborelui), si un pointer catre radacina sistemului de fisiere montat (pentru traversare spre baza arborelui). Directorul în care se realizeaza montarea si radacina sistemului de fisiere ce se monteaza sunt echivalente din punct de vedere logic, iar nucleu stabileste aceasta echivalenta prin pastrarea lor în intrarea din tabela de montare. Dupa montare, procesele nu vor mai accesa inodul directorului în care se face montarea.

Nucleul initializeaza câmpurile superblocului punând pe zero câmpurile de blocare pentru lista blocurilor libere si cea a inodurilor libere, si stabilind numarul inodurilor libere din superbloc la zero. Scopul initializarilor este de a minimiza pericolul de alterare a sistemului de fisiere datorita folosirii acestuia pe durata montarii.

Daca sistemul de fisiere este montat cu optiunea "numai citire" pentru a nu permite modificarea sa, nucleul seteaza un indicator în superbloc. În final, nucleul marcheaza inodul în care s-a facut montarea ca punct de montare pentru ca alte procese sa-l poata identifica ulterior. Figura 5.24 prezinta legaturile ce se stabilesc între structurile de date ale nucleului dupa apelul sistem mount.

Figura 5.24 Structurile de date dupa montare

Parcurgerea punctelor de montare într-un nume de fisier

Sa rescriem algoritmii namei si iget pentru cazurile în care numele fisierului traverseaza un punct de montare.

Cele doua sensuri de traversare sunt: de la sistemul de fisiere în care se face montarea la cel montat (de la radacina sistemului global catre nodurile frunza) si invers. Urmatoarea secventa de comenzi shell ilustreaza cele doua cazuri.

mount /dev/dsk1 /usr

cd /usr/src/uts

cd ../../..

algoritm iget

intrare: numarul inodului din sistemul de fisiere

iesire: inodul blocat

/* în caz ca inodul este punct de montare */

if (inodul este punct de montare)

if (inodul se afla în lista inodurilor libere)

scoate inodul din lista;

incrementeaza contorul de referinta al inodului;

return (inod);

}

/* inodul nu este în cache */

scoate un nou inod din lista de inoduri libere;

reseteaza numarul inodului si sistemul de fisiere;

scoate inodul din vechea lista hash si îl plaseaza în cea noua;

citeste inodul de pe disc (algoritmul bread);

initializeaza inodul (contor=1);

return (inod);

}

}

Figura 5.25 Algoritmul revizuit pentru accesarea unui inod

Comanda mount invoca apelul sistem mount dupa ce executa în prealabil unele verificari, si monteaza în directorul "/usr" sistemul de fisiere identificat prin fisierul special "/dev/dsk1". Prima comanda shell cd (care lanseaza apelul sistem chdir) detrmina traversarea punctului de montare atunci când nucleul analizeaza calea, de la "/usr" spre "uts". A doua comanda cd determina traversarea punctului de montare atunci când nucleul analizeaza calea de la "uts "spre radacina sistemului de fisiere ( "/" ). Pentru traversarea punctului de montare în primul caz este prezentat algoritmul iget modificat (vezi figura 5.25), care este identic cu cel din figura 4.3, exceptând faptul ca se verifica daca inodul este un punct de montare. Daca inodul este marcat "punct de montare", nucleul va gasi intrarea din tabela de montare corespunzatoare inodului în care s-a facut montarea si va memora numarul dispozitivului pentru sistemul de fisiere montat. Folosind numarul de dispozitiv si numarul inodului radacina (care este comun tuturor sistemelor de fisiere), se acceseaza inodul radacina al dispozitivului montat si se returneaza acel inod. În prima schimbare de director din exemplul anterior, nucleul acceseaza mai întâi inodul lui "/usr" din sistemul de fisiere în care s-a executat montarea, îl gaseste marcat "punct de montare", gaseste în tabela de montare inodul radacina al sistemului de fisiere montat, si apoi îl acceseaza.

Pentru al doilea caz de traversare a punctului de montare este prezentat algoritmul revizuit namei (vezi figura 5.26). El este similar celui prezentat în figura 4.11. Totusi, dupa gasirea numarului inodului corespunzator componentei din calea specificata pentru numele fisierului, nucleul verifica daca numarul inodului este al inodului radacina al unui sistem de fisiere. În caz afirmativ, daca inodul de lucru curent este de asemenea radacina si componenta din cale este "..", nucleul identifica inodul ca fiind un punct de montare. Apoi, gaseste intrarea din tabela de montare a carui numar de dispozitiv este egal cu numarul de dispozitiv al ultimului inod gasit, obtine inodul directorului în care s-a facut montarea si continua cautarea pentru ".." folosind ca inod de lucru inodul în care s-a facut montarea.

algoritm namei  /* converteste numele caii în inod */

intrare: numele fisierului

iesire: inod blocat

elibereaza inodul de lucru (algoritmul iput);

inodul de lucru = inodul pentru noul numar de inod (algoritm

iget);

}

else /* componenta nu este în director */

return (nu a fost gasit nici un inod);

}

return (inodul de lucru);

}

Figura 5.26 Algoritmul revizuit pentru parcurgerea unui nume de fisier

În exemplul anterior (cd "../../..") presupunem ca directorul curent de început al procesului este "/usr/src/uts". Când se analizeaza calea conform algoritmului namei, inodul de lucru cu care se începe este cel al directorului curent. Nucleul schimba inodul de lucru dupa analiza primei componente ".." din cale, devenind "/usr/src". Când se analizeaza urmatoarea componenta "..", nucleul gaseste inodul radacina corespunzator unui sistem de fisiere montat, "usr", iar el devine inod de lucru în namei. La analiza ultimei componente ".." nucleul determina ca numarul inodului pentru ".." este numarul inodului radacina, inodul sau de lucru este inodul radacina si ca ".." este componenta curenta a numelui fisierului. Nucleul determina intrarea în tabela de montare pentru punctul de montare "usr", elibereaza inodul de lucru curent (radacina sistemului de fisiere "usr") si aloca inodul în care s-a facut montarea (inodul directorului "usr" din sistemul de fisiere radacina) ca noul inod de lucru.Apoi, cauta intrarea ".." în structura directorului "/usr" si gaseste numarul inodului pentru radacina sistemului de fisiere ("/").

Demontarea sistemului de fisiere

Apelul sistem destinat pentru realizarea acestei operatii este umount, iar sintaxa sa este urmatoarea:

umount (nume fisier special);

unde nume fisier special reprezinta sistemul de fisiere ce va fi demontat.

algoritm umount

intrare: numele fisierului special ce contine sistemul de fisiere ce va fi demontat

iesire: niciuna

Figura 5.27 Algoritmul pentru demontarea unui sistem de fisiere

Când demontam un sistem de fisiere (figura 5.27), nucleul acceseaza inodul dispozitivului ce va fi demontat, stabileste numarul dispozitivului pentru fisierul special, elibereaza inodul (algoritmul iput) si gaseste intrarea în tabela de montare a carui numar de dispozitiv este egal cu cel al fisierului special. Daca în momentul demontarii exista procese ce acceseaza fisiere din sistemul de fisiere ce urmeaza a fi demontat, atunci apelul umount va esua. În caz contrar fisierele active ar deveni inaccesibile. Anumite aspecte referitoare la aceasta situatie pot fi gasite în capitolul 8.

Daca în sistemul de buffere mai exista date care nu au fost scrise pe disc (blocuri marcate "întârziere la scriere"), nucleul le va scrie. Nucleul sterge intrarile de text partajat din tabela regiunilor care nu sunt operationale (pentru detalii vezi capitolul 8), salveaza pe disc toate superblocurile recent modificate si actualizeaza copiile disc pentru toate inodurile care trebuie actualizate. Apoi, nucleul elibereaza inodul radacina al sistemului de fisiere montat (pastrat de la executia apelului sistem mount) si invoca driverul dispozitivului ce contine sistemul de fisiere pentru închiderea dispozitivului. Dupa aceea, buffere-le din cache ce contin blocuri de date corespunzatoare sistemului de fisiere demontat vor fi invalidate. Invalidarea buffere-lor presupune mutarea lor la începutul listei buffere-lor libere, pentru ca cele valide sa ramâna mai mult timp în cache. Se sterge semnalizatorul "punct de montare" din inodul în care s-a facut montarea (setat în apelul mount) si se elibereaza inodul. Dupa eliberarea intrarii corespunzatoare din tabela de montare, apelul umount se încheie.

5.15 Apelul sistem link

Apelul sistem link permite accesarea unui fisier printr-un nume nou, creându-se în structura de directoare a sistemului de fisiere o noua intrare director pentru un inod existent. Sintaxa apelului este:

link (numeÎfisier sursa, numeÎfisier nou);

unde nume­Îfisier sursa este numele unui fisier existent iar nume­Îfisier nou este noul nume (aditional) pe care fisierul îl va avea dupa încheierea apelului. Sistemul de fisiere contine un nume de cale pentru fiecare legatura a fisierului, iar procesele pot accesa fisierul folosind oricare dintre cai. Nucleul nu cunoaste numele initial al fisierului, asa ca toate numele fisierului sunt tratate la fel. De exemplu, dupa executia apelurilor sistem

link ("/usr/src/uts/sys","/usr/include/sys");

link("/usr/include/realfile.h","/usr/src/uts/sys/testfile.h");

urmatoarele trei nume vor referi acelasi fisier "/usr/src/uts/sys/testfile.h", "/usr/include/sys/testfile.h", "/usr/include/realfile.h" (vezi figura 5.28).

Figura 5.28 Exemplu de fisiere legate într-un sistem de fisiere

Pentru simplitatea codului programelor ce traverseaza arborele sistemului de fisiere, nucleul acorda numai superutilizatorului dreptul de a lega directoare. Daca orice utilizator ar avea acest drept, programele concepute sa traverseze ierarhia de fisiere ar trebui sa testeze într-un ciclu infinit daca utilizatorul urmeaza sa lege directorul la un nume de nod aflat sub el în ierarhie. Se considera ca superutilizatorii sunt mai atenti când executa astfel de legaturi. Facilitatea de legare a directoarelor trebuia sa fie prezenta la versiunile initiale ale sistemului, deoarece statea la baza implementarii comenzii mkdir, de creare a unui nou director.

algoritmul link

intrari: nume fisier existent

noul nume asociat fisierului

iesire: niciuna

incrementeaza contorul de legaturi al inodului;

actualizeaza copia disc a inodului;

deblocheaza inodul;

determina inodul pentru directorul parinte care va contine noul nume de fisier

(algoritmul namei);

if (noul nume de fisier deja exista sau fisierul existent si cel nou se afla în

sisteme de fisiere diferite)

creeaza o noua intrare în directorul parinte pentru noul nume al fisierului cu:

numele noului fisier, numarul inodului corespunzator numelui fisierului existent;

elibereaza inodul directorului parinte (algoritm iput);

elibereaza inodul fisierului existent (algoritm iput);

Figura 5.29 Algoritm de creare a legaturilor pentru fisiere

În algoritmul link (vezi figura 5.29), nucleul aloca mai întâi un inod pentru fisierul sursa (folosind algoritmul namei), incrementeaza contorul sau cu numarul de legaturi, actualizeaza copia disc a inodului (pentru consistenta) si deblocheaza inodul. Apoi cauta noul fisier. În caz ca este gasit, nucleul decrementeaza contorul incrementat anterior, iar apelul se încheie cu eroare. Altfel, determina o intrare libera în directorul parinte al noului fisier, pe care o completeaza cu numele noului fisier si numarul inodului fisierului sursa, dupa care elibereaza inodul directorului parinte. Deoarece noul fisier nu exista la începutul apelului, nu vor elibera alte inoduri. În final, nucleul elibereaza inodul fisierului sursa. În urma apelului link contorul de legaturi (numarul de intrari director care refera fisierul si care este diferit de contorul de referinta al inodului) este incrementat. Daca nici un alt proces nu mai acceseaza fisierul la încheierea apelului link, contorul de referinta al inodului este 0, iar contorul de legaturi este cel putin 2.

De exemplu, la executia apelului

link ("source","dir/target");

nucleul gaseste inodul pentru "source", incrementreaza numarul sau de legaturi, îi memoreaza numarul inodului - sa zicem 74 , si deblocheaza inodul. Localizeaza inodul directorului "dir", parintele fisierului "target", gaseste o intrare libera în "dir" în care scrie numele lui "target" si numarul de inod 74. În final este eliberat inodul lui "source" folosind algoritmul iput. Daca contorul de legaturi a fost 1, acum el este 2.

Prezentam în continuare doua posibilitati de blocare a sistemului care arata motivul pentru care trebuia deblocat inodul fisierului sursa dupa incrementarea contorului de legaturi. Daca nucleul nu ar debloca inodul, doua procese s-ar putea bloca reciproc prin executarea simultana a urmatoarelor apeluri:

procesul A: link("a/b/c/d","e/f/g");

procesul B: link("e/f","a/b/c/d/ee");

Presupunem ca sistemul se afla în starea în care fiecare proces a alocat inodul corespunzator; procesul A inodul pentru fisierul "a/b/c/d", iar procesul B inodul pentru fisierul "e/f". Figura 5.30 prezinta un scenariu de executie. Când procesul A va încerca sa gaseasca inodul pentru directorul "e/f", el se va pune în asteptare pâna când inodul lui "f" devine liber. La fel, procesul B încearca sa gaseasca inodul pentru directorul "a/b/c/d", punându-se în asteptare pâna când inodul lui "d" devine liber.

Se ajunge astfel la o situatie clasica de blocare în care doua procese ocupa fiecare câte o resursa si se blocheaza asteptând eliberarea resursei ocupata de celalalt proces. Aceasta situatie este evitata prin deblocarea inodului fisierului sursa dupa incrementarea contorului de legaturi. Deoarece prima resursa (inod) este libera în momentul accesarii urmatoarei resurse, nu va avea loc o blocare.

procesul A procesul B

. încearca sa obtina inodul pentru

. SE PUNE IN ASTEPTARE - inodul este

. blocat

. .

obtine inodul pentru .

elibereaza inodul lui .

obtine inodul pentru .

elibereaza inodul lui .

obtine inodul pentru .

elibereaza inodul lui .

obtine inodul pentru .

. .

încearca sa obtina inodul pentru .

SE PUNE IN ASTEPTARE - inodul este blocat .

. .

.

TREZIRE PROCESE- inodul lui este deblocat

. .

. obtine inodul pentru

. elibereaza inodul lui

. obtine inodul pentru

. obtine inodul pentru

. elibereaza inodul lui

. .

. încearca sa obtina inodul pentru

. SE PUNE IN ASTEPTARE -

. procesul A a blocat inodul

. .

. .

obtine inodul pentru .

elibereaza inodul lui .

încearca sa obtina inodul pentru .

SE PUNE IN ASTEPTARE - procesul B a blocat inodul .

BLOCARE RECIPROCĂ

Timp

Figura 5.30 Situatie de blocare reciproca pentru apelul link

Exemplu anterior a prezentat cazul în care doua procese se pot bloca reciproc în cazul în care inodul nu este deblocat. Exista posibilitatea ca un proces sa se autoblocheze. Daca executa

link("a/b/c",a/b/c/d");

nucleul va aloca în prima parte a algoritmului un inod pentru fisierul "c". Daca nu ar fi deblocat, se va ajunge la o situatie de blocare în cazul în care trebuie sa se acceseze inodul lui "c" pentru a cauta fisierul "d". Daca doua procese, sau chiar unul nu si-ar putea continua executia datorita unei blocari, sistemul nu o va putea înlatura fara o noua initializare. Aceasta deoarece inodurile sunt resurse alocabile în numar finit, iar receptionarea unui semnal nu poate trezi un proces aflat în asteptare(detalii în capitolul 8).

Se mentioneaza ca blocarea unui director poate afecta si alte procese.

5.16 Întretinerea sistemelor de fisiere

Pe durata operarii normale nucleul pastreaza consistenta sistemului de fisiere. Totusi, pot exista situatii deosebite (cum ar fi caderea tensiunii de alimentare) în care sistemele de fisiere sunt lasate într-o stare de inconsistenta. În astfel de situatii majoritatea datelor sunt în buna stare, dar pot exista si date care sa fie afectate de aceste accidente. Comanda fsck poate verifica si reface sistemul de fisiere. Ea acceseaza sistemul de fisiere printr-o interfata (descrisa în capitolul 6) care ocoleste metodele clasice de acces la fisier. În continuare se vor descrie unele inconsistente verificate cu fsck.

Un bloc de pe disc poate apartine mai multor inoduri, sau este în lista blocurilor libere si apartine totodata unui inod. La prima initializare a unui sistem de fisiere toate blocurile disc sunt în lista libera. Atunci când un bloc de date este utilizat, el este sters din lista blocurilor libere si va figura în structura unui inod. Nucleul nu poate reasigna blocul disc altui inod pâna când acesta nu va fi introdus în lista blocurilor libere. Astfel, un bloc disc este fie în lista libera, fie asignat unui inod. Sa consideram situatia în care nucleul elibereaza un bloc disc al unui fisier, numarul blocului fiind depus în lista libera din superbloc, iar apoi aloca blocul disc eliberat unui alt fisier. Daca nucleul scrie inodul si blocurile noului fisier pe disc, iar sistemul cade înaintea actualizarii pe disc a inodului vechiului fisier, atunci vor exista doua inoduri care vor referi acelasi bloc disc. Similar, daca nucleul scrie pe disc superblocul si lista sa libera, iar sistemul cade înaintea scrierii pe disc a vechiului inod, atunci blocul disc va aparea atât în lista libera cât si în vechiul inod.

Daca un numar de bloc nu este nici în lista blocurilor libere si nu este nici continut de un fisier, sistemul este inconsistent (dupa cum s-a mentionat anterior, blocurile trebuie sa apara undeva). Aceasta situatie poate avea loc daca blocul disc este sters din fisier si plasat în lista libera. Daca vechiul fisier este scris pe disc, iar sistemul cade înaintea scrierii pe disc a superblocului, blocul nu va aparea în niciuna din listele memorate pe disc.

Desi un inod nu are contorul cu numarul de legaturi egal cu zero, este posibil ca numarul inodului sa nu fie gasit în niciunul din directoarele sistemului de fisiere. Toate fisierele exceptând pipe-urile nenumite trebuie sa existe în sistemul de fisiere. Daca sistemul cade dupa crearea unui pipe sau dupa crearea unui fisier, dar înainte de a-i crea intrarea în director, inodul va avea numarul de legaturi setat chiar daca nu va aparea în sistemul de fisiere.

Daca formatul unui inod este incorect (de exemplu, daca tipul fisierului este nedefinit) se pot produce erori. Aceasta se poate întâmpla daca administratorul monteaza un tip de sistem de fisiere formatat necorespunzator. Nucleul acceseaza blocuri disc despre care crede ca contin inoduri, dar care în realitate contin date.

Daca un numar de inod apare într-o intrare director dar inodul este liber, sistemul de fisiere este inconsistent deoarece numarul inodului ce apare într-o intrare în director trebuie sa fie al unui inod alocat. Aceasta se întâmpla când nucleul scrie pe disc intrarea director pentru un fisier nou creat si sistemul cade înaintea scrierii pe disc a inodul corespunzator. Aceeasi situatie are loc si în cazul în care un proces desface legatura unui fisier si scrie inodul eliberat pe disc, dar nu mai apuca sa scrie pe disc directorul. Aceste situatii sunt evitate prin efectuarea operatiilor de scriere într-o anumita ordine.

Daca numerele blocurilor si inodurilor libere memorate în superbloc nu corespund realitatii existente pe disc, sistemul de fisiere este inconsistent.


Document Info


Accesari: 2787
Apreciat: hand-up

Comenteaza documentul:

Nu esti inregistrat
Trebuie sa fii utilizator inregistrat pentru a putea comenta


Creaza cont nou

A fost util?

Daca documentul a fost util si crezi ca merita
sa adaugi un link catre el la tine in site


in pagina web a site-ului tau.




eCoduri.com - coduri postale, contabile, CAEN sau bancare

Politica de confidentialitate | Termenii si conditii de utilizare




Copyright © Contact (SCRIGROUP Int. 2024 )