Wirtualny system plików
VFS - Virtual FileSystem Switch
Marcin Szuppe
1. Cechy VFS
- Jest to warstwa programowa w jądrze Linuksa, obsługująca wszystkie wywołania systemowe dotyczące standardowego systemu plików,
- Analogiczne operacje na plikach a różnorodność rozwiazań w różnych systemach operacyjnych (np. ext2, NFS, FAT, proc i in.),
- Jednolity interfejs - wysoka kompatybilność, niezależność od formatu danych (łącze między jądrem a systemem plików),
- Ukrywanie różnic dla użytkownika (systemu operacyjnego): ogólny interfejs <> właściwe operacje,
- Własne uniwersalne struktury danych i własne implementacje standardowych funkcji,
- Montowanie systemu plików (dostarczenie implementacji właściwych funkcji),
- Hierarchiczna struktura systemu plików (drzewo, wmontowane systemy są poddrzewami
głównego systemu plików),
- Obsługa różnych typów plików (np. plik zwykły, katalog, urządzenie blokowe, urządzenie znakowe, FIFO, symlink, gniazdo),
- Wspólny model pliku,
- Dla użytkownika plik jako strumień bajtów.
1.1. Typy systemów plików, które obsługuje VFS:
- dyskowe systemy plików: zarządzanie przestrzenią dostępną na lokalnych partycjach dysków np. Ext2, VFAT, NTFS
- sieciowe systemy plików: dostęp swobodny do plików, które znajdują się na systemach plików należących do innych komputerów w sieci np. NFS, Coda, AFS, NCP,
- wirtualne systemy plików: nie korzystają z przestrzeni dyskowej np. system plików /proc, /dev/pts.
Katalogi Linuksa (jak w Uniksie) tworzą drzewo, którego korzeniem jest katalog /. Główny katalog znajduje się na głównym systemie plików, który w Linuksie jest zazwyczaj typu Ext2. Wszystkie inne systemy plików mogą być zamontowane w podkatalogach głównego systemu plików.
1.2. Wspólny model pliku
- Umożliwienie reprezentacji wszystkich dostępnych systemów plików,
- Brak przyporządkowania na stałe obsługi operacji np. read(),
- Model jakby obiektowy - obiekty zrealizowane za pomocą struktur,
- Składa się z obiektów typu:
- superblok - informacje o zamontowanym systemie plików,
- i-węzeł - ogólne informacje o danym pliku; ma przyporządkowany jednoznaczny numer, który identyfikuje dany plik w systemie plików,
- plik - informacje o powiązaniu między otwartymi plikami a procesem; informacja ta istnieje w jądrze gdy proces korzysta z pliku,
- pozycja katalogu - informacje o powiązaniu pozycji katalogu z odpowiednim plikiem;
- Zwiększenie wydajności - ostatnio używane obiekty pozycji katalogu są umieszczane w dyskowej pamięci podręcznej (pamięć podręczna katalogu, ang. dentry cache).
Opis: dwa procesy używają tego samego sztywnego dowiązania; każdy swojego własnego obiektu file; wymagane 2 obiekty pozycji katalogu
 
Opis struktury:
- Każdy zarejestrowany system plików jest reprezentowany przez egzemplarz tej struktury,
- Struktury file_system są powiązane w jądrze w listę jednokierunkową, której początek wskazuje zmienna file_systems,
- Zawiera m.in. pola takie jak: nazwa systemu plików, wskaźnik do metody odczytującej superblok (funkcja read_super()), wskaźnik na następny element listy.
Struktura taka jest przyporządkowana każdemu istniejącemu w systemie operacyjnym
systemowi plików.
Lista tych struktur jest przechowywana w jądrze sytemu Linux.
2.1. Rejestrowanie systemu plików - funkcja register_filesystem()
int register_filesystem(struct file_system_type *fs);
- Zarejestrowanie systemu plików w sytemie operacyjnym,
- Dodanie struktury fs do listy zarejestrowanych systemów plików,
- Początek wskazywany przez static struct file_system_type *file_systems.
2.2 Montowanie systemu plików - funkcja mount()
- Po rejestracji systemu plików w jądrze należy go zamontować za pomocą tejże funkcji,
- W pamięci zostaje utworzony i-węzeł korzenia montowanego systemu,
- Każdy zamontowany system plików jest reprezentowany w jądrze przez strukturę super_block.
3. Obiekty superbloków: struktura super_block
- Przechowuje informacje o zamontowanym systemie plików,
- Zawiera m.in pola: identyfikator urządzenia, rozmiar bloku, wskaźnik na typ systemu plików, operacje na sobie (wskaźnik na strukturę super_operations), wskaźnik na listę zmodyfikowanych (tzw. brudnych) i-węzłów...,
- Podczas montowania inicjowana jest poprzez wywołanie funkcji read_super dla danego systemu,
- Wszystkie obiekty superbloków (po jednym na każdy system plików) są powiązane w jądrze w dwukierunkową listę cykliczną, na której pierwszy i ostatni element wskazuje zmienna super_blocks (pola next i prev) (typu struct list_head).
Inne pola zawierają informacje o zamontowanym systemie plików. (czas ostatniej modyfikacji, struktury synchronizujące dostęp ...).
3.1. Operacje na superblokach: struktura
super_operations
- Zawiera dostępne operacje, które są wykonywane na elementach typu super_block - superblokach.
Do operacji tych należą m.in.:
- uaktualnianie i-węzła,
- zwalnianie i-węzła,
- uwalnianie obiektu superbloku,
- aktualizowanie superbloku,
- ponowne montowanie systemu;
- Zawarte w niej funkcje (funkcje, na które wskazują odpowiednie pola) są wykorzystywane przez VFS,
- Przejście między specyficzną dla danego systemu reprezentacją i-węzła i superbloku a postacią w pamięci,
- Komunikacja ze sterownikami,
- Jeśli dla jakiegoś systemu plików dana funkcja nie jest zaimplementowana, to odpowiedni wskaźnik pokazuje na NULL.
4. Obiekty i-węzłów: struktura inode
- Znajduj ąsię w niej wszystkie informacje potrzebne systemowi do obsąłżenia pliku,
- Są dwa rodzaje i-węzłów: te na dysku i w pamięci. W pamięci są wzbogacone o informacje, które przyspieszają działanie,
- I-węzeł dyskowy może mieć w pamięci tylko jedną swoją kopię,
- Każdy obiekt i-węzła znajduje się na jednej z trzech cyklicznych list dwukierunkowych:
- lista i-węzłów nie używanych: na pierwszy i ostatni element tej listy wskazują pola next i prev zmiennej inode_unused,
- lista używanych i-węzłów: wskaźniki do pierwszego i ostatniego w zmiennej inode_in_use,
- lista "brudnych" i-węzłów: wskaźniki w polu s_dirty odpowiedniego obiektu superbloku;
- Liczbę i-węzłów, które są aktualnie w pamięci podaje zmienna nr_inodes, zaś i-węzłów wolnych zmienna nr_free_inodes,
- Zawsze jest pewna liczba wolnych (czystych) i-węzłów w pamięci,
- Aby zwiększyć efektywność dostępu, "używane" i "brudne" i-węzły przechowuje się także w tablicy mieszającej inode_hashtable (rozmiar NR_HASH), w której kolizje są rozwiązywane za pomocą dwukierunkowych list cyklicznych (pole i_hash).
Wybrane pola struktury:
- numer urządzenia, na którym znajduje się plik,
- id właściciela,
- id grupy właściciela,
- numer i-węzła na dysku,
- prawa dostępu i typ pliku (0 oznacza wolny i-węzeł),
- liczba dowiązań twardych, tzn. ile różnych nazw tego pliku,
- rozmiar w bajtach,
- ile bloków na dysku zajmuje plik,
- rozmiar bloku,
- czasy utworzenia, modyfikacji i dostępu,
- licznik odwołań do i-węzła (0 = wolny),
- flaga, czy i-węzeł w pamięci różni się od dyskowego (tzn. czy "brudny"): bit i_dirty,
- wskaźnik na strukturę super_block,
- wskaźnik na strukturę z operacjami na sobie inode_operations i na obiekcie pliku file_operations,
- wskaźnik do i-węzła korzenia zamontowaego systemu plików,
- struktury do synchronizacji dostępu,
- charakterystyczne informacje dla danego systemu plików,
- wkaźniki do innych i-węzłów w pamięci.
4.1. Metody związane z i-węzłem: struktura
inode_operations
- Struktura ta zawiera także wskaźnik do operacji na obiekcie pliku,
- Wybrane metody to:
- tworzenie nowego i-węzła dla zwykłego pliku/katalogu/pliku specjalnego,
- przeszukiwanie katalogu w celu znalezienia i-węzła,
- tworzenie i usuwanie sztywnego dowiązania,
- usuwanie podkatalogu,
- przenoszenie pliku;
- metody są dostępne dla wszystkich możliwych i-węzłów i typów systemów plików, lecz niektóre z nich nie mają zastosowania w przypadku konkretnego i-węzła - wówczas ustawiany jest NULL,
- są to przeważnie operacje związane ze zmianą struktury fizycznego pliku.
 
5. Obiekty plików: struktura file
- Reprezentacja pliku z perspektywy procesu - użytkownika,
- Umożliwienie współbieżności operacji na pliku, realizacja dostępu dzielonego,
- Nie ma swojego odpowiednika na dysku tak jak i-węzeł, więc nie ma pola "dirty",
- Główną informacją jest wskaźnik pliku - tzw. aktualna pozycja w pliku,
- Każdy obiekt tego typu jest na jednej z dwukierunkowych cyklicznych list:
- Pole f_count mówi ile jest odwołań do danej struktury tzw. licznik otwarć,
- lista nie używanych obiektów plików: Lista ta wykorzystwana jest jako pamięć podręczna obiektów plików, jak i rezerwa dla administratora (superużytkownika - root'a). Jądro dba o to, by lista ta zawierała co najmniej NR_RESERVED_FILES == 10 elementów. Adres pierwszego elementu w zmiennej free_filps,
- lista obiektów używanych: Każdy element tej listy jest używany przez co najmniej jeden proces. Adres pierwszego elementu tej listy w zmiennej inuse_filps;
W elementach tej listy pole f_count>0;
- Rozmiar listy nie używanych obiektów plików jest przechowywany w zmiennej nr_free_files,
- Gdy VFS chce zaalokować nowy obiekt pliku, to najpierw patrzy na listę nie używanych obiektów (jeśli jest ich więcej niż NR_RESERVED_FILES, to używa jednego elementu, wpp. alokuje w zwykły sposób).
- Proces otwierający plik otrzymuje jego deskryptor,
- Tablica indeksowana deskryptorami zawiera wskaźniki do struktur file (o rozmiarze NR_OPEN == 1024*1024),
- Z jednym plikiem może być związanych kilka struktur file,
- Wskaźniki do tej samej struktury - wiele procesów (fork),
- Licznik otwarć f_count ustawiany na 1, a przy kopiowaniu deskryptora zwiększany o 1, zamykaniu pliku zmniejszany o 1. Gdy licznik otwarć == 0, to struktura jest zwalniana.
5.1. Zestaw operacji na pliku: struktura file_operations - operacje plikowe
- Wskaźniki do funkcji pozwalających operować na obiekcie pliku,
- Charakterystyczne dla danego systemu plików,
- Operacje jak np. czytanie pliku, nie zmieniające fizycznie pliku,
- Podczas ładowania i-węzła przez jądro z dysku do pamięci, zachowuje wskaźnik do tych operacji w strukturze file_operations, której adres znajduje się w polu default_file_ops struktury inode_operations,
- Wybrane funkcje:
- czytanie bajtów z pliku,
- otwieranie pliku (utworzenie nowego obiektu pliku i połączenie go z odpowiednim obiektem i-węzłem),
- uwalnianie obiektu pliku,
- blokowanie pliku;
- Wszystkie metody są dostępne dla wszystkiech możliwych systemów plików, jednak nie zawsze mają zastosowanie - wtedy jest NULL.
6. Specjalna obsługa obiektów plików katalogów
- Szczególna ostrożność; katalogów nie można blokować tak jak plików (blokowanie całego poddrzewa o korzeniu wyznaczanym przez dany katalog,
- Rozwiązanie poprzez pole f_version obiektu pliku i pole i_version obiektu i-węzła oraz zmnienną global_event,
- przy każdej modyfikacji obiektu i-węzła pliku katalogu, wartość global_event jest zwiększana o 1, a nowa wartość znacznika zachowywana w polu i_version obiektu. Analogicznie dla obiektu pliku,
- Oba znaczniki się muszą zgadzać - sprawdzane przy wywołaniach systemowych (tam gdzie potrzeba) ,
- Gdy wykryta zostanie niespójność, to ponownie zostaje wyliczony wskaźnik pliku katalogu poprzez przeczytanie całej zawartości katalogu; zwraca się wtedy pozycję katalogu następującą tuż za ostatnią zwróconą pozycją. Następnie f_version ustawiane na i_version w celu zaznaczenia synchronizacji.
6.1 Obiekty pozycji katalogów i jej metody: struktura
dentry i dentry_operations
- Katalog uważany przez VFS jako zwykły plik, zawierający listę plików i innych katalogów,
- Obiekt tego typu tworzony jest przez jądro dla każdego składnika ścieżki poszukiwanej przez proces; obiekt pozycji wiąże dany składnik z i-węzłem,
- Struktura ta nie ma pola "dirty" jako, że nie ma odpowiednika na dysku,
- Obiekty są przechowywane w pamięci podręcznej dentry_cache (alokator pąytowy - tworzenie i usuwanie poprzez kmem_cache_alloc() i kmem_cache_free()),
- Cztery stany: wolny, nie używany, w użyciu, ujemny,
- Pamięć podręczna pozycji katalogów (bez "w użyciu") - odczytywanie z dysku elementu pozycji i utworzenie odpowiedniego obiektu pozycji katalogu zajmuje dużo czasu, więc przetrzymuje się w pamięci obiekty pozycji katalogów, które nie są aktualnie potrzebne, ale mogą się przydać w przyszłości (dodatkowo realizacja przez tablicę haszującą dentry_hashtable),
- "Nie używane" pozycje katalogów znajdują się na cyklicznej liście dwukierunkowej LRU, posortowanej względem czasu wstawiania. Ostatnio uwolniony na początek listy; gdy system operacyjny zmniejsza pamięć podręcznš, to kasowane są te końcowe; pola next i prev zmiennej dentry_unused wskazują na poczatek i koniec tej listy,
- "Aktualnie używane" na dwukierunkowej liście, określonej przez pole i_dentry odpowiedniego obiektu i-węzła,
- Obiekt "w użyciu" staje się "ujemny", gdy zostanie skasowane ostatnie sztywne łącze odpowiedniego pliku; wtedy przenoszony jest obiekt na listę LRU nie używanych pozycji; za każdym razem gdy jądro zmniejsza pamięć podręczną, ujemne pozycje katalogów są przesuwane w kierunku końca listy LRU i kolejno zwalniane.
Uwaga:
Jeśli nie zaznaczono inaczej, to wszystkie definicje znajdują się
w pliku /include/linux/fs.h.