Laborator 05 - Sistemul de fișiere

În acest laborator vom lucra atât în mașina virtuală de Linux, cât și pe Micro:bit pentru a realiza operații cu sistemul de fișiere.

Fișiere. Sisteme de fișiere

Fișierul este una dintre abstractizările fundamentale în domeniul sistemelor de operare; cealaltă abstractizare este procesul. Dacă procesul abstractizează execuția unei anumite sarcini pe procesor, fișierul abstractizează informația persistentă a unui sistem de operare. Un fișier este folosit pentru a stoca informațiile necesare funcționării sistemului de operare și interacțiunii cu utilizatorul.

Un sistem de fișiere este un mod de organizare a fișierelor și prezentare a acestora utilizatorului. Din punctul de vedere al utilizatorului, un sistem de fișiere are o structură ierarhică de fișiere și directoare, începând cu un director rădăcină. Localizarea unei intrări (fișier sau director) se realizează cu ajutorul unei căi în care sunt prezentate toate intrările de până atunci. Astfel, pentru calea /usr/local/file.txt directorul rădăcină '/' are un subdirector usr care include subdirectorul local ce conține un fișier file.txt.

Fiecare fișier are asociat, așadar, un nume cu ajutorul căruia se face identificarea, un set de drepturi de acces și zone conținând informația utilă.

Sistemele de fișiere suportate de sistemele de operare de tip Unix și Windows sunt ierarhice. Sistemele Linux/Unix sunt case-sensitive (Data este diferit de data), iar sistemele Windows sunt case-insensitive.

Ierarhia sistemului de fișiere Unix are un singur director cunoscut sub numele de root și notat '/', prin care se localizează orice fișier (a nu se confunda cu directorul /root, care este home-ul utilizatorului privilegiat, root). Notația Unix pentru căile fișierelor este un șir de nume de directoare despărțite prin '/', urmat de numele fișierului. Există și căi relative la directorul curent '.' sau la directorul părinte '..'.

În Unix nu se face nicio deosebire între fișierele aflate pe partițiile discului local, pe CD sau pe o mașină din rețea. Toate aceste fișiere vor face parte din ierarhia unică a directorului root. Acest lucru se realizează prin montare: sistemele de fișiere vor fi montate într-unul dintre directoarele sistemului de fișiere rădăcină.

În Windows există mai multe ierarhii, câte una pentru fiecare partiție și pentru fiecare loc din rețea. Spre deosebire de Unix, delimitatorul între numele directoarelor dintr-o cale este '\', și pentru căile absolute trebuie specificat numele ierarhiei în forma C:\, E:\ sau \\FILESERVER\myFile (pentru rețea). Ca și Unix, Windows folosește '.' pentru directorul curent și '..' pentru directorul părinte.

Sistemul de fișiere Micro:bit

Pe Micro:bit sistemul de fișiere se rezumă la un director rădăcină care conține toate fișierele de pe plăcuță.

Fiind un sistem mult mai simplu, fără utilizatori, fișierele de pe Micro:bit nu au detalii adiacente, exceptând dimensiunea.

Operații pe fișiere

În Unix, un descriptor de fișier este un întreg care indexează o tabelă cu pointeri spre structuri care descriu fișierele deschise de un proces. În cazul în care un program rulează într-un shell Unix, procesul părinte (shell-ul) deschide pentru procesul copil (programul respectiv) 3 fișiere standard având descriptori de fișiere cu valori speciale:

 Descriptorii standard de fișiere

  • standard input (0) - citirea de la intrarea standard (tastatură)
  • standard output (1) - afișarea la ieşirea standard (consolă)
  • standard error (2) - afișarea la ieşirea standard de eroare (consolă)

In Python, cei trei descriptori de fisiere pot fi accesati cu ajutorul bibliotecii sys:

Un fișier are asociat cursorul de fișier (file pointer) care indică poziția curentă în cadrul fișierului. Cursorul de fișier este un întreg care reprezintă deplasamentul (offset-ul) față de începutul fișierului.

Operațiile specifice pentru lucrul cu fișiere:

  • deschiderea/crearea unui fișier - înseamnă asocierea unui descriptor de fișier identificat prin numele său 1). ( Linux )
  • închiderea unui fișier - înseamnă eliberarea structurilor de fișier asociate procesului și a descriptorului acelui fișier - doar dacă nu mai există nici o intrare în tabela file descriptorilor care să puncteze spre acea structură 2). ( Linux )
  • citirea dintr-un fișier - înseamnă copierea unui bloc de date într-un buffer; după ce se realizează citirea se actualizează cursorul de fișier 3). ( Linux )
  • scrierea într-un fișier - înseamnă copierea unui bloc de date dintr-un buffer în fișier; efectuarea scrierii înseamnă și actualizarea cursorului de fișier 4). ( Linux, Windows )
  • poziționarea într-un fișier - înseamnă schimbarea valorii cursorului de fișier; citirile sau scrierile ulterioare vor porni din locul indicat de acest cursor de fișier 5). ( Linux )
  • schimbarea atributelor unui fișier - înseamnă stabilirea unor parametri pentru fișier 6). ( Linux)

Operații cu fișiere Micro:bit

Pentru a manipula fișiere folosind MicroPython pentru Micro:bit, avem la dispoziție modulul os împreună cu un set de funcții ce permit citirea/scrierea de fișiere.

Folosind modulul os putem folosi următoarele funcții:

  • os.listdir() - returnează o listă cu numele fișierelor stocate pe dispozitiv
  • os.remove(filename) - șterge fișierul cu numele filename
  • os.size(filename) - returnează dimensiunea în bytes a fișierului cu numele filename

În plus, avem la dispoziție următoarele funcții pentru manipularea fișierelor:

  • open(filename, mode='r') - returnează un obiect reprezentând fișierul cu numele filename; parametrul mode specifică permisiunile pe care le avem supra fișierului: 'r'-read, 'w'-write
  • close() - închide fișierul deschis anterior
  • name() - returnează numele fișierului
  • read(size) - citește și returnează cel mult size caractere; dacă size lipsește sau are valoarea -1, se va citi tot fișierul.
  • readinto(buf, n=-1) - citește n caractere în variabila buf
  • readline(size) - citește o linie din fișier; dacă n e dat ca parametru, se citesc maxim n caractere
  • write(buf) - scrie conținutul lui buf în fișier și returnează numărul de caractere scrise.

Exemple

import os
 
#open the file for writing
f = open ("my_file", 'w')
#write text to file
count = f.write ("My awesome text")
#print how many characters were written
print ("{} characters were written to file".format(count))
f.close()
#read text from file and print it
f = open ("my_file", "r")
text = f.read()
print (text)
 
#delete the file
os.delete("my_file")
#list all remaining files
files = os.listdir()
print (files)

Exerciții

  1. Listați fișiere stocate pe Micro:bit.
  2. Creați un fișier numit student care să conțină numele vostru și grupa. Afișați noua listă de fișiere.
  3. Afișati toate fișiere de pe Micro:bit împreună cu dimensiunea acestora.
  4. Creați un program care o dată la 10 secunde stochează valoarea temperaturii într-un fișier.
  5. Creați un program care o dată la 10 secunde citește valoarea senzorului de lumină și stochează valoarea într-un fișier doar dacă aceasta difera cu cel puțin 20 de unități față de valoarea citită anterior.
  6. Creați un program care la apăsarea butonului a crează câte un fișier nou pe Micro:bit (numele fișierelor fiind de forma file_0,file_1 etc.) iar la apăsarea butonului b șterge fișierele începând cu ultimul creat. După fiecare apăasare de buton afișați noua listă de fișiere. În rezolvarea exercițiului, nu stocați lista de fișiere create, acestea vor fi citite direct de pe Micro:bit.

Operații pe fișiere în Linux

Următoarele exemple și exerciții se vor rezolva în mașina virtuală de Linux, NU pe Micro:bit.

Pentru a putea utiliza funcții de manipulare a fișierelor în Python, vom importa biblioteca os din Python.

import os

Crearea, deschiderea, închiderea si stergerea fișierelor

open

Pentru deschiderea/crearea unui fișier se folosește funcția open.

os.open(path, flags) #deschidere
os.open(path, os.O_CREAT|flags, mode) #creare      

Valori posibile pentru flags sunt:

  • os.O_RDONLY open for reading only
  • os.O_WRONLY open for writing only
  • os.O_RDWR open for reading and writing
  • os.O_NONBLOCK do not block on open or for data to become available
  • os.O_APPEND append on each write
  • os.O_CREAT create file if it does not exist
  • os.O_TRUNC truncate size to 0
  • os.O_EXCL error if * O_CREAT and the file exists
  • os.O_SHLOCK atomically obtain a shared lock
  • os.O_EXLOCK atomically obtain an exclusive lock
  • os.O_NOFOLLOW do not follow symlinks
  • os.O_SYMLINK allow open of symlinks
  • os.O_EVTONLY descriptor requested for event notifications only
  • os.O_CLOEXEC mark as close-on-exec

Valorile flags sunt reprezentate prin biti, astfel ca pot fi combinate prin operatorul | (sau pe biti).

# open a file in write only and delete all its contents (truncate to 0)
os.open(pathname, os.O_WRONLY | os.O_TRUNC, mode);

Valoarea lui mode este reprezentata de drepturile noului fisier creat (pe biti). In general, se foloseste un numar in baza 8. Acesta are trei cifre, fiecare cu trei biti.

r w x r - x r - -
1 1 1 1 0 1 1 0 0
7 5 4
  • r - read
  • w - write
  • x - execute

Fiecare cifra se refera la:

  • prima cifra - permisiunile utilizatorului care detine fisierul
  • a doua cifra - permisiunile grupului care detine fisierul
  • a treia cifra - permisiunile utilizatorulor care nu detin fisierul si nici nu fac parte din grupul care detine fisierul

close

Închiderea de fișiere se realizează cu close:

os.close(fd)

Ștergerea efectivă a unui fișier de pe disk se realizează cu funcția unlink:

os.unlink(pathname)

Toate functiile din biblioteca os arunca o exceptie daca operatia nu reuseste. Vom folosi constructia try-except pentru a trata exceptiile.

Exemplu

Dacă, spre exemplu, dorim să deschidem fișierul in.txt pentru citire și scriere, cu eventuala creare a acestuia, iar fișierul out.txt pentru scriere, cu trunchiere putem folosi următoarea secvență de cod:

import os
 
try:
    fd1 = os.open("in.txt", os.O_RDWR | os.O_CREAT, 0644)
 
    # will fail if out.txt does not exist
    fd2 = os.open("out.txt", os.O_WRONLY | os.O_TRUNC)
 
    os.close(fd1)
    os.close(fd2)
 
except Exception as e:
    print ("Error: {}".format (e))

Atenție! O greșeală frecventă este omiterea drepturilor de creare a fișierului (0644 în exemplul de mai sus) când se apelează open cu flag-ul os.O_CREAT setat.

Scrierea și citirea

read

Funcția read e folosită pentru citirea din fișier a maxim count octeți:

os.read(fd, count)

Funcția read întoarce un bytestring care contine octetii cititi, cel mult count. Când se ajunge la sfârșitul de fișier se va întoarce un bytestring gol.

write

Funcția write e folosită pentru scrierea în fișier a datelor din str:

os.write(fd, str)

Valoarea întoarsă este numărul de octeți ce au fost efectiv scriși. În mod implicit nu se garantează că la revenirea din write scrierea în fișier s-a terminat. Pentru a forța actualizarea se poate folosi fsync sau fișierul se poate deschide folosind flagul os.O_FSYNC, caz în care se garantează că după fiecare write fișierul a fost actualizat.

Observație: Pentru read/write există versiunile pread/https://docs.python.org/3/library/os.html#os.pwrite, care permit specificarea unui offset în fișier de la care să se efectueaze operația de citire/scriere.

Poziționarea în fișier (lseek)

lseek

Funcția lseek permite mutarea cursorului unui fișier la o poziție absolută sau relativă.

 os.lseek(fd, offset, whence)

Parametrul whence reprezintă poziția relativă de la care se face deplasarea:

  • os.SEEK_SET - față de poziția de început
  • os.SEEK_CUR - față de poziția curentă
  • os.SEEK_END - față de poziția de sfârșit

Observație lseek permite și poziționări după sfârșitul fișierului. Scrierile care se fac în astfel de zone nu se pierd, ceea ce se obține fiind un fișier cu goluri, o zonă care este sărită - nu este alocată pe disc.

Trunchierea fișierelor

Pe lângă trunchierea la 0 care se poate face prin apelul open cu flag-ul O_TRUNC, se poate specifica trunchierea unui fișier la o dimensiune specificată, prin apelurile de sistem ftruncate și truncate:

os.ftruncate(fd, length)        
os.truncate(path, length)

În cazul ftruncate, parametrul fd este file descriptorul obținut cu un apel open, care a asigurat drept de scriere. În cazul truncate, fișierul reprezentat prin path trebuie să aiba drept de scriere.

Exemplu utilizare operații I/O

import os
 
# Print the last 100 bytes from a file
 
try:
    # open file 
    fd = os.open("file.txt", os.O_RDONLY)
 
    # set file pointer at 100 characters _before_ the end of the file
    rc = os.lseek(fd, -100, os.SEEK_END)
 
    # read the last 100 characthers 
    msg = os.read(fd, 100)
    bytes_read = len(msg)
 
    print ("the last " + bytes_read + " bytes")
    print (buf.decode("utf-8"))
 
    # close file
    os.close(fd)
except Exception as e:
    print ("Error: {}".format (e))

Redirectări

În Linux redirectările se realizează cu ajutorul funcțiilor de duplicare a descriptorilor de fișiere dup și dup2 (observați diferența dintre cele 2 în link-urile anterioare):

os.dup(fd)
os.dup2(fd1, fd2, inheritable=True)

De exemplu, pentru redirectarea ieșirii în fișierul output.txt, sunt necesare două linii de cod:

try:
    fd = os.open("output.txt", os.O_RDWR|os.O_CREAT|os.O_TRUNC, 0600)
    os.dup2(fd, 1)
except Exception as e:
    print ("Error: {}".format (e))

Exerciții

În rezolvarea laboratorului folosiți repository-ul de github. Pentru a descarca repository-ul, rulati comanda git clone https://github.com/UPB-FILS-SdE2/sde in terminal.

Exercitiul 2 - read-write (3p)

Deschideti fisierul read-write.py.

2a. Scrieti-va numele (1p)

Puneti numele vostru in variablila name. Afisati variabila pe ecran folosind doar functia write. Urmariti liniile cu TODO 1.

2b. Cititi-va numele (1p)

Cititi de la tastatura variabila name folosind doar functia read.

Afisati noul nume pe ecran folosind functia write.

Urmariti liniile cu TODO 2.

2c. Redirectarea iesirii standard (1p)

Scrieti-va numele intr-un fisier numit output.txt folosind functia print.

Redirectati iesirea standard catre fisier.

La crearea unui fisier nou, trebuie sa ii setati modul (al treilea parametru al functiei open). Modul cel mai des folosit este 0644.

Urmariti linii cu TODO 3.

Exercițiul 3 - mcat (3p)

Deschideți fișierul mcat.py.

3a. Similitudine cat (1p)

Completați fișierul astfel încât programul rezultat mcat să aibă funcționalitate similară cu a utilitarului cat (urmăriți comentariile cu TODO 1)

Programul mcat va primi ca argument în linia de comandă numele unui fișier al cărui conținut îl va afișa la ieșirea standard. Nu aveți voie să citiți tot fișierul în memorie. Puteți citi doar bucăți de dimensiune maximum BUFSIZE.

Verificați codul de eroare întors de apelurile de sistem. Revedeți secțiunile Crearea, deschiderea și închiderea fișierelor și Scrierea și citirea fișierelor.

Testați cu o comandă de genul:

python3 mcat.py Makefile 

3b. Similitudine cp (1p)

Extindeți funcționalitatea astfel încât output-ul să fie redirectat într-un fișier primit ca al doilea argument - funcționalitate similară cu a utilitarului cp. (urmăriți comentariile cu TODO 2)

Revedeți secțiunea de redirectări.

Testați funcționalitatea:

python3 mcat.py Makefile out ; python3 mcat.py out 

4. Ierarhie de fișiere

Creați un program python care afișează ierarhia de fișiere și directoare pornind de la directorul curent într-un mod similar comenzii tree. Pentru a diferenția fișiere de directoare, în fața directoarelor se va pune semnul +, iar în fața fișierelor se va pune semnul -.

Exemplu:

-main.py
+dir
--file
-+dir2
----file
-Makefile

Soluții

Resurse utile

  1. Low level I/O (info libc “Low-Level I/O”)
  2. Duplicating descriptors (info libc “Duplicating Descriptors”)
  3. Low level I/O (Advanced Linux Programming)
1) fopen (ANSI C), open, creat (POSIX), CreateFile (Win32 API)
2) fclose (ANSI C), close (POSIX), CloseHandle (Win32 API)
3) fread (ANSI C), read (POSIX), ReadFile (Win32 API)
4) fwrite (ISO C), write (POSIX), WriteFile (Win32 API)
5) fseek (ANSI C), lseek (POSIX), SetFilePointer (Win32 API)
6) fcntl (POSIX), SetFileAttributes (Win32 API)
sde2/laboratoare/04_microbit_ro.txt · Last modified: 2021/04/06 20:45 by ioana_maria.culic
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