Mechanizm buforowania

 

Zapewnia jednolity dostęp do urządzeń blokowych dla systemów plików.

Zapobiega wielokrotnemu czytaniu z dysku tych samych danych, zbędnego ich zapisywania na dysk.

Umożliwia wcześniejsze przeczytanie danych (w wolnym momencie), które będą najprawdopodobniej później potrzebne.

Pozwala nie zatrzymywać procesu, który chce coś zapisać na dysk lub odczytać z dysku.

Pozwala rozłożyć zapisy w czasie.

Zapewnia synchronizację.

 

W Linuksie istnieje pięć pamięci podręcznych związanych z systemami plików. Są to:

  1. pamięć podręczna buforów (buffer cache)
  2. pamięć podręczna stron (page cache)
  3. pamięć podręczna katalogów (dcache)
  4. pamięć podręczna i-węzłów (i-node cache)
  5. pamięć podręczna punktów montowań (mount cache)

Główną pamięcią podręczną jest obecnie pamięć stron.
Przechowuje się w niej strony plików (niekoniecznie kolejne bloki pliku z dysku).
Główne struktury danych to: tablica hashująca i listy stron plików przy każdym i-węźle.
Na stronach są zazwyczaj bufory (ich dane) - jeśli czytamy stronę pliku to tworzymy kilka buforów na tej stronie, i wczytujemy do nich dane (chociaż tak nie musi być).

Bufory nie są już tak istotną częścią linux'a jak w jądrach przed 2.4; spadek ich ważności uwidacznia się w przydzielanej im pamięci (e. g. tablica hashująca 4 razy mniejsza niż tablica hashująca page cache'a).

Buforowanie jest prawie całkowicie uwątkowione, tzn. blokadę jądra (kernel_lock) zamyka się rzadko, a większość pracy wykonywana jest asynchronicznie.

Jest ono też przystosowane do działania na systemach wieloprocesorowych.

 


czytanie bloków


 

getblk
Dane: numer urządzenia, numer bloku, rozmiar bloku

Zwraca bufor z żądanym blokiem, jeśli już taki jest, lub pusty zaalokowany bufor.

Jak się wyszukuje bufory?

  • Tablica buforów to tablica hashująca z liniowym rozwiązywaniem kolizji.
  • Gdy szuka się bufora, oblicza się dla niego funkcję hashującą, i przegląda listę w tablicy, na pozycji odpowiadającej wartości funkcji.
  • Aby bufor został uznany za właściwy zgodzić się musi wszystko - urządzenie, numer i rozmiar.
  • Gdzie jest używany getblk?

  • Po pierwsze w breadzie, ale poza tym w niektórych systemach plików (nfs, jfs, fat...) i w procedurach obsługi urządzeń blokowych.
  •  

    bread
    Dane: numer urządzenia, numer bloku, rozmiar bloku

    Zwraca bufor zawierający dany blok.

    Jak działa ll_rw_block (READ, 1, &bh)?

  • Jeśli bufor nie jest zablokowany, to go blokuje, jeśli jest, to nie robi nic.
  • Jeśli bufor jest aktualny to nie robi nic.
  • Dodaje żądanie do kolejki odpowiedniego urządzenia (dokładniej wywołuje make_request_fn - zdefiniowaną dla każdego urządzenia).
  • Ustawia funkcję wywoływaną po zakończeniu transmisji (funkcja ta powinna przynajmniej odblokować i oznaczyć bufor jako aktualny).
  • Jak działa wait_on_buffer?

  • Zwiększa licznik użytkowników bufora.
  • Dodaje się do kolejki czekających na bufor.
  • Dopóki bufor jest zablokowany:
              - wykonuje zadania z kolejki zadań dyskowych (run_task_queue (&tq_disk)),
              - oddaje procesor.
  • Usuwa się z kolejki czekających na bufor.
  • Zmniejsza licznik użytkowników bufora.
  • Gdzie jest używany bread?

  • Głównie przy czytaniu bloków przez system plików (ext2, ext3, minix, nfs, reiserfs...).
  • Jest tak gdyż tylko system plików wie, dokładnie którego bloku potrzebuje.

  • Wykorzystanie jest często pośrednie - system plików tworzy swoją procedurę czytania bloku (..._get_block - z użyciem bread'a),
  • którą używa przy definiowaniu procedury wczytywania strony (i-node->adress_space_operations->readpage() - zazwyczaj wczytanie strony pełnej buforów).
  • Ta ostatnia procedura jest z kolei używana przy czytaniu plików. (Tak jest na przykład dla ext2.)

  • Jest również wykorzystywany, w niektórych sterownikach urządzeń.
  •  


    zwalnianie buforów


     

    brelse

    Powiadamia system, że już nie używamy bufora.
    Atomowo zmniejsza ilość użytkowników bufora (b_count).
    Licznik ten służy jako pewnego rodzaju blokada, dopóki jest większy od zera system nie pozbędzie się bufora. Większość operacji na buforach jest otoczona parą: zwiększ licznik, zmniejsz licznik.
    Brelse dodatkowo dodaje wpis do historii bufora.
    Jest to funkcja "do pary" do bread'a, który zwiększa ten licznik.

     

    bforget

    To samo co brelse, poza tym, że próbuje wstawić bufor na listę wolnych buforów.

    Algorytm:

    1. Zablokuj listy LRU.
    2. Zablokuj tablicę hashującą.
    3. Atomowo sprawdź i zminiejsz liczbę użytkowników bufora.
    4. Jeśli bufor nie ma użytkowników, nie jest zablokowany, nie jest chroniony to usuń go ze wszystkich struktur.
    5. Odblokuj tablicę i listy.
    6. Jeśli bufor spełnił warunki p. 4 (był nieużywany) to dodaj go do odpowiedniej listy wolnych buforów.

     


    odzyskiwanie wolnych buforów


     

    refill_freelist
    Dane: rozmiar buforów

    Uzupełnia jedną z list wolnych buforów.

    Co robi free_shortage?

  • Sprawdza czy jest odpowiednia ilość nieaktywnych czystych stron - takich jakie są potrzebne do utworzenia buforów.
  • Jak działa page_launder (GFP_NOFS, 0)?

  • Przenosi strony na listę nieaktywnych czystych; flaga GFP_NOFS zapobiega próbom zapisania stron na dysk (poprzez adress_space_operations->writepage() - co mogłoby prowadzić do zapętlenia).
  • Nie żądamy też zapewnienia uzyskania stron (poprzez ich synchroniczne zwalnianie).
  • Jak działa grow_size?

  • Alokuje jedną stronę (page_alloc (GFP_NOFS)).
  • Blokuje tę stronę.
  • Tworzy na niej bufory (create_buffers).
  • Blokuje odpowiednią listę wolnych buforów.
  • Dodaje wszystkie bufory na listę.
  • Odblokowuje listę.
  • Dodaje tę stronę do cache'a stron (lru_cache_add).
  • Odblokowuje stronę.
  • Jak działa create_buffers?

  • Pobiera nagłówki buforów, z listy nieużywanych lub nowe (tworzone alokatorem płytowym - SLAB_NOFS), i przydziela im obszary danych na otrzymanej stronie.
  • Gdzie jest wykorzystywana ta procedura?

  • W getblk, gdy brakuje bloków.

  • Utrzymywana jest rezerwa nieużywanych nagłówków buforów dla asynchronicznych operacji wejścia/wyjścia - żeby uniknąć blokady; na przykład przy braku pamięci i konieczności wyrzucania stron na dysk.

     


    demony dyskowe


     

    Zmienione bufory od czasu do czasu przydałoby się zapisać na dysk.
    Robią to dwa demony - kupdate i bdflush.
    Pierwszy budzi się co pewien czas (co 5 sS) i zapisuje wszystkie metadane i brudne bufory.
    Drugi jest budzony zazwyczaj gdy brudnych buforów zaczyna być zbyt dużo, czy też brakuje miejsca na nowe bufory.

    Obydwa wykorzystują procedurę (asynchronicznego) zapisywania buforów na dysk: flush_dirty_buffers (jest to w ogóle jedyna taka procedura).

    flush_dirty_buffers
    Dane: tryb działania

    Zapisuje i zwalnia brudne bufory.

    Demon bdflush w kółko wykonuje następujące czynności:

    1. Wypisuj brudne bufory (flush_dirty_buffers), bez sprawdzania czasów.
    2. Jeśli nic nie udało się wypisać lub jest mniej niż 30% (nfract) brudnych buforów to wykonaj zadania dyskowe (tq_disk) i uśnij.

    Demona można budzić - wakeup_bdflush - dzieje się to czasem podczas uzyskiwania wolnych buforów.

    Kupdate - drugi demon, budzi się co pewien czas i wypisuje niezablokowane węzły indeksowe, super bloki, i stare bufory; wykonuje, również synchronicznie, kolejkę zadań dyskowych (run_task_queue(&tq_disk)).

    Działanie bdflush'a jest sparametryzowane, można je zmieniać nawet podczas działania systemu (sys_bdflush).

    Parametry demona to:
    parametropiswartość
    ustalona
    wartość
    minimalna
    wartość
    maksymalna
    nfractprocent brudnych buforów, przy którym zwalnia się je asynchronicznie30%0%100%
    nfract_syncprocent brudnych buforów, przy którym zwalnia się je synchronicznie (blokująco)60%0%100%
    ndirtymaksymalna liczba buforów wypisanych przy jednym obudzeniu641050000
    age_bufferprzez jaki czas bufor będzie zbyt młody żeby go wypisać30s1s6000s
    intervalczas pomiędzy uruchumieniami kupdate'a5s0s600s
    nrefillliczba buforów, jaką próbuje się uzyskać przy jednym wywołaniu refill_freelist (nieużywane)64520000

     

     


    wersja jądra: 2.4.7
    autor: Wojciech Ruszczewski

     

    Czytanie z wyprzedzeniem