Do spisu treści tematu

6.4.3 Pobieranie i-węzła




Spis treści


Wstęp

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.


Algorytm

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.


Funkcja __iget()

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;
}

Funkcje pomocnicze

Bibliografia
  1. Pliki źródłowe:  fs/inode.c
  2. Maurice Bach: Budowa systemu operacyjnego UNIX
  3. David A. Rusling: The Linux Kernel
  4. Linux Kernel Hackers' Guide

Autor: Bartłomiej Starosta

Początek