Algorytm iget jest częścią wirtualnego systemu plików VFS.
Przypomnę podstawowe pojęcia związane z VFS:
- i-węzeł - to struktura związana z plikiem,
- superblok (nazywany również blokiem specjalnym) - to struktura
związana z systemem plików,
- listy i-węzłów - każda struktura i-węzła w pamięci znajduje się na
jednej z trzech list w VFS: na liście i-węzłów używanych, brudnych
(tj. zmienionych w pamięci, ale nie na dysku) albo nieużywanych.
- tablica haszowana (nazywana również kolejką mieszającą) -
ta struktura danych pełni w VFS rolę słownika umożliwiającego
szybkie znalezenie i-węzła, znając tylko jego numer i adres superbloku;
jest to (stałej długości) tablica list i-węzłów.
Opis algorytmu iget:
W jądrze 2.4.7 funkcją pobierającą i-węzeł z pamięci jest iget4().
struct inode *iget4(struct super_block *sb, unsigned long ino, find_inode_t find_actor, void *opaque)
Przyjmowane parametry to kolejno: adres struktury superbloku, numer i-węzła,
dwa opcjonalne parametry przekazywane dalej - do funkcji find_inode.
Zwraca ona wskaźnik do struktury i-węzła w pamięci albo - w przypadku
niepowodzenia - NULL.
Funkcja iget4() najpierw sprawdza, czy szukany i-węzeł jest już w pamięci.
W tym celu oblicza funkcją hash(sb,ino) indeks w tablicy haszującej,
spod którego odczytuje do zmiennej head głowę listy, na której może znajdować
się szukany i-węzeł.
Teraz zakładany jest spin-lock (funkcja spin_lock()), który gwarantuje,
że następujący po nim fragment kodu będzie wykonywany jednocześnie
najwyżej przez jeden procesor.
Wywoływana jest funkcja find_inode, która ma za zadanie znaleźć
i-węzeł na liście head. Jeśli zwróci ona NULL, po zwolnieniu spin-locka
(funkcja spin_unlock()) wywoływana jest funkcja get_new_inode(),
mająca za zadanie wczytanie i-węzła z dysku.
Natomiast jeśli funkcja find_inode zwróci i-węzeł, wywoływana jest funkcja
__iget(), uaktualniająca pola struktury inode, następnie zwalniany jest
spin-lock i proces zawiesza się w kolejce funkcją wait_on_inode().
Po wyjściu z tej funkcji iget4 zwraca znaleziony i-węzeł.
Funkcja find_inode() szuka i-węzła na liście cyklicznej.
static struct inode *find_inode(struct super_block *sb, unsigned long ino, struct list_head *head, find_inode_t find_actor, void *opaque)
Aby ustalić, czy kolejny element na liście to szukany i-węzeł,
sprawdza po kolei: czy zgadzają się numery, czy zgadzają się superbloki
oraz ewentualnie wywołujemy funkcję find_actor (z parametrem opaque)
charakterystyczną dla danego systemu plików (zaimplementowana jedynie
w systemach plików nfs i coda).
Funkcja get_new_inode() wczytuje i-węzeł z dysku (używając funkcji
specyficznych dla danego systemu plików).
static struct inode *get_new_inode(struct super_block *sb, unsigned long ino, struct list_head *head, find_inode_t find_actor, void *opaque)
Najpierw funkcja alokuje pamięć dla struktury inode (i-węzła) funkcją
alloc_inode(). Jeśli alokacja nie powiodła się, funkcja zwraca NULL.
Zakładany jest spin-lock. Ponieważ spin-lock był przez jakiś czas zwolniony,
ponawiana jest próba znalezienia węzła w pamięci funkcją find_inode.
Jeśli się udała, to zwalniany jest spin-lock, zwalniany jest inode,
do zmiennej inode przypisywany jest znaleziony i-węzeł, proces
zawiesza się w funkcji wait_on_inode; po wyjściu z niej funkcja zwraca inode.
Jeśli w pamięci nie ma węzła, trzeba wczytać go z dysku. Wykonywane
są kolejno: zwiększenie licznika i-węzłów (inodes_stat.nr_inodes),
dodanie węzła do listy i-węzłów (inodes_in_use), dodanie go do odpowiedniej
listy w tablicy haszowanej, inicjalizacja pól struktury -
i_sb, i_dev, i_ino, i_flags, i_count, i_state.
Następnie zwalniany jest spin-lock, wywoływane clean_inode(inode).
Z superbloku wołana jest funkcja wczytująca i-węzeł z dysku - read_inode
(dla reiserfs - read_inode2). Czyszczony jest bit I_LOCK we fladze i_state
oraz budzone są procesy czekające na dany węzeł (kolejka inode->i_wait).
Funkcja __iget() uaktualnia pola struktury i-węzła, która jest pobierana
z pamięci.
static inline void __iget(struct inode *inode)
Funkcja atomowo (tzn. z gwarancją, że tylko jeden procesor wykonuje
jednocześnie daną operację) odczytuje licznik odwołań do struktury
i-węzła (pole i_count). Jeśli jest ono większe od zera, zwiększa je
(atomowo) i wychodzi z funkcji. W przeciwnym wypadku atomowo zwiększa
i_count, a następnie, jeśli i-węzeł nie jest brudny ani zablokowany,
zostaje usunięty ze swojej listy i dodany do listy i-węzłów używanych.
Na końcu zmniejszany jest licznik węzłów nieużywanych (inode_stat.nr_unused).