• Nie Znaleziono Wyników

6. SYSTEMY PLIKÓW

6.3. Operacje plikowe

System operacyjny udostępnia użytkownikom szereg operacji na plikach, w postaci wywołań funkcji systemowych. Niezależnie od tego jakie operacje na plikach będą wykonywane, system operacyjny musi wykonywać uniwersalne operacje związane z opisem plików:

 określenie charakterystyki pliku na podstawie jego nazwy symbolicznej,

 określenie praw użytkownika do wykonania określonej operacji (np. odczyt, zapis, usunięcie itp.) na podstawie opisu pliku,

 zwolnienie pamięci operacyjnej po charakterystyce pliku.

Ponadto każda operacja plikowa zawiera szereg działań, jak np. odczyt oraz zapis określonego zbioru klastrów, usunięcie pliku itp.

6.3.1. Otwarcie pliku

W systemie UNIX wywołanie systemowe open wykorzystuje dwa parametry: nazwę pliku wraz ze ścieżką dostępu oraz tryb otwarcia pliku. Tryb otwarcia wskazuje systemowi operacyjnemu jakie operacje zamierzamy wykonywać na pliku aż do momentu jego zamknięcia (np. wyłącznie operacje odczytu, wyłącznie zapisu czy odczytu i zapisu). Podczas otwarcia pliku system operacyjny wyznacza przede wszystkim numer węzła identyfikacyjnego pliku na podstawie ścieżki dostępu do pliku, a następnie odczytuje I-noda z dysku i kopiuje go do pamięci operacyjnej. Informacje zawarte w węźle identyfikacyjnym pliku zostają następnie przez system operacyjny uzupełnione o następujące pola:

 stan I-noda w pamięci, określający: o czy plik jest zablokowany,

o czy jakiś proces oczekuje na zdjęcie blokady z pliku, o czy kopia I-noda w pamięci jest identyczna z oryginałem na

dysku,

o czy zawartość pliku w pamięci różni się od oryginału na dysku,

 logiczny numer urządzenia dyskowego zawierającego plik,

 numer I-noda (w tablicy I-nodów na dysku numer I-noda nie jest zapisany, gdyż jest określony przez pozycję w tej tablicy),

 licznik odwołań do I-noda.

Jeden plik w danej chwili może być jednocześnie wykorzystywany przez wiele procesów. Mimo to system operacyjny przechowuje w pamięci jedną kopię I-noda, określaną mianem V-noda. Podczas kolejnego otwarcia pliku system operacyjny sprawdza, czy w pamięci jest umieszczony I-nod tego pliku, a jeżeli tak, to licznik odwołań do I-noda jest zwiększany o 1. Analogicznie, podczas zamknięcia pliku licznik odwołań do I-noda zostaje zmniejszony o 1, a jeżeli przyjmuje wartość 0, to bufor przechowujący I-noda zostaje zwolniony.

Poza I-nodem przechowującym ogólne informacje o pliku, system operacyjny tworzy strukturę zawierającą informacje niezbędne dla konkretnego procesu korzystającego z danego pliku. Struktura ta (typu file) jest przechowywana w pamięci operacyjnej w tablicy otwartych plików. Podczas otwarcia pliku przez jeden z procesów system operacyjny sprawdza prawa dostępu procesu do pliku i jeżeli operacja ta przebiegła pomyślnie, to w tablicy

otwartych plików tworzona jest nowa pozycja, zawierająca strukturę typu file. Struktura file zawiera następujące pola:

 tryb otwarcia pliku (wyłącznie do odczytu, do odczytu i zapisu itp.),

 wskaźnik na strukturę I-noda,

 bieżącą pozycję w pliku (offset) dla operacji odczytu/zapisu,

 licznik odwołań do I-noda,

 wskaźnik na strukturę zawierającą prawa procesu otwierającego plik (struktura ta znajduje się w deskryptorze procesu),

 wskaźnik na poprzednią i następną strukturę file (2-kierunkowa lista struktur file).

Zmienna offset w strukturze file pozwala na zapamiętanie bieżącego położenia w pliku. Podczas otwarcia pliku zmienna ta zawiera początkową lub końcową (w zależności od trybu otwarcia) pozycję w pliku. Operacje odczytu oraz zapisu danych powodują zmianę bieżącej pozycji w pliku. Program użytkowy może zmieniać aktualne położenie w pliku przy pomocy funkcji systemowej lseek.

Przy każdym otwarciu jakiegokolwiek pliku system operacyjny tworzy nową strukturę file, umieszcza ją w tablicy otwartych plików, a w tablicy deskryptorów procesu umieszcza wskaźnik na tę strukturę (rys. 6.23). Gdy proces otwiera dany plik wielokrotnie, dla każdego otwarcia system operacyjny tworzy nową strukturę file. Proces potomny, który dziedziczy kontekst procesu macierzystego (a w tym także tablicę deskryptorów), uzyskuje możliwość wykonywania operacji na plikach otwartych w procesie macierzystym.

6.3.2. Wymiana danych z plikiem

Do wymiany danych z otwartym dyskiem w systemie operacyjnym UNIX przewidziano funkcje read oraz write. Funkcja systemowa read wykorzystuje trzy parametry:

Read(deskr, bufor, l_bajtów);

Parametr deskr jest liczbą całkowitą określającą numer pozycji w tablicy deskryptorów procesu, przydzielonej danemu plikowi. Drugim parametrem jest wskaźnik na tablicę znaków, w której zostanie umieszczony odczytany fragment pliku. Liczbę odczytywanych znaków określa się przy pomocy trzeciego parametru l_bajtów. Funkcja read zwraca rzeczywistą liczbę odczytanych bajtów (liczba ta może się różnić od parametru l_bajtów) lub wartość -1 oznaczającą błąd odczytu. Odczyt danych z pliku rozpoczyna się od aktualnej pozycji wskaźnika w pliku (offset) przechowywanej w strukturze file. Po wykonaniu operacji odczytu offset zostaje zwiększony o liczbę przeczytanych bajtów.

Funkcja write wykonuje zapis do pliku określonego przez zmienną deskr, liczbę bajtów l_bajtów z bufora określonego przez wskaźnik bufor:

write(deskr, bufor, l_bajtów);

Tablica otwartych plików 0 1 2 19 Tablica otwartych plików 0 1 2 19 Proces 1 Proces 2 Struct File OFFSET Read Struct File OFFSET Write

Struct File OFFSET Read&Write Struct File OFFSET Read Struct File OFFSET Write Systemowa tablica otwartych plików V-node I-node Tablica I-nodów Kopie I-nodów w pamięci

Rys. 6.23. Związki procesów z otwartymi plikami

Funkcja zwraca liczbę rzeczywiście zapisanych bajtów lub kod błędu -1. Obydwie operacje służące do wymiany danych z plikiem są operacjami synchronicznymi, co oznacza, że podczas realizacji operacji odczytu/zapisu proces zostaje zablokowany aż do jej zakończenia.

6.3.3. Blokady plików

Blokady plików oraz poszczególnych rekordów w plikach służą do synchronizacji procesów korzystających ze wspólnych plików. Wielozadaniowe systemy operacyjne z zasady obsługują specjalne wywołanie systemowe pozwalające programiście na ustawianie i sprawdzanie blokad na plikach oraz ich częściach. W systemie UNIX służy do tego funkcja fcntl. Parametrami tej funkcji są: deskryptor pliku, typ operacji (ustawienie lub sprawdzenie blokady,

blokowanie dostępu dla odczytu lub zapisu), a także obszar blokowania – offset oraz liczba bajtów.

Podczas wywołania funkcji fcntl w celu sprawdzenia blokad pliku, funkcja natychmiast zwraca do procesu informację o nałożonych blokadach. Natomiast nałożenie blokady może odbywać się dwojako:

 z przejściem procesu w stan oczekiwania, w przypadku gdy nałożenie blokady jest niemożliwe (tzw. synchroniczne wywołanie systemowe),

 z natychmiastowym powrotem do procesu wywołującego oraz zwróceniem kodu błędu (tzw. asynchroniczne wywołanie systemowe). Blokada pliku do zapisu nie może być zrealizowana w przypadku, gdy inny proces wcześniej ustawił blokadę do zapisu na tym pliku. Oznacza to, że blokada pliku do zapisu jest blokadą wyłączną. Blokady pliku do odczytu nie są wyłączne, co oznacza, że wiele procesów jednocześnie może wykonywać operacje odczytu z danego pliku. Jeżeli natomiast na jakąkolwiek część pliku nałożono blokadę odczytu, to nie można na tę część pliku nałożyć blokady zapisu.

W systemie UNIX stosowane są dwa tryby funkcjonowania blokad:

 konsultatywny (ang. advisory), w którym system operacyjny nie zajmuje się blokowaniem operacji na pliku, a jedynie ustawia wskaźniki blokady w strukturze file. Współbieżne procesy wykorzystujące wspólny plik muszą w takim przypadku sprawdzać występowanie blokad nałożonych na plik,

 obowiązujący (ang. mandatory), w którym system operacyjny blokuje dostęp do pliku, na który zostały nałożone blokady przez inny proces. Podstawowym trybem pracy jest tryb konsultatywny, gdyż w niewielkim stopniu obciąża system operacyjny.