Do spisu tresci tematu 5


5.3.5 Katalogi

Spis tresci:


Katalog na dysku

Katalog w UNIXie podobnie jak zwykly plik sklada sie z ciagu bajtow. Od zwyklego pliku rozni go, oprocz pola typu pliku zapisanego i-wezlie (i_mode), sposob zapisu informacji. W katalogu jest on scisle okreslony i stanowi ciag struktor o nastepujacej budowie(opisane w linux/fs/ext2/dir.c)

Struktura ext2_dir_entry


struct ext2_dir_entry{ 
    _u32  inode                 /*  numer i-wezla                */
    _u16  rec_len               /*  dlugosc pozycji katalogowej */
    _u16  name_len              /*  dlugosc nazwy               */
    char  name[EXT2_NAME_LEN]   /*  nazwa                       */

}

W przeciwienstwie do tego co pisze Bach rozmiar pozycji nie jest ustalony z gory. Kazda ma rozmiar okreslony przez dlugosc nazwy zaokroglona do 4n bajtow i wlasnie ta wielkosc jest wpisana w polu rec_len. Informacja o faktycznej dlugosci pozycji znajduje sie w polu name_len. Nazwa pliku jest ograniczona do 255 znakow (stala EXT2_NAME_LEN). W zaleznosci od ustawienia flagi NO_TRUNCATE nazwy dluzsze moga byc obcinane lub traktowane jako bledne.

Na poczatku kazdego katalogu znajduja sie pozycje o nazwach "." i ".." oznaczajace odpowiednio katalog biezacy i macierzysty. W katalogu bedacym korzeniem drzewa systemu plikow ww. pozycje maja numer i-wezla systemu plikow.

W katalogu moga znajdowac sie pozycje, ktore maja numer i-wezla rowny 0. Taka sytuacja powstaje w momencie usuwania pozycji z katalogu. Dodawanie pliku jest na zasadzie przeszukiwania liniowego do pierwszej struktory, w ktorej numer i-wezla jest rowny 0 i jest odpowiednio duzo miejsca. Jezeli nie zostanie ona znaleziona nowy plik dopisywany jest na koncu.

Z kazdym katalogiem, jak ze zwyklym plikiem, zwiazane sa prawa dostepu, jednak dla katalogu ich znaczenie jest nieco inne:

Wszystkie procesy maja potencjalna mozliwosc odczytu katalogu jako pliku, jednak zapisywac katalog moze tylko jadro, co gwarantuje poprawnosc danych.


Katalog w pamieci

Aby przyspieszyc dostep do pozycji katalogowych czesc z nich przechowywana jest w pamieci. Dlugosc nazwy takiej pozycji nie moze przekraczac jednak 15 znakow (stala DCACHE_NAME_LEN), gdyz zbytnio obciazyloby to pamiec. Wykorzystuje sie tu dwie struktury danych(opisane w linux/fs/dcache.c)

Struktury hash_list i dir_cashe_entry


struct hash_list{
    dir_cache_entry * next   /*     nastepny w kolejce haszujacej   */
    dir_cache_entry * prev   /*     poprzedni w kolejce haszujacej  */

}

struct dir_cashe_entry {
   hashlist  * h
   k_devt    dc_dev                    /*  numer urzadzenia       */
  unsigned long       dir    /*numer i-wezla katalogu, w ktorym znajduje sie pozycja */
  unsigned long       version          /*   wersja katalogu      */
  unsigned long       inode            /*   numer i-wezla pozycji */
  unsigned char       name_len         /*   dlugosc nazwy        */
  unsigned char      * name[DCACHE_NAME_LEN]    /* nazwa         */
  dir_cashe_entry * lru_head           /* wskaznik na kolejke lru w ktorej
                                         znajduje sie pozycja    */
  dir_cache _entry * nextlru,
                   * prevlru           /* wskazniki na poprzedni i nastepny 
                                          w kolejce lru          */
}

Przy ich pomocy utworzono tablica jedniekierunkowych list cyklicznych

struct hash_list hash_table[DCACHE_HASH_QUEUES]

dpowiedzialna za implementacje kolejek mieszajacych (stala DCACHE_HASH_QUEUES wynosi 32)

Funkcja haszujaca zalerzy od :

i ma nastepujaca postac:

#define hashfn (dev, dir,namehash) ((HASHDEV(dev)^(dir)^namehash))%DCACHE_HASH_QUEUES

static inline unsigned long namehash (const char * name, int len)
{
   return len +((const unsigned char * )name)[0]-((const unsigned char 
   * )name)[len-1]
}

Do obslugi tej tablicy sluza nastepujace funkcje:

void remove_hash (struct dir_cache_entry * de)
        usuwa de z tablicy haszujacej
void add_hash (struct dir_cache_entry * de,struct hash_list *hash)   
        dodaje de na poczatek listy hash
struct dir_cache_entry * find_entry (struct inode * dir,const char * name, int len, struct hash_list * hash)    
       znajduje w kolejce hash pozycje z katalogu dir nazwie name
       ( musza zgadzac sie numer urzadzenia ,wersja katalogu,numer 
       iwezla katalogu,dlugosc nazwy pozycji i nazwa pozycji).
       Jezeli nie ma takiej pozycji to zwraca null.

Kazda z pozycji katalogowych znajdujach sie w tablicy haszujacej umieszczona jest ponadto na jednej z jednokierunkowych cyklicznych kolejek lru o ustalonej (po zanicjowaniu tych kolejek i tablicy mieszajacej, czyli wywolaniu funkcji cashe_init) dlugosci rownej 128 (stala DCACHE_SIZE)

Sa to kolejki :

dir _cashe_entry level1_cashe [DCASHE_SIZE]

dir_cashe_entry level2_cashe [DCASHE_SIZE]

Wskazniki dir_cashe_entry * level1_head, *level2_head wskazuja na pierwsze elementy w tych kolejkach.

Operacje na tych kolejkach wykonywane sa przez funkcje

void remove_lru( struct dir_cache_entry * de)
          usuwa element wskazywany przez de z kolejki
void add_lru( struct dir_cache_entry * de, struct hash_list
* hash)
          Wstawia element de na koniec kolejki wskazywanej przez head 
void update_lru ( strust dir_cache_entry * de)
          Przesuwa element wskazywany przez de na koniec kolejki.
void move_to_level2 ( struct dir_cache_entry *de, struct hash_list
*hash)
      Jezeli element old_de znajduje sie juz w kolejce level2_cache ,
      to przesuwa go na jej koniec wywolujac update_lru . W przeciwnym przypadku
      usuwa z tablicy haszujacej pierwszy element z level2_cache (czyli wskazywany
      przez level2_head ), kopiuje do niego dane (numer iwezla katalogu, nazwe
     itp.) z old_de, wstawia go ponownie do tablicy haszujacej
      i przesuwa wskaznik level2_head na nastepny w kolejce.Tak wiec nowa pozycja
      znajdzie sie na koncu kolejki level2_head .

Poniewaz sa to kolejki o stalej dlugosci, wiec operacje remove_lru i add_lru zawsze nastepuja bezposrednio po sobie i w powyzszej kolejnosci .

Roznice pomiedzy level1 i level2 mozna zaobserwowac rozpatrujac dwie funkcje dcashe_lookup i dcashe_add .

Funkcja dcache_lookup

DEFINICJA: int dcache_lookup(struct inode * dir,const char * name,            
           int len,unsigned long *ino)
   /* dir - i-wezel katalogu */
   /* name,len - nazwa i dlugosc nazwy pozycji katalogowej 
WYNIK:1 w przypadku sukcesu
      0 w przypadku porazki
      numer i-wezla na ino


{
     if(dlugosc nazwy wieksza od DCACHE_NAME_LEN )
          return(0);
     znajdz odpowiednia kolejke mieszaja;
     wywolaj find_entry, aby znalezc szukana pozycje w tablicy haszujacej;
     if(nie ma szukanej pozycji w pamieci -find_entry zwrocilo null)
         return(0);
     przenies pozycje ktora zwrocilo find_entry na koniec kolejki level2_cache
      -czyli wywolaj move_to_level2);
mc    przypisz na ino numer i-wezla ze znalezionej pozycji;
    zwroc 1;
}

Funkcja dcache_add

DEFINICJA:void dcache_add (structure inode *dir, const char * name ,
              int  len, unsigned long *ino )
      /* dir - i-wezel katalogu
      /* name,len,ino -nazwa, dlugosc nazwy ,numer i-wezla pozycji katalogowej

{

    if(dlugosc nazwy wieksza od DCACHE_NAME_LEN)
        wyjdz z funkcij;
    znajdz odpowiednia kolejke haszujaca;
    wywolaj find_entry,aby sprawdic czy dana pozycja jest w tablicy haszujacej
    if(pozycja jest juz w tablicy haszujacej find_entry nie zwrocilo nulla)
    { 
       wpisz do pozycji jako numer i-wezla wejsciowy numer;
       przesun pozycje na koniec kolejki (tej w ktorej juz jest) wywolujac
       update_lru;
       return;
    }
    usun pierwszy element w kolejki level1-cache (wskazywany przez level1_head)
    z tablicy haszujacej;
    przypisz zmiennej pomocniczej de wskanik level1_head;
    przesun wskaznik level1_head na nastepny w kolejce;
    wpisz do elementu wskazywanego przez de dane dotyczace nowej pozycji(numer
    iwezla katalogu, wersja katalogu ,nazwa pozycji itp.);
    wstaw pozycje wskazywana przez de do kolejki haszujacej;
}

Widac wiec ,ze pozycja wstawiana do tablicy haszujacej umieszczana jest w level1_cache i dopiero wywolanie funkcji dcache_lookup, czyli odwolanie sie do tej pozycji, powoduje wstawienie jej kopi do level2_cache .

Warto zauwazyc, ze po wywolaniu ktorej kolwiek z powyzszch funkcji pozycja znajdzie sie na koncu odpowiedniej kolejki . Na poczatku kazdej z nich znajduja sie wiec elementy do ktorych odwolywano sie najdawniej i to one zostana usuniete jako pierwsze .

Przy dokladnym przyjzeniu sie funkcji dcache_lookup mozna znalezc instrukcje pozornie zbyteczna . Otoz po znalezieniu szukanej pozycji w tablicy haszujacej wpisuje sie do niej numer i-wezla, ktory juz powinien sie tam znajdowac . Wynika to z tad, ze ostatni parametr funkcji dcache_add -numer i-wezla moze byc rowny 0, co oznacza ze pozycja tak naprawde nie istnieje w danym katalogu . Jezeli uzytkowinik (po sprawdzeniu ze danej pozycjia nie istnieje) stwozy ja, to dzieki przechowywaniu pozycji z 0 jako numerem i-wezla , wstawienie jej do tablicy haszujacej wymagalo bedzie tylko wpisania numeru i-wezla .


Bibliografia


Pytania i odpowiedzi


Autor:Malgorzata Guziak