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