8. SYSTEMY PLIKÓW
Poza pamięcią operacyjną (pamięcią główną) komputery są wyposażone w
pamięć zewnętrzną
(pamięć pomocniczą), która jest realizowana w oparciu o nośnik magnetyczny lub
optyczny.
Programy aktualnie wykonywane (oraz ich dane i stosy), a przynajmniej aktualnie
wykorzystywane
fragmenty muszą przebywać w pamięci operacyjnej, gdyż tylko stamtąd procesor
może bezpośrednio
pobierać rozkazy do wykonywania i tylko tam może bezpośrednio odczytywać lub
zapisywać dane
(mówimy, że pamięć operacyjna jest pamięcią o dostępie bezpośrednim).
Do pamięci zewnętrznej procesor ma dostęp za pośrednictwem układu
sterującego danym urządzeniem
(kontrolera), któremu może podawać rozkazy zapisu lub odczytu całych bloków
danych (zatem nie
jest możliwe zapisywanie lub odczytywanie pojedynczych bajtów bez ponoszenia
kosztów związanych
z transmisją całego bloku). Dostęp do informacji zapisanej w pamięci zewnętrznej
jest wielokrotnie
wolniejszy, niż do informacji zapisanej w pamięci operacyjnej.
W związku z blokowym charakterem transmisji i zapisu danych, w pamięci
zewnętrznej, podobnie
jak w pamięci operacyjnej, mamy do czynienia ze zjawiskiem fragmentacji.
Przyczyny stosowania pamięci zewnętrznych:
1) ze względów technologicznych mogą mieć dużo większą pojemność, niż
aktualnie produkowane
układy pamięci operacyjnej;
2) są dużo tańsze od pamięci operacyjnej (w tym sensie, że jeden bajt pamięci
zewnętrznej kosztuje
dużo mniej, niż jeden bajt pamięci operacyjnej);
3) pamięć zewnętrzna jest pamięcią trwałą - w przypadku wyłączenia zasilania
cała zawartość
pamięci operacyjnej zostaje utracona, a zawartość pamięci zewnętrznej nie.
Pamięci zewnętrzne są produkowane w postaci dysków stałych (zwanych
powszechnie twardymi
dyskami
(hard disc)
) oraz dysków wymiennych (dyskietek, płytek CD).
Rozgraniczenie pomiędzy
dyskami stałymi i wymiennymi jest dość zatarte, gdyż twarde dyski mogą być
umieszczane w obudo-
wach umożliwiających ich łatwą wymianę i przenoszenie do innego komputera.
Pamięci służące do
okresowego zapisywania bardzo dużych ilości danych ze względów
bezpieczeństwa
(backup)
mają
często postać taśmy magnetycznej. W komputerach często jest instalowanych
kilka różnych
urządzeń pamięci zewnętrznej.
Pomijając przypadek przestrzeni wymiany omówiony na poprzednim wykładzie,
zapis w pamięci
zewnętrznej odbywa się w logicznie wyodrębnionych jednostkach o zmiennej
wielkości, zwanych
plikami
(file)
. Pliki są obiektami logicznymi, które są „najbardziej widoczne” dla
użytkowników
systemów komputerowych - większość użytkowników patrzy na pracę komputera
przez pryzmat
operacji wykonywanych na pojedynczych plikach, bądź też ich zbiorach.
Ze względu na dużą liczbę plików przechowywanych we współczesnych
komputerach ich zbiorowi
jest zwykle narzucana pewna struktura - pliki są pogrupowane w katalogi
(directory)
zwane również
folderami. W zależności od systemu operacyjnego cała pamięć zewnętrzna, jaką
dysponuje komputer
(być może zorganizowana w postaci kilku oddzielnych urządzeń fizycznych)
może być postrzegana
przez użytkownika jako jedna duża, wspólna przestrzeń służąca do
przechowywania plików, bądź też
może być podzielona na kilka urządzeń logicznych (partycji)
(partition)
, przy
czym podział na
urządzenia logiczne wcale nie musi odzwierciedlać podziału na urządzenia
fizyczne.
W poszczególnych partycjach przechowywane są odrębne struktury
katalogów. Jedna z partycji
zwykle przeznaczana jest na przestrzeń wymiany (do której dostęp jest
zorganizowany za pomocą
innych procedur, niż dostęp do plików, i która nie jest bezpośrednio widoczna dla
użytkownika).
Z punktu widzenia interfejsu użytkownika plik jest najmniejszą jednostką
pamięci zewnętrznej, na
której użytkownik może wykonywać operacje. W plikach mogą być
przechowywane programy
wykonywalne (lub ich fragmenty), lub dane. Formalnie, każdy plik może być
postrzegany jako zbiór
danych przeznaczonych do wykorzystania przez konkretny program (lub grupę
programów).
Program wykonywalny (binarny) jest przeznaczony do pobrania i załadowania
do pamięci przez
program ładujący (loader), plik zawierający program źródłowy
(nieskompilowany) stanowi dane dla
przetwarzającego go kompilatora, plik zawierający zapis tekstu lub obrazu
stanowi dane dla edytora
tekstowego lub graficznego (lub programu odtwarzającego) itd.
Zapis informacji w plikach musi mieć ściśle określoną strukturę (format),
znaną programowi, który
będzie tę informację wykorzystywał. Szczególnymi przypadkami plików są:
- plik wykonywalny
(executable file)
, przeznaczony do załadowania do pamięci
operacyjnej
i interpretacji jako ciągu rozkazów dla procesora;
- plik tekstowy, zawierający ciąg bajtów traktowany jako ciąg znaków ASCII, w
tym znaczników
końca linii (występujących nie rzadziej, niż określona liczba pozycji) i znacznik
końca pliku.
Niektóre programy mogą traktować pliki jako „bezpostaciowe”, czyli ciągi
bajtów o zupełnie
dowolnej strukturze.
Z każdym plikiem związany jest pewien zbiór jego atrybutów (zwany też czasem
metryką pliku).
Atrybuty są podstawowymi informacjami na temat własności pliku i sposobów
jego użytkowania.
Różne systemy plików stosują różne atrybuty, ale przeważnie są to takie
informacje, jak:
- nazwa pliku;
- typ pliku;
- umiejscowienie w fizycznej pamięci zewnetrznej;
- rozmiar pliku;
- prawa dostępu (czy i kto może zapisywać, odczytywać, wykonywać ...);
- identyfikator właściciela pliku;
- data i czas utworzenia pliku, ostatniej modyfikacji i ostatniego dostępu do
pliku.
Informacje na temat właściciela dotyczą tylko plików w wielodostępnych
systemach operacyjnych.
W takim przypadku odmienne prawa dostępu mogą być wyznaczone dla
właściciela pliku, odmienne
dla grupy jego współpracowników, a jeszcze inne dla pozostałych użytkowników
systemu. Prawa
dostępu nadaje i zmienia właściciel pliku.
Plik jako obiekt logiczny wywodzi się z abstrakcji taśmy magnetycznej, a w
związku z tym zbiór
typowych funkcji systemowych operujących na plikach odzwierciedla w pewnym
stopniu fizyczne
operacje związane z obsługą taśmy. Plik należy zatem wyobrażać sobie jako ciąg
lokat o jednakowej
wielkości, zakończony znacznikiem końca pliku. Wzdłuż pliku posuwa się
wskaźnik bieżącego
położenia w pliku (wskaźnik pliku, odpowiadający fizycznej głowicy zapisująco-
odczytującej).
eof
(end of
file)
Tradycyjną metodą dostępu do danych w pliku jest dostęp sekwencyjny, czyli
zapisywanie lub
odczytywanie danych w pliku w kolejności zgodnej z ich umiejscowieniem (od
początku do końca
pliku). Od chwili, gdy zaczęto implementować pliki w pamięci dyskowej, pojawiła
się możliwość
„przeskakiwania” wskaźnika pliku z jednego miejsca w drugie - ten tryb dostępu,
podobnie jak
w przypadku pamięci operacyjnej, nazwano dostępem swobodnym.
Typowe czynności wykonywane przez funkcje systemowe operujące na plikach
to:
- utworzenie pliku (początkowo pustego, czyli wyznaczenie mu jedynie miejsca
w pamięci
fizycznej i zapisanie atrybutów);
- otwarcie pliku (wpisanie informacji do systemowej tablicy plików otwartych,
przydzielenie bufora
w pamięci operacyjnej, ustawienie wskaźnika na początku lub na końcu pliku);
- pisanie do pliku (zapis ciągu bajtów poczynając od bieżącej pozycji w pliku,
ustawienie wskaźnika
na kolejnej pozycji po ostatniej zapisanej);
- odczyt z pliku (odczyt ciągu bajtów poczynając od bieżącej pozycji w pliku,
ustawienie wskaźnika
na kolejnej pozycji po ostatniej odczytanej);
- zmiana położenia bieżącego w pliku (tylko przeniesienie wskaźnika w inne
miejsce);
- zmiana atrybutów pliku (wywoływanie tej funkcji zwykle podlega pewnym
ograniczeniom);
- zamknięcie pliku (przepisanie zawartości bufora do pamięci zewnętrznej i
usunięcie wpisu
w tablicy plików otwartych);
- usunięcie pliku (zwolnienie miejsca zajmowanego przez plik w pamięci
zewnętrznej i usunięcie
metryki pliku).
W systemach umożliwiających współbieżną pracę wielu użytkowników,
prawidłowe zorganizowanie
ich dostępu do plików jest rzeczą o wiele trudniejszą, niż w systemach
jednozadaniowych. Ponieważ
system plików jest wspólny dla wszystkich użytkowników, poszczególne pliki
mogą być używane
przez wiele procesów jednocześnie. Wiąże się to z koniecznością
zaprojektowania funkcji systemo-
wych tak, aby zapewnić logiczny i spójny obraz danych w pliku wszystkim
procesom. W szczegól-
ności implementacja powinna uwzględniać:
- możliwość niezależnego otwierania i zamykania pliku przez każdy proces.
Każdy plik powinien
posiadać licznik otwarć, który zwiększa się o 1 przy każdym otwarciu przez
proces, a zmniejsza
o 1 przy każdym zamknięciu. Plik może być fizycznie usunięty tylko wtedy, gdy
jego licznik
otwarć zawiera zero;
- możliwość niezależnego czytania i pisania przez każdy proces. Każdy proces
powinien posiadać
swój własny wskaźnik pliku i móc go przemieszczać niezależnie od innych
procesów. Operacje
czytania i pisania powinny być zaimplementowane jako operacje niepodzielne,
aby zapewnić
logiczną spójność danych. Skutek każdego dokonanego przez jeden proces
zapisu w pliku powinien
być natychmiast widoczny dla wszystkich innych procesów czytających ten
plik.
W niektórych zastosowaniach (na przykład w implementacjach wielodostępnych
baz danych) procesy
mogą potrzebować wykonywania bardziej złożonych operacji na współdzielonych
plikach, niż tylko
takich, które mogą być zrealizowane przez pojedyncze wywołania funkcji
systemowych (w teorii baz
danych takie operacje nazywane są transakcjami). Aby zapewnić niepodzielność
wykonywania
transakcji, powinny być zaimplementowane systemowe mechanizmy blokowania
(locking)
całych
plików, bądź poszczególnych rekordów zapisanych w danym pliku.
Ponieważ operacje zapisu i odczytu do / z pliku przez procesy są w rzeczywistości
zaimplementowane
jako operacje na buforach plików w pamięci, do implementacji blokowania mogą
być użyte
odpowiednie mechanizmy ochrony fragmentów pamięci (omówione na
poprzednich wykładach).
Uwaga
Same pliki mogą też służyć jako specyficzne mechanizmy koordynacji procesów.
Jednym
z najstarszych i najdłużej wykorzystywanych prostych mechanizmów jest tak
zwany plik zamkowy
(lockfile)
. Plik zamkowy jest pustym plikiem, czasowo tworzonym w wybranym i
znanym procesom
katalogu. Działa on jak prosty semafor - fakt jego istnienia informuje procesy o
tym, że dany zasób
jest właśnie zajęty przez pewien proces, zaś jeśli go nie ma, oznacza to, że zasób
jest wolny.
Pierwotnie pliki tworzyły w pamięci zewnętrznej niezorganizowany zbiór. Gdy
liczby plików
w systemach stały się bardzo duże, pojawiła się potrzeba narzucenia ich zbiorowi
pewnej struktury,
aby łatwiej było wyszukiwać poszczególne pliki i wykonywać na nich operacje.
Tak powstały
katalogi, będące w istocie specjalnym rodzajem plików, zawierających informacje
o innych plikach
i przeznaczonych do operowania na nich za pomocą innych poleceń
systemowych, niż na zwykłych
plikach.
Z punktu widzenia użytkownika, katalogi zawierają wykazy nazw plików wraz ze
związanymi z nimi
informacjami (wielkość, położenie w pamięci zewnętrznej itp.) - w rzeczywistości
informacje te są
zazwyczaj przechowywane w innym miejscu, a katalogi zawierają jedynie adresy
tych miejsc.
Początkowo katalogi były jednopoziomowe (czyli stanowiły uporzadkowaną listę
wszystkich plików),
potem pojawiły się dwupoziomowe (na przykład: poziom pierwszy - identyfikatory
uzytkowników,
poziom drugi - pliki pogrupowane w zbiory należące do poszczególnych
użytkowników). Obecnie
prawie wyłącznie spotyka się wielopoziomowe struktury katalogów
zorganizowane w postaci
drzewa lub grafu acyklicznego.
Przykładowa organizacja drzewa katalogów:
GŁÓWNY
SYSTEM BINARNE TEKSTOWE UŻYTKOWNICY
STEROWNIKI ....... abc.bin xx.bin ... info.txt instr.txt .... ALA JAN
OLA
ZADANIA
PROGRAMY
zad1.txt zad2.txt ...
pierwszy.pas .....
Katalog najwyższego poziomu (główny katalog systemu plików na danym
urządzeniu logicznym)
nazywany jest korzeniem drzewa katalogów. Może zawierać zarówno katalogi
niższego poziomu
(podkatalogi), jak i pliki. Podobnie katalogi niższego poziomu również mogą
zawierać zarówno
podkatalogi, jak i pliki, itd.
W każdej chwili sesji pracy użytkownika z systemem operacyjnym użytkownik ma
przyporządkowany
katalog bieżący
(current directory)
, do którego zawartości przez domniemanie
odnoszą się polecenia
użytkownika. Użytkownik może (wydając odpowiednie polecenie) zmienić swój
katalog bieżący na
dowolny inny (w granicach posiadanych praw dostępu).
Nazwy plików i podkatalogów w obrębie jednego katalogu muszą być unikalne,
natomiast w różnych
katalogach mogą występować takie same nazwy. Użytkownik, chcąc wydać
polecenie dotyczące
obiektu umieszczonego w innym katalogu, niż jego katalog bieżący, musi nazwę
tego obiektu
poprzedzić ścieżką dostępu. Ścieżki dostępu dzielą się na bezwzględne
(podające położenie danego
obiektu względem korzenia drzewa katalogów) i względne (podające położenie
obiektu względem
bieżącego katalogu użytkownika wydającego polecenie).
Przykład
Przyjmując strukturę drzewa katalogów z poprzedniego slajdu i zakładając, że
katalogiem bieżącym
jest ALA, możemy stwierdzić, że bezwzględna ścieżka dostępu do pliku zad1. txt
składa się z ciągu
katalogów GŁÓWNY, UŻYTKOWNICY, JAN, ZADANIA , zaś bezwzględna -
ALA, UŻYTKOWNICY, JAN, ZADANIA.
GŁÓWNY
SYSTEM BINARNE TEKSTOWE
UŻYTKOWNICY
STEROWNIKI ....... abc.bin xx.bin ... info.txt instr.txt .... ALA JAN
OLA
ZADANIA
PROGRAMY
zad1.txt
zad2.txt ... pierwszy.pas .....
- katalog bieżący
- bezwzględna ścieżka dostępu
- względna ścieżka dostępu
Uwaga
Nazwa pliku poprzedzona bezwzględną ścieżką dostępu (tak zwana pełna nazwa
ścieżkowa pliku)
stanowi jednoznaczny identyfikator pliku na danym urządzeniu logicznym.
Graf acykliczny stanowi uogólnienie drzewa. Na ogół, podobnie jak drzewo,
posiada jeden
wyróżniony wierzchołek (korzeń), ale od korzenia do innych węzłów drzewa
może prowadzić już
więcej, niż jedna ścieżka.
katalog
plik
W tego rodzaju strukturze jeden i ten sam plik może figurować w więcej, niż
jednym katalogu.
Podobnie, pewien katalog może mieć więcej nadkatalogów, niż tylko jeden.
Struktura grafu
acyklicznego jest bardziej elastyczna, niż struktura drzewa, lecz jednocześnie
jej implementacja
stwarza wiele istotnych problemów:
- usunięcie lub przemieszczenie pliku lub katalogu wymaga konsekwentnego
usunięcia lub zmiany
wszystkich dowiązań;
- algorytmy przeszukiwania całej takiej struktury są bardziej skomplikowane,
niż w przypadku
drzewa;
- nieumiejętne tworzenie dowiązań do katalogów może spowodować powstanie
cyklu (pętli)
w grafie, co może wiązać się nawet z koniecznością reinstalacji całego
systemu plików (dlatego
w systemie Unix prawo tworzenia dowiązań do katalogów ma jedynie
administrator systemu).
W Uniksie poza tak zwanymi dowiązaniami twardymi istnieją też dowiązania
miękkie, których
tworzenie nie powoduje zmian w rzeczywistej strukturze katalogów, i które nie
stwarzają wyżej
opisanego zagrożenia.
Systemy plików mogą być implementowane w pamięci zewnętrznej na różne
sposoby. Logiczny
obraz dysku jest ciągiem bloków o jednakowej wielkości, które są ponumerowane
kolejno, czyli
adresy ich tworzą liniową przestrzeń adresową (adresy liniowe na dysku są
przetwarzane na
kilkuskładnikowe adresy fizyczne, określające numer powierzchni itd.).
Najprostszym sposobem przydziału miejsca na plik jest przydział ciągły, czyli
przydział odpowiedniej
liczby bloków o kolejnych adresach logicznych. Taka implementacja wiąże się z
niedogodnościami
podobnymi do tych, jakie związane są z niestronicowaną pamięcią operacyjną -
fragmentacja
zewnętrzna i konieczność ewidencjonowania dziur i stosowania algorytmów
wyboru.
Wady tej nie ma przydział listowy miejsca na dysku, gdzie ciąg przydzielonych
bloków stanowi
listę jednostronnie wiązaną (czyli strukturę, gdzie na końcu każdego bloku
przechowywana jest
informacja o położeniu następnego bloku lub znacznik końca listy). Implementacja
taka ma inną wadę -
praktycznie wyklucza dostęp swobodny do plików (a dostęp sekwencyjny też nieco
spowalnia, wskutek
możliwości chaotycznego rozrzucenia bloków jednego pliku po całym dysku).
Przydział indeksowy polega na skupieniu całej informacji o położeniu kolejnych
bloków należących
do pliku w specjalnym bloku indeksowym. Jest to rozwiązanie analogiczne do
stronicowania pamięci
operacyjnej (przy czym blok indeksowy pełni rolę tablicy stron). Pozwala ono
uniknąć fragmentacji
zewnętrznej i ułatwia dostęp swobodny, ale również wiąże się z pewnymi
problemami implementa-
cyjnymi:
- krótkie pliki (a takich jest zwykle w systemie bardzo dużo) marnują dużo
miejsca w swoich blokach
indeksowych;
- jeśli plik jest bardzo długi, pojedynczy blok indeksowy może być dla niego
niewystarczający,
w takim przypadku należy utworzyć listę wiązaną złożoną z kilku bloków
indeksowych (co od razu
spowalnia dostęp).
W praktyce w systemach plików często są stosowane mieszane metody
implementacji.