Do spisu tresci tematu 5

5.2.3 Scenariusze dostepu - algorytm getblk




Spis tresci



Funkcja getblk()

Zadaniem funkcji getblk() jest dostarczenie bufora z podrecznej pamieci buforowej odpowiadajacego wskazanemu blokowi. Blok ten jest opisany przez parametry funkcji podajace urzadzenie (kdev_t dev), z ktorego blok ma byc pobrany, numeru bloku (int block) oraz jego rozmiaru (int size). Te trzy parametry jednoznacznie wyznaczaja blok oraz przyporzadkowany mu bufor z podrecznej pamieci buforowaj.
DEFINICJA: struct buffer_head* getblk(kdev_t dev, int block, int size)
    WYNIK: wskaznik do naglowka bufora w przypadku sukcesu
           NUll wpp.

Algorytm dzialania funkcji getblk().

  1. W odpowiedniej kolejce haszujacej szukamy bufora opisanego przez parametry funkcji getblk() tzn. ktorego pola b_dev, b_blocknr, b_size z naglowka bufora sa odpowiednio rowne dev, block, size.
  2. Jesli znalezlismy poszukiwany bufor, to jesli nie jest brudny (bit BH_Dirty pola b_state naglowka bufora rowny jest 0) tzn. jego dane sa takie same jak dane zpisane w odpowiadajacym mu bloku fizycznego urzadzenia, to ustawiamy czas zapisu (pole b_flushtime naglowka bufora) na 0. Jesli dodatkowo bufor zawiera aktualne dane (bit BH_Uptodate pola b_state rowny 1) to wstawiamy go na koniec odpowiadajacej mu kolejki lru (najdluzej nieuzywanych buforow) za pomoca funkcji put_last_lru(). Oznaczamy bufor jako dotkniety tzn. ustawiamy bit BH_Touched pola b_state na 1. Nastepnie zwracamy znaleziony bufor konczac funkcje getblk().

    Uwaga. Wszystkie bufory znajdujace sie w kolejkach haszujacych zawieraja aktualne dane. Dlatego bufor znaleziony w kolejce haszujacej i nie bedacy brudnym zawsze zawiera aktualne dane. (Tak mi sie wydaje)

  3. Jesli nie znalezlismy poszukiwanego bufora to musimy wziac wolny bufor odpowiedniego rozmiaru i przyporzadkowac go danemu blokowi, czyli:


Funkcja refill_freelist().

Funkcja refill_freelist() ma na celu dostarczenie wolnych buforow podanego rozmiaru i wstawienie ich do kolejki wolnych buforow.
  DEFINICJA: void refill_freelist(int size)
 
Algorytm dzialania funkcji refill_freelist().
  1. Okreslamy zapotrzebowanie na pamiec potrzebna na utworzenie buforow podanego rozmiaru (zmienna needed). Liczba tworzonych buforow podczas jdnego wywolania funkcji refill_freelist() jest podana w bdf_prm.b_un.nrefill.
  2. Jesli liczba wolnych stron pamieci (nr_free_pages) jest dwa razy wieksza niz okreslona w zmiennej min_free_pages minimalna liczba wolnych stron i nie utworzylismy jeszcze wystarczajacej liczby buforow (needed >0 ) to wywolujemy funkcje grow_buffers(). Funkcja ta probuje zwiekszyc liczbe buforow podanego rozmiaru Jesli funkcji grow_buffers() udalo sie stworzyc nowe bufory, to zmniejszamy zapotrzebowanie na pamiec (needed) i wracamy do punktu 2.
  3. Sprawdzamy czy mamy juz dosyc wolnych buforow (needed <= 0) jesli tak, to konczymy funkcje refill_freelist().
  4. Sprawdzamy, czy jest zbyt duzo buforow innych rozmiarow. Jesli tak, to usuwamy je, a na ich miejsce probujemy utworzyc bufory podanego rozmiaru. Jesli mamy ich wystarczajaco duzo, to konczymy funkcje refill_freelist. Wyszukiwaniem zbednych buforow i usuwaniem ich zajmuje sie funkcja maybe_shrink_lav_buffers(), zas tworzeniem nowych funkcja grow_buffers().
  5. Teraz bedziemy probowac uzyskac wolne bufory likwidujac uzywane bufory podanego rozmiaru. Najpierw wyznaczamy kandydata dla kazdej niepustej kolejki lru (najdluzej nieuzywanych buforow). Pomijamy przy tym kolejki lru buforow brudnych i dzielonych. Kandydatem nie moze byc bufor, ktory jest brudny, dzielony, wlasnie uzywany, chroniony, niewlasciwego rozmiaru czy zalokowany. Elekcje wygrywa kandydat, ktory najdluzej nie byl uzywany (czas ostatniego uzycia jest wpisany w pole b_lru_time naglowka bufora). Usuwamy go z kolejki lru i haszujacej, nastepnie wstawiamy do kolejki buforow wolnych. Wybieramy nastepnego kandydata dla kolejki, z ktorej wybralismy zwyciezce. Sprawdzamy czy mamy juz dosyc wolnych buforow. Jesli tak, to konczymy funkcje, jesli nie to wracamy do elekcji.
  6. Nie uzyskalismy dostatecznej liczby wolnych buforow, nawet korzystajac z listy lru. W takim przypadku probujemy zwiekszyc liczbe wolnych buforow, wywolujac funkcje grow_buffers() jesli tylko liczba wolnych stron pamieci jest o piec wieksza niz minimalna liczba wolnych stron ( min_free_pages). Jesli udalo sie, to wracamy do punktu piatego.
  7. Ostatnim krokiem w zdobywaniu wolnych buforow jest wywolanie funkcji grow_buffers() z wiekszym priorytetem, niz do tej pory. Jesli i to zawodzi, to budzimy proces bdflush (zapisuje brudne bufory), a sami zasypiamy czekajac na zakonczenie jego dzialania.
  8. Wracamy do punktu piatego.

Funkcja grow_buffers()

Funkcja grow_buffers() probuje zwiekszyc liczbe dostepnych buforow podanego rozmiaru.
    DEFINICJA: int grow_buffers(int pri, int size)
        WYNIK: 1 w przypadku sukcesu
               0 wpp
  1. Pobiera wolna strone pamieci, za pomoca funkcji get_free_page(), wywolujac ja z priorytetem podanym w parametrach (pri). Jesli to sie nie uda, to zwraca zero.
  2. Wywolujemy funkcje create_buffers(), podajac w parametrach pobrana strone oraz rozmiar bufora. Funkcja ta stworzy nam bufory, ktorych dane beda umieszczane na podanej stronie.
  3. Wstawiamy uzyskane bufory do kolejki buforow wolnych i zwracamy 1.

Funkcja create_buffers().

  DEFINICJA: struct buffer_head* create_buffers(unsigned long page, unsigned long size)
      WYNIK: wskaznik do naglowka bufora 
             NULL jesli nie udalo sie stworzyc bufory 
Funkcja create_buffers() tworzy bufory podanego rozmiaru, ktorych dane przechowywane beda na dostarczonej (jako parametr funkcji) stronie pamieci. Dokladniej dopoki jest miejsce na stronie pamieci pobiera nieuzywany naglowek bufora (funkcja get_unused_buffer_head()), a nastepnie przydziela mu (pole b_data naglowka) kolejny kawalek strony. Otrzymane bufory laczymy w liste przez pola b_this_page w naglowku bufora. Zwracamy pierwszy bufor z tej listy.

Funkcja get_unused_buffer_head()

  DEFINICJA: buffer_head* get_unused_buffer_head(void)
      WYNIK: wskaznik do naglowka bufora
             NULL jesli nie udalo sie pobrac naglowka bufora
Funkcja get_unused_buffer_head() dostarcza nieuzywany naglowek bufora. Jesli nie ma nieuzywanych naglowkow, ktore sa trzymane na liscie unused_list, to probujemy je stworzyc. W tym celu pobieramy wolna strone pamieci i tworzymy na niej naglowki buforow. Jesli to sie nie uda (mamy za malo pamieci), to zasypiamy w oczekiwaniu na zwolnienie jakiegos naglowka.

Funkcja maybe_shrink_lav_buffers().

  DEFINICJA: int maybe_shrink_lav_buffers(int size)
      WYNIK: 1 w przypadku sukcesu
             0 wpp
Funkcja maybe_shrink_lav_buffers() wybiera bufory (dokladniej rozmiar buforow), o rozmiarze roznym niz podany w parametrze, ktore byly stosunkowo rzadko uzywane. Nastepnie probuje zwolnic jedna strone pamieci, zajmowana przez te bufory (korzystajac z funkcji shrink_specific_buffers()).

Funkcja shrink_specific_buffers().

Funkcja shrink_specific_buffers() probuje zwolnic jedna strone pamieci zajmowana przez bufory rozmiaru podanego w parametrach.
  DEFINICJA: int shrink_specific_buffers(unsigned int priority, int size)
      WYNIK: 1 w przypadku sukcesu
             0 wpp
  1. Najpierw rozpatrujemy wolne bufory. Bierzemy bufor z kolejki wolnych. Jesli pozostale bufory, ktorych segment danych znajduje sie na tej samej stronie pamieci, sa nieuzywane, to zwalniamy ja i zwracamy jeden.
  2. Teraz probujemy zwalniac bufory z kolejnych list lru. Pomijamy przy tym liste buforow dzielonych. Bierzemy kolejno bufory z listy lru zgodnie z kolejnoscia postarzania. Liczba buforow, ktore zbadamy zalezy od licznosci tej listy. Jesli dany bufor jest w stanie dotkniety (BH_Touched), to zmniejszamy wiek bufora, jesli nie, to postarzamy bufor. Wiek bufora jest zapisany w naglowku strony pamieci zawierajacej jego segment danych. Jesli bufor jest uzywany, chroniony lub zalokowany, to bierzemy nastepny. Jesli jest brudny, to zapisujemy go i bierzemy nastepny. Jesli bufor ma wiek rowny zero (wiek strony pamieci) i jesli pozostale bufory, ktorych dane znajduja sie na tej samej stronie, sa nieuzywane, to zwalniamy ja i zwracamy jeden. Jesli nie udalo sie zwolnic strony, to zwracamy zero.

    Uwaga Dzialanie tej funkcji opisalem dla wywolania przez funkcje maybe_shrink_lav_buffers(), czyli z priorytetem (pri) rownym 6.


    Bibliografia

    1. Pliki zrodlowe Linuxa:


    Autor: Wojciech Poslowski