Do spisu treści tematu 6
6.4.1 Opis ogólny modułu
Spis treści
Po
pierwsze nazewnictwo...
Wprowadzenie
Plik jest pojęciem w pewnym sensie abstrakcyjnym. Dla
użytkownika powinien być widoczny jedynie interfejs, dostarczony przez
system plików, który zgodnie z ideą pliku - zbioru informacji, nie zmusza
biednego użytkownika do szukania wiadomości gdzie na dysku leży, powiedzmy
piętnasty rekord jego danych.
System, dla zaimplementowania takiej idei pliku, wykorzystuje i-węzły
- są one zbiorem informacji o cechach pliku: prawach dostępu, rozmiarze,
jego położeniem na dysku itd.
System przechowuje i-węzły, a także informacje o
nich w odpowiednich strukturach pamięci.
Te, związane z konkretnym i-węzłem dane (m.in. dowiązania do list i-węzłów;
gdzie, na którym urządzeniu się znajduje; itd) zapisane są w odpowiedniej
strukturze, nazwanej przeze mnie i-ramką (patrz: uwaga
na temat nazewnictwa ).
Moduł, którym się zajmuję odpowiada za obsługę wprowadzonych
do obsługi i-węzłów struktur danych oraz za dostarczenie procedurom wyższego
poziomu (jak open, close) możliwości korzystania z nich.
Po co ten cały ambaras
I-węzły trzymane są na dysku, więc po co wprowadzać
cały moduł zajmujący się ich wczytywaniem do pamięci i póżniejszą obsługą
? Powodów jest kilka, spróbuję je omówić poczynając od tych najbardziej
ogólnych.
Po pierwsze muszę zadać fałsz tezie wypowiedzianej w
pytaniu na początku poprzedniego akapitu - nieprawdą jest jakoby każdy
system plików miał dokładnie odpowiadające sobie struktury będące i-węzłami.
Prawda - każdy musi utrzymywać jakieś informacje o plikach i to w z grubsza
podobnej formie, ale oczywiście zapis tych informacji na urządzeniu fizycznym
jest zdecydowanie różny.
I tutaj dochodzimy od strony i-węzłów do modelu
zwanego wirtualnym systemem plików (VFS - Virtual File System). Model
ten można traktować jako logiczny system zarządzania danymi na dysku,
całkowicie niezależny od używanego systemu plików.
Tak jak VFS chce zapewnić nam interfejs obsługi
dowolnego systemu plików, tak i-ramka , która
zresztą jest jego częścią ma umożliwić uniwersalne gromadzenie informacji
o pliku, niezależnie od tego czy korzystamy z partycji DOS, czy też EXT2.
Oprócz informacji, wspólnych dla wszystkich systemów
istnieją także takie, które zależą ściśle od konkretnej platformy - co
więcej - nie możemy mówić tylko o przechowywaniu informacji dla jakiegoś
systemu, albowiem w jakiś sposób trzeba je także odczytać z dysku, czy
też - po zmodyfikowaniu - zapisać na niego od nowa. Jasne jest jednak,
że nie może istnieć wspólna funkcja do odczytu np. rozmiaru pliku dla systemów,
które zapisują tę właśnie wielkość w całkowicie różnych miejscach na fizycznym
urządzeniu!
I tutaj przychodzi nam na pomoc i-ramka:
w niej to przechowywane są wskaźniki do tych, interesujących nas w tym
momencie funkcji, oczywiście zależne od systemu, na którym znajduje się
nasz plik.
Podsumowując - i-ramka ma
umożliwić nam uniwersalny, niezależny od systemu plików dostęp do danych
zgromadzonych na dyskach, czy też innych nośnikach.
powód drugi: wykluczanie
Każdy z omawianych powodów, aż prosi się o podanie odniesienia
do coraz to innej dziedziny wiedzy. Tym razem trzeba wspomnieć o współbieżnym
wykonywaniu operacji. Załóżmy, że mamy dwa procesy: A i B, które chcą jednocześnie
(z dokładnością do przydziału czasu procesora) coś na naszym pliku zapisać,
co wiąże się jednocześnie między innymi ze zmianą czasu ostatniej modyfikacji
pliku, trzymanej oczywiście w i-węźle - pojawia się dobrze znany problem
wprowadzenia sekcji krytycznej. Otóż mając i-ramkę
mamy równocześnie semafor, który opuszczamy sobie w odpowiednim momencie
i .... już po sprawie.
Oczywiście wszystkie struktury danych
pozwalają na występowanie tylko jednej kopii i-węzła w pamięci. Dlatego,
chociaż mamy kilka struktur przechowujących i-węzły, wszystkie one operują
jedynie na wskaźnikach.
powód trzeci: efektywność
Spójrzmy na numer omawianej wersji Linuxa - 2.0.32 -
czyż nie budzi respektu? Ale spróbujmy wgłębić się w istotę tej liczby
- przypuszczalnie była wersja 31 (beta), jakaś dwudziestka, kilka
nastek... to o czymś świadczy - o tym mianowicie, że każdy kawałek został
poddany wnikliwej analizie, między innymi pod względem efektywności. Tak
zapewne stało się i w tej sytuacji. Kiedy szanowny czytelnik przejdzie
do opisu np funkcji iget , dowie się, że
kiedy załadujemy już jakiś i-węzeł do i-ramki w pamięci - nie usuniemy
go prędko - dopiero, kiedy nie będzie już żadnych innych wolnych i-ramek
do wykorzystania. Ta swego rodzaju inercja pozwala w większości sytuacji
na zaoszczędzenie czasu ładowania i-węzła z dysku do i-ramki
w pamięci.
Główna idea i założenia
struktury danych:
Istnieją dwie podstawowe struktury danych, przeznaczone
do wspomagania operowania na i-ramkach w pamięci operacyjnej:
-
dwukierunkowa Lista (będę ją tak od tej pory nazywał) przechowująca wszystkie
i-ramki,
-
tablica haszująca list dwukierunkowych zawierających i-ramki.
Ad. 1.
Na Liście umiejscowione są wszystkie dostępne, czyli utworzone
wcześniej i-ramki.
Ad. 2.
Każdy i-węzeł identyfikowany jest poprzez swój numer oraz numer urządzenia
na którym się znajduje. Tablica haszująca umożliwia szybki dostęp do i-ramki
zawierającej i-węzeł poprzez obliczenie funkcji haszującej dla i-węzła
i wyszukanie odpowiadającej mu i-ramki na liście w tablicy. Jest więc ona
jedynie nakładką na Listę, przyspieszającą dostęp do i-ramek.
W oryginalnym komentarzu na początku pliku fs/inode.c zamieszczono
ostrzeżenie, żeby uważać przy wykorzystywaniu tablicy haszującej
w związku z równoczesnym jej używaniem przez różne procesy.
Jak wynika z wcześniejszego opisu, i-ramka dla danego
i-węzła występuje w pamięci operacyjnej dokładnie raz. Dlatego zarówno
tablica haszująca, jak i Lista przechowują jedynie wskaźniki na rozpatrywane
obiekty.
Wszystkie dołączenia do list (tych w tablicy haszującej,
a także tej, zawierającej wszsystkie i-ramki) przechowywane są w i-ramkach.
Dla ściślejszego zorientowania się jak powyższe struktury
są wykorzystywane polecam zapoznanie się z opisami dotyczącymi algorytmów
obsługi pobierania i-ramki (iget ) oraz jej zwalniania
(iput ).
stałe:
NR_INODE : maksymalna ilość i-ramek możliwa do utworzenia w systemie; ustawiona
w include/linux/fs.h na 3072
NR_IHASH : rozmiar tablicy haszującej; ustawiona w fs/inode.c na 512
Uwagi
Istnieje pewien problem dotyczący nazewnictwa. Mianowicie
używane jest jedno słowo na określenie dwóch różnych rzeczy: i-węzeł(i-node)
może być zarówno na dysku, jak i w pamięci. Ta rzecz określana jest w kodzie
systemowym Linuxa poprzez: inode (dla i-węzła pamięciowego) oraz ext2_inode
(dla i-węzła na dysku) - gdzie ext2 jest tylko przykladem systemu.
Dlatego wzorem ramki dla strony w stronicowaniu
pamięci chciałbym wprowadzić i-ramkę dla i-węzła.
Dlaczego tak ? Tak jak ramka zawiera stronę, tak i-ramka zawiera
i-węzeł, oczywiście i-ramka zawiera jeszcze więcej informacji oprócz samego
i-węzła, ale to nie przeszkadza w intuicyjnym rozumieniu tego słowa.
Bibliografia
1. Pliki źródłowe:
- linux/include/fs.h
- linux/fs/inode.c
- linux/fs/ext2/inode.c
2. Projekt
Linux
3. Linux
Kernel Hackers Guide
Autor: Grzegorz Gawron