Do spisu tresci tematu 4

4.3.2 Wymiana stron z pamieci na dysk

Spis tresci


Wstep

Potrzeba zwolnienia ramki strony pamieci glownej poprzez zapisanie jej kopii na dysku zachodzi zawsze wtedy, gdy w systemie zaczyna brakowac pamieci dla dzialajacych aktualnie procesow. Podstawowa funkcja jadra wolana zawsze w tego rodzaju przypadkach jest funkcja try_to_free_page. Jej zadaniem jest zwolnienie dokladnie jednej ramki strony w pamieci glownej. Jest ona wolana na dwa rozne sposoby, mozna rzec w dwoch trybach, przy czym tryb ten rozpoznajemy po wartosci jednego z argumentow (wait). Pierwsza mozliwosc (wait==1) zachodzi wtedy, gdy jadro nie dysponuje w systemie wolnymi ramkami stron a potrzebuje przydzielic nowa. Druga (wait==0) spotykamy zas wtedy, gdy specjalny proces jadra (demon wymiany) zauwazy, ze ilosc wolnej pamieci w systemie zaczyna spadac do niebezpiecznie niskiego poziomu. Reakcja na to jest tzw. wymiana w tle (ang. background pageout).


Funkcje rodziny TRY_TO_FREE_PAGE

Funkcja try_to_free_page.

Argumenty: priority - nie sluzy do niczego 
           dma - przekazywany do jednej z trzech (patrz nizej) wywolywanych funkcyj
           wait - flaga informujaca o trybie wywolania funkcji, przekazywana dalej 
Rezultat: 1 gdy udalo sie zwolnic jakas ramke w pamieci glownej, 0 wpp.

Opis: 

Podstawowe znaczenie dla dzialania tej funkcji ma statyczna zmienna, ktorej wartosc decyduje o tym w jaki sposob sprobujemy zwolnic pamiec. Sa trzy mozliwosci: przez zwolnienie ramek stron uzywanych do stronicowania plikow (za pomoca funkcji shrink_mmap), przez wymiane z dyskiem stron pamieci dzielonej (funkcja shm_swap), lub zwyczajnych stron pamieci procesow (funkcja swap_out). Po zakonczeniu sukcesem dzialania funkcji try_to_free_page (czyli zwolnieniu jakiejs ramki), dzieki informacji zawartej w tej zmiennej statycznej, podczas nastepnego wywolania omawianej funkcji bedziemy probowac w pierwszej kolejnosci zwolnic ramke "tego samego rodzaju" tzn. np. ramke pamieci dzielonej. To zas powoduje, ze nie preferujemy wyrzucania z pamieci glownej jakiegos konkretnego rodzaju stron, przez co trudniejsze jest przeoczenie jakiejs bardzo starej, dawno juz nieuzywanej strony znajdujacej sie w pamieci, tylko dlatego, ze jest np. strona pamieci dzielonej. Gdy wait==1 try_to_free_page jest bardziej uparta w dazeniu do zwolnienia ramki strony, tzn. znacznie dluzej szuka odpowiednio starej. W efekcie moze zwolnic taka, ktora przy wait==0 uznalaby za zbyt mloda. Konkretnie, wola ktoras z trzech ww. funkcyj kilka razy, zmniejszajac za kazdym razem argument priority.

Funkcja swap_out

Argumenty: priority - mowi o tym, jak pilne jest zlecenie zwolnienia ramki 
           dma - przekazywany do funkcji swap_out_process 
           wait - jak dma 
Rezultat: jak w funkcji poprzedniej 

Opis: 

Funkcja przeszukuje tablice procesow, w celu znalezienia takiego, ktory posiada przydzielona mu pamiec glowna oraz jest oznaczony jako "wymienialny" (ang. swappable). Jesli ta faza sie powiedzie, funkcja ustala, na podstawie informacji w strukturze mm_struct procesu, liczbe stron jaka nalezy procesowi odebrac (upraszczajac mozna rzec , iz jest to pewien ulamek liczby zajmowanych stron), przy czym podczas jednego wywolania omawianej funkcji zwolnimy co najwyzej jedna strone tego procesu a reszte podczas nastepnych wywolan. Gdy po kolejnym wywolaniu nasz licznik spadnie do zera to obliczymy go nastepnym razem wg tego samego algorytmu. Po przebrnieciu przez te faze wstepna funkcja wywoluje swap_out_process podajac jako argument namierzony proces. Jezeli w wyniku otrzyma informacje o powodzeniu, przekazuje te wiadomosc dalej, przy czym jesli zabrala juz tyle stron procesowi, ile sobie pierwotnie zaplanowala, przy nastepnym wywolaniu zacznie szukac nowego kandydata (dzieki temu unikamy sytuacji, w ktorej caly czas wymieniamy strony jednego procesu). W przeciwnym przypadku szuka nastepnego procesu do wymiany, lub, jesli poszukiwania trwaja juz zbyt dlugo (decyduje o tym argument priority), zwraca 0 (niepowodzenie).

Funkcja swap_out_process

Argumenty: tsk - znaleziony w swap_out proces, ktorego strone chcemy zwolnic 
           dma - przekazywany do swap_out_vma 
           wait - rowniez 
Rezultat: 1 -sukces, -1 - wystapil blad, wpp. porazka 

Opis: 

Funkcja wywoluje dla wszystkich uzywanych obszarow wirtualnej przestrzeni adresowej (czyli wszystkich struktur typu vm_area_struct) procesu funkcje swap_out_vma, az w ktoryms z nich uda sie zwolnic strone. Wtedy zapamietuje (w pozycji tablicy procesow) adres (wirtualny), od ktorego przy nastepnym wywolaniu powinna zaczac przeszukiwac przestrzen adresowa procesu (patrz parametr start funkcji nastepnej). Zwraca informacje o sukcesie.

Funkcja swap_out_vma

Argumenty: tsk - ten sam, co w swap_out_process 
           vma - obszar przestrzeni wirtualnej powyzszego 
           start - pierwszy interesujacy nas adres w obszarze vma 
           pgdir - pozycja w katalogu stron pierwszego poziomu odpowiadajaca wartosci start
           dma - przekazywany do swap_out_pgd 
           wait - rowniez 
Rezultat: jak w funkcji poprzedniej

Opis: 

Na poczatku sprawdzane sa atrybuty obszaru vma. Jesli jest to segment pamieci dzielonej to funkcja zwraca 0, bo wymiana tego rodzaju pamieci rzadza inne mechanizmy. Podobna sytuacja ma miejsce, gdy obszar jest zablokowany (flaga VM_LOCKED). Nastepnie funkcja wola funkcje swap_out_pgd dla wszystkich pozycji w katalogu stron pierwszego poziomu procesu odpowiadajacym adresom poczawszy od start a skonczywszy na koncu obszaru pamieci wirtualnej wskazywanego przez vma, az otrzyma informacje o sukcesie. Oczywiscie, moze sie zdarzyc, ze nie uda sie wymienic zadnej ze stron zwiazanej z vma. I wtedy zwracane jest 0.

Funkcja swap_out_pgd

Argumenty: tsk - ten sam, co w swap_out_vma
           vma - podobnie 
           pgdir - pozycja w katalogu stron pierwszego poziomu 
           start - poczatek obszaru, ktory chcemy zrzucic na dysk (adres wirtualny) 
           end - koniec (j.w.) 
           dma - przekazywany do swap_out_pmd 
           wait - rowniez 
Rezultat: jak w funkcji poprzedniej 

Opis:

Po sprawdzeniu, ze pgdir jest uzywanym katalogiem, funkcja wola funkcje swap_out_pmd podajac jako argument kolejno katalogi stron drugiego poziomu bedace pozycjami katalogu pgdir a odpowiadajace adresom zawartym pomiedzy start a end.

Funkcja swap_out_pmd

Argumenty: tsk -
           vma - 
           start -
           end - 
           dma - 
           wait - WSZYSTKO jak w funkcji poprzedniej 
           pmdir - katalog stron drugiego poziomu 
Rezultat: Znowu: 1 oznacza, ze zwolnilismy jakas ramke, zas inne wartosci oznaczaja porazke 

Opis:

Funkcja jest identyczna jak poprzednia, z ta roznica, ze zamiast operowac na katalogu stron pierwszego poziomu, dziala na katalogu poziomu drugiego i miast wolac funkcje swap_out_pmd wola try_to_swap_out. Dwie ostatnie funkcje sa konieczne ze wzgledu na skomplikowana strukture tablicy stron procesu. Gdyby tablica stron byla zaimplementowana jako tablica prosta bylyby one zbedne i ze swap_out_vma wolalibysmy od razu funkcje ponizsza.

Funkcja try_to_swap_out

Argumenty: tsk - proces, ktorego strone probujemy zgrac na dysk
           vma - obszar jego przestrzeni adresowej, ktory zawiera interesujaca nas strone
           address - strony w przestrzeni adresowej procesu 
           pte - pozycja w tablicy stron procesu (identyfikuje strone) 
           dma - w opisie funkcji 
           wait - przekazywane do rw_swap_page
Rezultat: 1 - Udalo nam sie zwolnic ramke, inna wartosc - nie 

Opis:

Po sprawdzeniu, ze pozycja pte odnosi sie do strony znajdujacej sie w pamieci glownej odszukujemy w tablicy ramek te zwiazana z pte. Jezeli jest ona zerezerwowana (flaga PAGE_RESERVED) lub zablokowana (PAGE_LOCKED) lub tez dma==1 a ramka nie moze korzystac z DMA (flaga PAGE_DMA) zwracamy informacje o porazce (zero). W przeciwnym przapadku na podstawie stanu kilku bitow kontrolnych podejmujemy decyzje o dalszej akcji (ACCESSED oznacza bit odwolania do strony, a DIRTY oznacza bit modyfikacji - obie te informacje trzymane sa w tablicy stron procesu):

ACCESSED DIRTY Czy jest kopia na dysku? Dzialanie

0

0

nie

E

0

0

tak

D

0

1

nie

C

0

1

tak

B

1

0

nie

A

1

0

tak

A

1

1

nie

A

1

1

tak

B

Wyjasnienia:
A. Strona byla niedawno uzywana. Dokonujemy jej odmlodzenia, czyscimy bit ACCESSED po czym zwracamy 0 (strona nie zestarzala sie na tyle, by mozna byloby ja wyrzucic).
B. Strona byla modyfikowana. Zwalniamy kopie na dysku (jest juz nieaktualna) i odmladzamy strone. Oczywiscie zwracamy 0 (nie udalo sie nic wyrzucic).
C. Strona byla modyfikowana, jej kopia dyskowa, o ile istniala, zdazyla sie zdezaktualizowac. W tym przypadku postarzamy strone i jezeli okaze sie odpowiednio stara zapisujemy jej obraz na dysku (funkcja rw_swap_page) a w tablicy stron procesu umieszczamy informacje o miejscu zapisu. Zwalniamy ramke i zwracamy 1 (sukces).
D. Strona nie byla ostatnio modyfikowana, w zwiazku z czym jej kopia na dysku zawiera aktualne dane. Mozna wiec bezkarnie zwolnic ramke a w tablicy stron procesu zapamietac adres dyskowy kopii strony. Robimy to po uprzednim postarzeniu strony i tylko wtedy, gdy zestarzala sie ona w stopniu wystarczajacym. Nie wykonujemy zadnej operacjii dyskowej.
E. Jest to strona zwiazana z i-wezlem (jest obrazem jakiegos pliku). Jezeli strona zestarzala sie czyscimy pozycje w tablicy stron procesu, wolamy funkcje page_unuse sluzaca do zwalniania stron zwiazanych z i-wezlami. Zwracamy 1, gdy operacja zakonczyla sie sukcesem, inna wartosc w przypadku porazki.


Funkcja shrink_mmap

Argumenty: priority - ma takie samo znaczenie jak w funkcji swap_out
           dma - czy strona ktora mamy zwolnic musi byc strona DMA?
Rezultat: 1 gdy udalo sie zwolnic jakas ramke, 0 w przeciwnym przypadku 

Opis:

Funkcja sluzy do zwalniania ramek stron uzywanych do stronicowania plikow, oraz buforow dyskowych. Przeglada ustalona liczbe pozycji (w zaleznosci od argumentu priority) w tablicy ramek w poszukiwaniu takich, dla ktorych flaga LOCKED jest wyzerowana i spelniajacych warunek dma. Jezeli znajdzie taka i nikt juz tej strony nie uzywa, zwlania ja i zwraca 1.


Mechanizm postarzania stron

Za kazdym razem, gdy funkcja try_to_swap_out stwierdzi, ze strona nadaje sie byc moze do wymiany (nie jest zablokowana ani zarezerwowana) uruchamiany jest mechanizm postarzania stron. Wiek strony to liczba przechowywyana w tablicy ramek stron, mowiaca o tym, czy dana strona jest juz wystarczajaco dlugo nieuzywana, by mozna bylo usunac ja z pamieci glownej. W Linuxie zastosowano algorytm LFU z liniowym (a nie typowym - wykladniczym) postarzaniem stron. Wiek kazdej nowej strony pojawiajacej sie w pamieci glownej jest ustawiany na 17 (!). Za kazdym razem, gdy try_to_swap_out zauwazy, ze strona byla ostatnio uzywana przez proces (bit ACCESSED), lub strona byla modyfikowana (bit DIRTY) po raz pierwszy od sprowadzenia jej z dysku, wiek odpowiedniej ramki zostaje zmniejszony o 3. W przeciwnym przypadku wielkosc ta rosnie o 1. Gdy osiagnie 20 - strona jest na tyle stara, ze nalezy ja z pamieci usunac. Wiek strony w pamieci glownej nie moze nigdy spasc ponizej zera.


Demon wymiany

Demon wymiany (kswapd) to specjalny proces jadra inicjowany raz na poczatku dzialania systemu i sluzacy do realizacji wymiany stron z dyskiem przeprowadzanej w tle, wtedy gdy liczba wolnych stron w pamieci glownej staje sie bardzo niska. Dopoki ilosc wolnej pamieci utrzymuje sie na wysokim poziomie, dopoty demon wymiany pozostaje nieaktywny. Zmienia sie to wraz ze spadkiem tej wielkosci ponizej pewnej granicy oznaczanej free_pages_high. Wtedy kswapd raz na jakis czas (standardowo co 250 ms) wola kilka razy (typowo 4) funkcje try_to_free_page z parametrem wait==0. Jezeli liczba wolnych ramek spadnie ponizej free_pages_low kswapd wykonuje te czynnosc podczas kazdego tykniecia zegara systemowego. Czasem jednak i to nie wystarcza i ilosc wolnej pamieci spada do min_free_pages. W takim przypadku gdy zachodzi potrzeba przydzielenia nowej strony jadro bez udzialu demona wywoluje raz funkcje try_to_free_page z parametrem wait==1, aby zwolnic jedna ramke pamieci. Na maszynach z procesorem z rodziny Intela przyjeto nastepujace wielkosci poszczegolnych granic:

, gdzie M - ilosc pamieci RAM podlegajacej stronicowaniu. Przypominam, ze wielkosc strony na tym sprzecie to 4KB. Na innych maszynach wzor na min_free_pages moze byc nieco inny.


Bibliografia

Pliki zrodlowe Linuxa, glownie mm/vmscan.c


Autor: Jerzy Koroblewski