W systemie Linux dostęp do plików odbywa się za pośrednictwem warstwy VFS (Virtual Filesystem Switch albo Virtual File System). Stanowi ona interfejs pomiędzy funkcjami udostępnianymi przez implementację konkretnego systemu plików a funkcjami systemowymi służącymi do operacji na plikach udostępnianymi przez jądro. Służy także jako podręczna pamięć dla i-węzłów, co przyspiesza dostęp do często używanych katalogów oraz plików.Niezależnie od systemu plików wszystkie pliki (również katalogi, pliki specjalne) na poziomie VFS są reprezentowane przez strukturę i-węzła struct inode. Nie należy jej mylić ze strukturami o nazwach fs_inode gdzie fs oznacza konkretny system plików (np. ext2_inode, ext_inode, minix_inode) , będącymi niskopoziomowymi (dyskowymi) reprezentacjami plików zależnymi od konkretnej implementacji. W wielu systemach plików to pojęcie nie występuje. Chcąc uzyskać dostęp do pliku, należy (pośrednio, poprzez wywołania funkcji systemowych) wywołać funkcję iget(). Pobiera ona jako argumenty: wskaźnik do superbloku dyskowego oraz numer pliku w systemie plików (np. numer i-węzła dyskowego) pozwalające odnaleźć lub umieścić i-węzeł w odpowiednim miejscu tablicy mieszającej zawierającej aktualnie używane i-węzły. Trzeci argument informuje, czy ten i-węzeł jest punktem montowania innego systemu plików. Jako wynik iget() zwraca wskaźnik do i-węzła; w przypadku punktów montowania jest to i-węzeł, do którego wskaźnik znajduje się w i-węźle odnalezionym za pomocą dwu pierwszych argumentów czyli wskaźnik do i-węzła montowanego.
Najpierw , na podstawie argumentów, znajdujemy listę w tablicy mieszającej, na której powinien znajdować się szukany i-węzeł. Następnie iterujemy tę listę w poszukiwaniu i-węzła, którego superblok i numer są takie , jak argumenty funkcji.
Definicja funkcji __iget()znajduje
się w pliku fs/inode.c
.
struct inode *__iget(struct super_block * sb, int nr, int crossmntp) { static struct wait_queue * update_wait = NULL; struct inode_hash_entry * h; struct inode * inode; struct inode * empty = NULL; if (!sb) panic("VFS: iget with sb==NULL");
h = hash(sb->s_dev, nr); /* Tu dostajemy listę w tablicy */ /* mieszającej, na której może */ /* znajdować się szukany i-węzeł. */ repeat: /* Szukamy i-węzła na liście ... */ for (inode = h->inode; inode ; inode = inode->i_hash_next) if (inode->i_dev == sb->s_dev && inode->i_ino == nr) goto found_it; /* Jeśli znaleziono właściwy ... */ if (!empty) { /* Jeśli nie znaleziono i-węzła, */ /* to należy utworzyć nowy */ /* i wypełnić go treścią . */ /* * If we sleep here before we have found an inode * we need to make sure nobody does anything bad * to the inode while we sleep, because otherwise * we may return an inode that is not valid any * more when we wake up.. */ h->updating++; /* Zakładamy blokadę na listę */ empty = get_empty_inode(); /* i pobieramy nowy i-węzeł. */ /* To może potrwać dłuższy czas . */ if (!--h->updating) /* Jeśli jakieś procesy żądały */ wake_up(&update_wait); /* dostępu do listy, na którą */ /* założyliśmy blokadę, to je */ /* budzimy, bo zostały uśpione. */ if (empty) /* Sprawdzamy, czy w międzyczasie */ goto repeat; /* szukany i-węzeł nie pojawił */ /* się na naszej liście. */ return (NULL); /* W przypadku braku pamięci na */ /* utworzenie nowego i-węzła */ /* get_empty_inode() zwraca NULL, */ /* który propagujemy dalej.Awaria.*/ } /* Jeśli po powtórnym przeszukaniu*/ /* nie ma naszego i-węzła na */ /* liście, to kontentujemy się */ /* nowym, dostarczonym przez */ /* get_empty_inode(), wypełniając */ inode = empty; /* go odpowiednią treścią... */ inode->i_sb = sb; inode->i_dev = sb->s_dev; inode->i_ino = nr; inode->i_flags = sb->s_flags; /* Przesuwamy i-węzeł na koniec */ put_last_free(inode); /* listy dostępnych i-węzłów. */ insert_inode_hash(inode); /* Wstawiamy do tablicy mieszając.*/ read_inode(inode); /* Wczytujemy informacje z dysku. */ goto return_it; found_it: /* Tu wchodzimy,jeśli nasz i-węzeł*/ /* znajdował się na liście . */ /* * The inode may currently be being pulled down by * clear_inode(). Avoid it if so. If we get past this, then * the increment of i_count will prevent the inode's reuse. */ if (inode->i_condemned) { /* Jeśli i-węzeł jest w trakcie */ /* clear_inode(), to usypiamy się.*/ sleep_on(&inode->i_wait); /* Zostaniemy obudzeni pod koniec */ goto repeat; /* clear_inode(). Należy wtedy */ /* rozpocząć wszystko od nowa. */ } if (!inode->i_count) /* Jeśli i-węzeł nie jest przez */ nr_free_inodes--; /* nikogo używany, to zmniejszamy */ inode->i_count++; /* liczbę wolnych i zwiększamy mu */ /* licznik dowiązań. */ wait_on_inode(inode); /* Jeśli na i-węzeł jest założona */ /* blokada, to czekamy na zdjęcie.*/ /* To się nie powinno zdarzyć: */ if (inode->i_dev != sb->s_dev || inode->i_ino != nr) { printk("Whee.. inode changed from under us. Tell Linus\n"); iput(inode); /* Zwracamy i-węzeł . */ goto repeat; /* I zaczynamy od początku . */ } if (crossmntp && inode->i_mount) { /* Jeśli katalog jest punktem */ /* zamotnowania, to zastępujemy */ /* ten, do którego się montuje, */ /* tym, który jest montowany. */ struct inode * tmp = inode->i_mount; tmp->i_count++; iput(inode); /* I zwalniamy ten poprzedni. */ inode = tmp; wait_on_inode(inode); /* Czekamy na zdjęcie ew. blokady.*/ } /* Jeśli get_empty_inode() zwróci */ /* nowy i-węzeł "empty", a potem */ /* odnajdziemy w tablicy mieszając*/ if (empty) /* ten, o którego chodziło, to */ iput(empty); /* trzeba zwrócić zbędny "empty". */ return_it: /* Jeśli inne procesy założyły */ /* blokadę na naszą listę, to */ while (h->updating) /* musimy poczekać aż ją zwolnią, */ sleep_on(&update_wait); /* gdyż jest możliwe, że operują */ /* na naszym i-węźle lub sąsiadach*/ return inode; }
Autor: Bartłomiej Starosta