Deși nu sunt specificate de standardul C, multe sisteme de operare oferă conceptul de descriptor de fișier (uneori abreviat ca fd (file descriptor)). În timp ce tipul FILE din stdio.h și funcțiile sale asociate încapsulează detaliile de nivel scăzut ale unui flux, un descriptor de fișier este un număr întreg care se referă la un flux pe care sistemul de operare îl urmărește.
Această secțiune va explora descriptorii de fișiere așa cum sunt implementați în sistemele POSIX, cum ar fi Linux.
Fluxuri standard ca descriptori de fișiere
Când un proces este creat, sistemul de operare alocă, printre alte resurse, trei fluxuri pentru un proces: fluxurile standard stdin, stdout și stderr. De obicei, fluxurile standard sunt interacționate folosind definițiile lor bazate pe FILE în stdio.h, așa cum a fost descris într-o secțiune anterioară. Aceste fluxuri pot fi, de asemenea, interacționate prin descriptorii lor de fișier brut, care sunt aceiași pentru fiecare proces:
unistd.h simbol | flux | Descriptor de fișier |
STDIN_FILENO | stdin | 0 |
STDOUT_FILENO | stdout | 1 |
STDERR_FILENO | stderr | 2 |
Observați că acești descriptori de fișiere sunt aceiași pentru fiecare proces, chiar dacă fluxurile standard conțin date diferite pentru fiecare proces. Aceasta înseamnă că descriptorii de fișiere nu sunt neapărat unici la nivelul întregului sistem; fiecare proces poate avea o vizualizare diferită a descriptorilor de fișiere mapați la ce fluxuri, la fel cum fiecare proces are o vedere diferită a spațiului de adrese virtuale al sistemului.
Citirea și scrierea de bază
Citirea și scrierea dintr-un descriptor de fișier poate fi efectuată folosind următoarele funcții[1]:
#include <unistd.h> ssiize_t read(int fd, void *buf, size_t count); ssiize_t write(int fd, const void *buf, size_t count);
Comparați și comparați aceste definiții cu funcțiile bazate pe FILE[2]:
#include <stdio.h> char *fgets(char *s, int size, FILE *stream); int fputs(const char *s, FILE *stream);
Sunt evidente trei diferențe:
- Se presupune că datele citite și scrise în flux nu sunt șiruri de caractere.
- Descriptorii de fișiere sunt luați ca parametri în loc de FILE.
- Pentru valoarea returnată este utilizat un tip consistent.
read și fgets preiau seturi similare de parametri: ceva reprezentând fluxul, un buffer și o dimensiune; în plus, în cazul în care cantitatea de date citite este egală cu dimensiunea cerută, bufferul va avea același conținut, indiferent de funcția utilizată. Cu toate acestea, aceste funcții se comportă diferit în cazul în care cantitatea de date citite nu se potrivește cu dimensiunea cerută. fgets, fiind destinat utilizării cu șiruri, va opri citirea devreme dacă este întâlnită o nouă linie, iar funcția se poate bloca dacă așteaptă ca restul șirului să apară în flux. read, pe de altă parte, nu va opri citirea devreme dacă se întâlnește o valoare specială, dar se va opri dacă nu toate datele solicitate au fost încă scrise în pipe. Deoarece read nu poate garanta că ceva complet utilizabil a fost scris în buffer (în cazul în care se oprește citirea mai devreme), valoarea returnată conține numărul de octeți scriși în buffer. Acest lucru face ca citirea să fie mai potrivită pentru situațiile în care programatorul are nevoie de mai mult control asupra tipului de date citite sau este dispus să tranzacționeze primirea de date parțial citite pentru a reduce numărul de operațiuni de blocare I/O.
În mod similar, write necesită un parametru de dimensiune explicit, deoarece nu poate presupune că este scris un șir terminat cu NULL și va returna numărul de octeți scriși, astfel încât programul să poată determina dacă datele transmise au fost complet scrise în flux.
Note
[1] read(2) and write(2), Linux Programmer’s Manual, 2019-10-10
[2] fgets(3) and fputs(3), Linux Programmer’s Manual, 2020-08-13
(Include texte traduse și adaptate din Wikibooks de Nicolae Sfetcu)
Lasă un răspuns