Do spisu treści
tematu 6
6.5.4 Czytanie z pliku - funkcja read()
Spis treści
Opis
Funkcja systemowa read() służy do czytania danych z otwartego pliku. Ma
ona postać:
int read ( unsigned
int dp, char *bufor, int licznik ), gdzie dp jest deskryptorem
otwartego pliku, bufor jest adresem struktury danych w procesie
użytkownika, do której wczytujemy dane, natomiast licznik określa
liczbę bajtów, jaką chce wczytać użytkownik. Jeżeli funkcja wykona się
pomyślnie, to jako wynik zwróci rzeczywistą liczbę wczytanych bajtów lub
0 w przypadku napotkania końca pliku. W przeciwnym przypadku zwróci wartość
-1, a zmienna errno będzie zawierała rodzaj błędu: EDEADLOCK(plik
lub rekord ma założoną blokadę), EFAULT(w przestrzeni wirtualnej nie ma
zaalokowanego bufora użytkownika ), EINVAL(nie można wywołać funkcji read
dla danego systemu plików), EBADF(niepoprawny deskryptor pliku).
Funkcja rozpoczyna czytanie od bieżącej pozycji w pliku, a po jego
zakończeniu zwiększa bieżącą pozycję w pliku o liczbę przeczytanych bajtów.
W przypadku stwierdzenia odczytu sekwencyjnego wykorzystuje ona czytanie
z wyprzedzeniem, co znacznie zwiększa wydajność systemu.
Funkcja read(), podobnie jak funkcja write(),
jest niezależna od zainstalowanego rodzaju systemu plików. Zrealizowana
jest ona na dwóch poziomach: najpierw wykonuje się część wspólną dla wszystkich
systemów plików (sprawdzenie praw dostępu oraz poprawności argumentów funkcji),
potem następuje wywołanie funkcji właściwej dla danego systemu plików.
Ta przynależna konkretnemu systemowi funkcja realizuje odczyt danych z
pliku.
Algorytm
algorytm read
wejście: deskryptor pliku
adres bufora w procesie użytkownika
liczba bajtów do wczytania
wyjście: liczba bajtów skopiowanych do bufora użytkownika
{
pobierz i-węzeł odpowiadający deskryptorowi pliku użytkownika;
sprawdź prawo do czytania dla pliku;
sprawdź możliwość wywołania funkcji read dla konkretnego systemu plików;
sprawdź, czy nie ma blokady na rekord do czytania
(funkcja locks_verivy_area);
sprawdź, czy struktura danych użytkownika jest zaalokowana w jego
przestrzeni adresowej (funkcja verify_area);
wywołaj funkcję read właściwą dla danego systemu plików (dla systemu
plików EXT2 jest to funkcja generic_file_read) i zwróć jej wynik;
}
Informacja o funkcjach, które realizują operacje na plikach dla danego
systemu plików znajdują się w strukturze file_operations.
Do struktury tej prowadzi wskaźnik z każdego pliku w tablicy plików. Na
przykład dla systemu EXT2 funkcją służącą do czytania danych z pliku jest
generic_file_read(), a dla FAT fat_file_read().
Funkcja generic_file_read()
Dla systemu EXT2 funkcją służącą do czytania z pliku jest funkcja generic_file_read().
Posiada ona następujący nagłówek:
int generic_file_read
( struct inode *inode, struct file *filp, char *bufor, int licznik ),
gdzie inode jest i-węzłem czytanego pliku, filp jest pozycją
w tablicy plików odpowiadającą danemu plikowi, bufor jest adresem
struktury danych w procesie użytkownika, do której wczytujemy dane, natomiast
licznik określa liczbę bajtów, jaką chce wczytać użytkownik. Jeżeli
funkcja wykona się pomyślnie, to jako wynik zwróci rzeczywistą liczbę wczytanych
bajtów lub 0 w przypadku napotkania końca pliku. W przeciwnym przypadku
zwróci kod błędu: -ENOMEM(błąd przy tworzeniu ramki pamięci), -EIO(błąd
wejścia-wyjścia).
Funkcja rozpoczyna czytanie od bieżącej pozycji w pliku, a po jego
zakończeniu zwiększa bieżącą pozycję w pliku o liczbę przeczytanych bajtów.
W przypadku stwierdzenia odczytu sekwencyjnego wywołuje funkcję generic_file_readahead(),
która realizuje czytanie z wyprzedzeniem. Funkcja generic_file_readahead()
korzysta m. in. z wartości dwóch pól znajdujących się w strukturze file
czytanego pliku, a mianowicie pola f_ramax, które zawiera aktualny
maksymalny rozmiar danych, jaki można przeczytać z wyprzedzeniem oraz pola
f_rawin przechowującego rozmiar danych wczytanych w trakcie ostatnio
wykonywanego czytania z wyprzedzeniem. Wartość pola f_ramax zmienia się
w trakcie działania funkcji, ale nie może być większa niż zdefiniowana
stała MAX_READAHEAD = PAGE_SIZE*18 i mniejsza niż MIN_READAHEAD = PAGE_SIZE*3,
gdzie PAGE_SIZE jest rozmiarem ramki w pamięci operacyjnej.
algorytm generic_file_read
wejście: i-węzeł czytanego pliku
pozycja w tablicy plików odpowiadająca danemu plikowi
adres bufora w procesie użytkownika
liczba bajtów do wczytania
wyjście: liczba bajtów skopiowanych do bufora użytkownika
{
sprawdź, czy kontynuujemy czytanie sekwencyjne pliku i
stosownie ustaw zmienne pomocnicze (f_ramax i f_rawin);
while (true)
{
znajdź ramkę pamięci zawierającą dane z pliku w pamięci podręcznej;
if (znaleziona ramka)
przejdź do etykiety znaleziono_ramkę;
else
przejdź do etykiety nie_znaleziono_ramki;
znaleziono_ramkę:
if (aktualna ramka jest pełna)
czytaj z wyprzedzeniem (funkcja generic_file_readahead);
poczekaj na ramkę (aż skończą się operacje wejścia-wyjścia)
if (ramka nie jest gotowa)
przejdź do etykiety ramka_błąd;
else
przejdź do etykiety sukces;
sukces:
skopiuj dane z ramki do bufora użytkownika;
zwolnij ramkę (funkcja release_page);
zwiększ pozycję w pliku, pozycję w buforze, ilość przeczytanych bajtów
oraz zmniejsz ilość bajtów do przeczytania o ilość bajtów przeczytanych
z ramki;
if (nie osiągnięto wskazania licznika)
{
if (potrzebne jest przeszeregowanie procesów)
wywołanie funkcji szeregującej procesy (funkcja schedule());
idź na początek pętli while;
}
else
wyskocz z pętli while;
nie_znaleziono_ramki:
if (nie mamy dodatkowej ramki przeczytanej z wyprzedzeniem)
{
stwórz nową ramkę (_get_free_page);
idź na początek pętli while;
}
else
dodaj ramkę do kolejki haszującej ramek (add_to_page_cache);
zapisz ramkę danymi z dysku (funkcja readpage);
przejdź do etykiety znaleziono_ramkę;
ramka_błąd:
zgłoś żądanie zapisu ramki (funkcja readpage);
if (żądanie zostało zrealizowane pomyślnie)
{
poczekaj na ramkę (być może jest w trakcie zapełniania);
przejdź do etykiety sukces;
}
zwolnij ramkę (funkcja release_page);
wyskocz z pętli while;
}
zaktualizuj bieżącą pozycję w pliku;
zwolnij dodatkową ramkę (free_page);
wywołaj funkcję UPDATE_ATIME(inode) w celu zaktualizowania czasu
ostatniego dostępu do i-węzła;
return (ilość wczytanych bajtów);
}
Uwagi
-
Funkcja systemowa read() nie może opóźniać wczytywania danych, tak
jak to robi funkcja write() w przypadku zapisu.
Jeśli dane nie znajdują się w wewnętrznym buforze jądra, to proces musi
zaczekać na pobranie ich z dysku.
-
W przypadku plików specjalnych oraz łączy komunikacyjnych powrót z funkcji
read() jest natychmiastowy, jeśli plik był otworzony z flagą O_NDELAY
i nie ma danych do przesłania.
-
Jeśli system nie jest zbyt przeciążony i dane mogą przez pewien czas pozostawać
w buforze, to w przypadku czytania sekwencyjnego, zastosowane wczytywanie
z wyprzedzeniem jest skuteczne i w znaczny sposób poprawia wydajność systemu.
-
Implementacja funkcji read()
w Linux 2.0.32 jest taka sama jak w wersji 2.0.30. Natomiast trochę zmodyfikowana
została funkcja generic_file_read().
A mianowicie dodano wywołanie funkcji szeregującej procesy schedule(),
zapewne w celu zoptymalizowania pracy systemu. Dodano także wywołanie funkcji
UPDATE_ATIME(inode) do aktualizacji atrybutów i-węzła.
Bibliografia
-
Bach M. J. "Budowa systemu operacyjnego Unix"
-
Pliki źródłowe Linuxa: mm/filemap.c,
fs/read_write.c
Autor: Piotr Niedolistek