Do spisu tresci tematu 5

5.3.2 Pobieranie i zwalnianie i-wezla




Spis tresci


Pobieranie i-wezla


Aby wykonac operacje na pliku, proces musi pobrac jego i-wezel. Odbywa sie to w ten sposob, ze wyszukiwana jest w pamieci kopia szukanego i-wezla. Jezeli nie zostanie ona odnaleziona, pobierany jest i-wezel z listy wolnych, przy czym wgrywa sie do niego dane z dysku. Procesy nie wykonuja operacji na i-wezlach bezposrednio na dysku. Za pobranie kopii i-wezla z pamieci odpowiedzialny jest algorytm iget znajdujacy sie w pliku inode.c.

Funkcja __iget()

- sluzy do pobierania kopii i-wezla z pamieci
DEFINICJA: struct inode* __iget(struct super_block *sb, int nr,
                                int crossmntp)
    WYNIK: kopia z pamieci i-wezla o numerze nr z urzadzenia
           o super bloku sb w przypadku sukcesu,
           wpp. NULL (tylko gdy nie da sie pobrac i-wezla z listy wolnych)
I-wezel identyfikowany jest przez numer urzadzenia, do ktorego nalezy (podane jako super blok) oraz przez swoj numer w tablicy i-wezlow tego urzadzenia. Stad argumenty wywolania funkcji __iget jednoznacznie wskazuja i-wezel, ktorego kopie proces chce uzyskac. W przypadku, gdy istnieja powody, dla ktorych proces musi czekac na ten i-wezel - jest usypiany.

Implementacja funkcji:

{
    static struct wait_queue * update_wait = NULL; /* STATIC !!! */
    struct inode_hash_entry * h; /* lista w tablicy mieszajacej */
    struct inode * inode; /* zwracany i-wezel */
    struct inode * empty = NULL; /* pobrany z listy wolnych */

    Znajdz odpowiednia liste mieszajaca h;
repeat:
    Szukaj w h i-wezla odpowiadajacego opisowi, jezeli znajdziesz, skocz do found_it;
    Jezeli nie ma przydzielonego wolnego i-wezla (empty==NULL)
    {
       Zwieksz licznik updating w h, zeby zaznaczyc ze chcemy cos
           namieszac w kolejce mieszajacej i zeby nikt niczego nie popsul;
       Pobierz wolny i-wezel (empty=get_empty_inode());
       Zmniejsz licznik updating w h, bo juz nie bedziemy zmieniac wskaznikow,
          jezeli osiagnal on wartosc 0 (nikt juz nie miesza w kolejce) obudz wszystkie procesy z
          kolejki update_wait, zauwazmy, iz jest ona static;
       Jezeli jest ustalony wolny i-wezel (empty!=NULL) skocz do repeat
          wpp zwroc NULL;
    }
    Ustaw zwracany i-wezel na wolny (inode=empty);
    Ustaw jego parametry takie jak dev, sb, nr itp.;
    Przesun inode z kolejki wolnych przed nia (put_last_free(inode));
    Wstaw inode do kolejki mieszajacej;
    Wgraj i-wezel z dysku (read_inode).
    Skocz do return_it;
found_it:
    /* tutaj wszystkie operacje odwoluja sie do znalezionego inode */
    Jezeli liczba otwarc pliku(i_count) jest 0,  zmniejsz licznik wolnych i-wezlow
       (nr_free_inodes), bo znalezlismy w liscie wolny i-wezel;
    Zwieksz licznik otwarc(i_count++);
    Wykonaj test na blokade i-wezla(wait_on_inode(inode));
    Jezeli ustawienia sb i nr inode nie sa zgodne z szukanymi
    {
       zwolnij i-wezel(iput(inode))
       skocz do repeat;
    }
    Specjalne przetwarzanie w razie punktu zamontowania;
    Jezeli przetrzymywany wolny i-wezel zwolnij go (iput(empty)),
       bo masz inny z kolejki mieszajacej;
return_it:
    Dopoki ktos cos zmienia w kolejce mieszajacej (h->updating>0)
       zasypiaj na kolejce update_wait;
    Zwroc inode;
}
Aby wgrac z dysku i-wezel, jadro musi wyliczyc jego fizyczne polozenie na dysku (urzadzeniu logicznym). Poniewaz tablica i-wezlow jest przechowywana w pewnej ilosci blokow dyskowych, jadro musi znalezc blok, w ktorym znajduje sie i-wezel oraz jego numer w danym bloku (liczac od 0). Odbywa sie to w przyblizeniu wg nastepujacych wzorow:
   nr_bloku=((nr-1) div (i_per_block)) + nr_bloku_tablicy_iwezlow
   nr_iwezla=(nr-1) mod (i_per_block))
Wystarczy teraz pomozyc nr_iwezla przez rozmiar struktury ext2_inode, aby uzyskac numer bajtu od ktorego nalezy rozpoczac czytanie i-wezla. Oczywiscie w systemie EXT2 nalezy do ww. wzorow wlaczyc informacje o grupach.



Zwalnianie i-wezla


Jezeli proces zakonczyl operacje z plikiem (a wiec takze z i-wezlem), musi on zwolnic kopie pamieciowa i-wezla, aby inne procesy mogly pobrac wolny i-wezel. Generalnie algorytm polega na zmniejszeniu licznika otwarc pliku (i_count), oraz wykonaniu specjalnych czynnosci w przypadku, gdy proces jest ostatnim zamykajacym plik (i_count==0). Czynnosci te to: Algorytm ten w Linuxie 2.0 jest realizowany przez funkcje iput, znajdujaca sie w pliku inode.c.

Funkcja iput()

- sluzy do zwalniania kopii i-wezla z pamieci
DEFINICJA: void iput(struct inode *inode)
    WYNIK: brak

Implementacja funkcji:

{
    Wykonaj test blokady (wait_on_inode(inode));
    Jezeli licznik otwarc(i_count) = 0 wyjdz;
    Jezeli inode jest typu PIPE, obudz wszystkie przerywalne procesy 
       z kolejki PIPE_WAIT(*inode);
repeat:
    Jezeli licznik otwarc jest > 1 to zmniejsz go o 1 i wyjdz;
    Zwolnij procesy czekajace na wolny i-wezel w kolejce inode_wait;
    Jezeli inode byl typu PIPE, zwolnij zwiazana z nim strone pamieci;
    Jezeli wszystkie wskazniki w inode sa OK(w tym na sb i funkcje)
    {
       wykonaj put_inode(inode);
       jezeli liczba dowiazan do pliku wynosi 0, wyjdz;
    }
    Jezeli i-wezel nalezy zaktualizowac (i_dirt>0)
    {
       nagraj i-wezel (write_inode(inode));
       wykonaj test na blokade (wait_on_inode(inode));
       skocz do repeat;
    }
    Jezeli i-wezel ma charakter nagrywalny(plik,katalog lub link) i 
       sa zdefiniowane operacje na quota
    {
       zaloz blokade na inode;
       uaktualnij quote(sb->dq->drop(inode));
       zdejmij blokade, a nastepnie skocz do repeat;
    }
    Zmniejsz licznik otwarc pliku (i_count);
    Zwieksz licznik wolnych i-wezlow (nr_free_inodes);
}



Pomocnicze funkcje


get_empty_inode()
funkcja przydziela i-wezel z listy wolnych. Po znalezieniu wolnego i-wezla, przejsciu przez blokade na nim, oraz w razie koniecznosci nagraniu go (write_inode), nastepuje funkcja clear_inode. Funkcja ta zeruje strony i-wezla, wyjmuje go z obu list, zmienia go troche, np. zeruje cala strukture i wstawia znowu do listy wolnych i konczy dzialanie. Jezeli proces nie znajdzie wolnego i-wezla, zasypia na inode_wait, a po obudzeniu zaczyna szukac od nowa. Dalej funkcja get_empty_inode() ustawia rozmaite parametry i-wezla (np. daje mu kolejny numer, licznik wcielen 1, liczbe linkow 1 itp.), zmniejsza licznik wolnych i-wezlow (UWAGA: Jezeli<0 to =0) i zwraca i-wezel.

wait_on_inode()
jezeli flaga i_lock jest ustawiona zasnij w kolejce zwiazanej z i-wezlem (wait_queue)

read_inode()
zalozenie blokady, nagranie, zdjecie blokady

write_inode(), drop_inode(), put_inode()
sa to operacje wykonywane fizycznie na dysku, zalezne od konkretnego systemu plikow, zazwyczaj wykonywane przez super block. Czasami znajduja sie w nich takze uaktualnienia pol struktury inode, np. czasu zmian itp.


Bibliografia


  1. Pliki zrodlowe Linuxa:
  2. Maurice J. Bach "Budowa systemu operacyjnego UNIX" - rozdzial 4
  3. Linux Kernel Hackers Guide


    Autor: Tomasz Lasica