Następna Poprzednia Spis | Linux, jądro 2.4.7 |
struct zone_struct /* struktura opisująca strefę pamięci */ unsigned long free_pages; /* liczba wolnych stron */ /* liczba stron zakwalifikowanych do wymiany, które nie posiadają aktualnej kopii w obszarze wymiany */ unsigned long inactive_dirty_pages; /* lista stron zakwalifikowanych do wymiany, które można szybko zwolnić bez zapisywania w obszarze wymiany */ struct list_head inactive_clean_list; unsigned long inactive_clean_pages; /* liczba tych stron */ unsigned long pages_high, /* wymagana liczba wolnych stron */ pages_low, /* niska liczba wolnych stron*/ pages_min, /* minimalna liczba wolnych stron */Deskryptor strefy posiada pola
pages_high
, pages_low
i pages_min
- przechowywane tam wartości definiują wspomniany wcześniej minimalny próg wolnych stron w systemie. Pola te inicjowane są następująco:pages_min = rozmiar_strefy * współczynnik_zbalansowania gdzie współczynnik_zbalansowania wynosi: strefa "DMA" = 1/32 strefa "Normal" i "HighMem" = 1/128 pages_low = pages_min*2 pages_high = pages_min*3Jeśli zawartość strony pamięci zostanie uznana z pewnych powodów za chwilowo niepotrzebną, strona taka wędruje do jednej z list stron nieaktywnych. Jeśli zawartość strony jest unikatowa - nie mamy jej aktualnej kopii na dysku, to strona zostaje dodana do listy stron nieaktywnych zabrudzonych, na której początek wskazuje zmienna globalna
inactive_dirty_list
.
inactive_clean_list
deskryptora strefy, do której należy.
active_list
.
freepages
- struktura ta przechowuje trzy wartości: min, low, high równe sumie wartości odpowiednio pages_min, pages_low, pages_high wszystkich stref,memory_pressure
- zmienna ta mówi o aktualnym zapotrzebowaniu systemu na pamięć. Pomaga ona określić, jaką liczbą nieaktywnych (gotowych do zwolnienia) stron powinniśmy w danej chwili dysponować. o sekundę jest zmniejsza o 1/64 swojej wartości (wątek kswapd).
Na jakiej podstawie stwierdzić, czy strona przestała być 'aktywna' i powinna zostać usunięta z listy active_list
? Pomaga nam w tym pole age
struktury page
opisującej stronę.
Choć nazwa tego pola sugeruje, że symuluje ono wiek strony, w istocie nie jest to wiek w intuicyjnym tego słowa znaczeniu.
Co oznacza naprawdę, dowiemy się już niebawem.
Wiek nowej strony ustawiany jest na 2. Nigdy nie przekracza on wartości PAGE_AGE_MAX (tu: 64).
Manipulacje wiekiem stron dokonywane są za pośrednictwem funkcji refill_inactive_scan()
.
Wywołuje ją opisana w dalszej części pracy funkcja do_try_to_free_pages()
oraz co sekundę Kernel Swap Deamon.
Spójrzmy na kod jakże interesującej funkcji refill_inactive_scan()
:
refill_inactive_scan(unsigned int priority, int target) /* Parametry: priority - określa, jak wiele stron aktywnych mamy przejrzeć target - ile nieużywanych stron chcemy znaleźć */ dla każdej strony z active_list rób { /* Uaktualnij wiek strony */ jeśli strona ma ustawiony bit PG_referenced rób wiek_strony = min(wiek_strony+3, PAGE_AGE_MAX) wpp { wiek_strony = wiek_strony / 2; jeśli wiek_strony == 0 and liczba użytkowników korzystających ze strony == 0 rób przenieś stronę na listę nieaktywnych } Wyczyść bit PG_referenced strony jeśli strona nie została zdezaktywowana rób przenieś ją na koniec active_list jeśli przejrzano wystarczająco dużo stron lub target <> 0 i zdezaktywowano target stron rób wyjdź z pętli } return liczba_zdeaktywowanych_stron;Jak widzimy funkcja ta przegląda listę stron aktywnych. Jeśli ostatnio ktoś odwoływał się do strony, sprzętowo została ustawiona jej flaga
PG_referenced
. Wtedy wiek strony jest zwiększany. W przeciwnym przypadku zmniejsza się go.
Tak więc duża wartość pola age
oznacza, iż zawartość strony jest często wykorzystywana, co przemawia za tym, by jej nie wymieniać.
Zauważmy, że jeśli wiek strony maleje, to przebiega to wykładniczo, a jeśli rośnie, to liniowo, czyli asymptotycznie o wiele wolniej. Jak mniemam dzięki temu jeśli strona jest na active_list
oznacza to, iż odwołania do niej są naprawdę częste i wymiana jej najprawdopodobniej nie ma sensu.
Strona poddana operacji aktualizacji wieku wędruje na koniec listy stron aktywnych. Dzięki temu wiek każdej strony aktualizowany jest z podobną częstotliwością.
Mechanizm postarzania stron wspiera i udoskonala algorytm zastępowania stron używanych najdawniej (ang. Least Recently Used) dzięki zapamiętywaniu częstości odwołań do strony w ostatnim czasie.
Gdy przyjdą ciężkie czasy i liczba wolnych stron pamięci RAM w systemie spadnie poniżej magicznego poziomu, funkcja
do_try_to_free_pages()
wkracza do akcji. Wywołują ją: funkcja __alloc_pages
zaspakajająca żądania systemu bliźniaków (posrednio, przez try_to_free_pages()
) oraz wątek kswapd
.
Funkcja ma tylko jeden cel: zwolnienie najmniej używanych stron. Mężnie giermkuje jej w tym mechanizm wymiany.
do_try_to_free_pages(unsigned int gfp_mask, int user) jeśli liczba wolnych stron + liczba nieaktywnych niezabrudzonych stron < freepages.high rób /* Wyczyść zabrudzone strony - jeśli trzeba wymień je na dysk, i przenieś je do listy inactive_clean_list */ page_launder(gfp_mask, user); /* spróbuj zwolnić strony pamięci podręcznej pozycji katalogów (przyspieszającej tłumaczenie ścieżki dostępu do pliku) Po usunięciu pewnych obiektów pozycji katalogów może się okazać, że odpowiadające im obiekty reprezentujące i-węzły przestały być używane - - próbujemy więc zwolnić pamięć podręczną, w której są one przechowywane. */ shrink_dcache_memory(DEF_PRIORITY, gfp_mask); shrink_icache_memory(DEF_PRIORITY, gfp_mask); } jeśli liczba wolnych oraz nieaktywnych stron jest zbyt niska rób /* przesuń jak najwięcej stron z listy stron aktywnych do listy stron nieaktywnych */ refill_inactive(gfp_mask, user); /* Spróbuj odzyskać strony z pamięci podręcznej alokatora płytowego*/ kmem_cache_reap(gfp_mask);
Funkcją odpowiadającą za czyszczenie 'zabrudzonych' stron i przygotowanie ich do zwolnienia do systemu bliźniaków jest
page_launder(int gfp_mask, int sync)
Godnym podkreślenia jest fakt, że to właśnie ta funkcja jest bezpośrednio odpowiedzialna za inicjację procesu wymienienia zawartości stron na dysk.
Działa ona według następującego algorytmu:
Funkcja
Funkcja
Aby przejrzeć pozycje Tablicy Stron poszczególnych procesów, wywołuje się szereg funkcji pomocniczych przeglądając w głąb struktury opisujące zajmowaną przez procesy pamięć. Przegląda się:
Jeśli już chcemy odbierać jakiemuś procesowi strony, to staramy się zwolnić ok. 1/32 stron, które posiada, ale nie mniej niż 8.
Zarówno deskryptory przestrzeni adresowej jak i regiony pamięci procesu przegląda się zaczynając od następnika elementu przeglądanego ostatnim razem, dzięki czemu każdy proces i każdy jego region pamięci jest krzywdzony na równi.
Do funkcji
Funkcja
Wymienione mogą być tylko te strony, dla których opisująca je pozycja Tablicy Stron ma wyczyszczoną flagę
Źródła omawianych tutaj struktur i funkcji można znaleźć w plikach:
inactive_dirty_list
. Jeśli strona używana jest jako bufor, spróbuj go usunąć. Jeśli się to uda, przenieś stronę do listy inactive_clear_list
lub zwolnij ją.
Jeśli nadal liczba wolnych i nieaktywnych niezabrudzonych stron jest zbyt mała:
bdflush()
- wątek jądra, który wyczyści pewną liczbę 'brudnych' buforów przechowywanych w pamięci zapisując je na dysku.inactive_dirty_list
:
strona zabrudzona? -> asynchronicznie przenieś ją do obszaru wymiany.
Zwróć liczbę stron przeniesionych do inactive_clean_list
.
2.5 Znajdowanie stron najmniej potrzebnych
refill_inactive()
wywoływana jest przez do_try_to_free_pages()
. Jej zadaniem jest
odebranie działającym w systemie procesom najmniej/najdawniej używanych stron, a także uaktualnienie wieku stron.
Algorytm działania funkcji jest następujący:
refill_inactive
jeśli działamy na zlecenie procesu użytkownika rób {
/* wymieniane strony staramy się zbierać w pakiety
o wielkości 2^page_cluster. Zmienna ta, w zależności od liczby
wszystkich dostępnych stron w pamięci ustawiana jest na 2,3 lub 4. */
ile_zwolnić = 2^page_cluster;
liczba_prób = 6;
} wpp {
ile_zwolnić = aktualny niedobór;
liczba_prób = 2^6;
}
powtarzaj liczba_prób razy {
/* spróbuj odebrać jakieś strony działającym procesom */
swap_out(DEF_PRIORITY, gfp_mask);
/* aktualizuj wiek stron, wyłapując nieużywane */
refill_inactive_scan(DEF_PRIORITY, ile_jeszcze_zwolnić);
jeśli zaspokojono niedobór stron rób
return
}
Jak widzimy dokonuje ona serii prób znalezienia stron nieaktywnych, starając się zaspokoić niedobór, który powstał w systemie. W tym celu próbuje odebrać jakieś strony działającym procesom, a także aktualizuje wiek stron, wyłapując w ten sposób najmniej nieużywane.
2.6 Odbieranie stron procesom.
przegląda strony działających w systemie procesów, w poszukiwaniu tych, które można zwolnić lub wymienić.
Poszukiwania rozpoczynają się zawsze od tego procesu, który spowodował, że trzeba zwolnić pewne strony pamięci, gdyż zażądał alokacji nowych.void swap_out(unsigned int priority, int gfp_mask)
Liczba procesów, którym mają być odebrane strony pamięci zależy wykładniczo od parametru priority
.
listę deskryptorów przestrzeni adresowych procesów
--->
swap_out()
regiony pamięci procesu
--->
swap_out_mm()
pozycje Globalnego Katalogu Stron danego regionu
--->
swap_out_vma()
pozycje Pośredniego Katalogu Stron
--->
swap_out_pgd()
pozycje Tablicy Stron
--->
swap_out_pmd()
każdą Stronę z danej Tablicy Stron
--->
try_to_swap_out()
try_to_swap_out()
przekazywane są tylko strony, których wymiana nie jest zabroniona (zgaszony bit PG_reserved)
try_to_swap_out()
określa, czy daną stronę pamięci można zapisać w obszarze wymiany.
Jeśli stronę można zwolnić bez zapisywania w obszarze wymiany, jest ona zwracana do systemu bliźniaków.
W starszych wersjach jądra, jeśli strona nadawała się do wymiany, funkcja dokonywała fizycznej wymiany. Obecnie tego nie robi, a jedynie przenosi ją na listę inactive_dirty_list
, umożliwiając wymienienie jej w przyszłości funkcji page_launder()
.
_PAGE_BIT_ACCESSED
. Ponieważ bit ten jest ustawiany przez jednostkę stronnicowania procesora przy każdym dostępie do strony, strona może być wymieniona tylko wtedy, gdy nie była wykorzystywana od poprzedniego wywołania na niej funkcji try_to_swap_out()
. Jest to uproszczona wersja algorytmu zastępowania stron używanych najdawniej (ang. Least Recently Used) (algorytm drugiej szansy).
try_to_swap_out(mm, vma, address, page_table, page)
/* Pojęcie 'strona' używane poniżej tyczy się strony, którą chcemy wymienić,
a którą wyznaczają przekazywane funkcji parametry */
jeśli test_and_clear(flaga Accessed strony) rób {
wiek_strony = min(wiek_strony+3, PAGE_AGE_MAX)
return
}
jeśli strona zablokowana (flaga PG_locked) rób
return
jeśli strona należy do pamięci podręcznej wymiany lub
strona ma wyczyszczony bit PG_dirty lub
strona odwzorowuje plik rób {
/* stronę można zdezaktywować bez zapisywania w obszarze wymiany */
jeśli wiek_strony == 0 rób
przenieś ją do listy inactive_dirty_list
jeśli liczba użytkowników korzystających ze strony == 0 rób
zwróć stronę do systemu bliźniaków (__free_pages_ok)
} wpp { /* Mamy do czynienia z 'zabrudzoną', wymienialną stroną */
entry = get_swap_page();/* znajdź wolny slot w jednym z obszarów wymiany */
jeśli znaleziono wolny slot rób
Zapisz identyfikator wymienionej strony do pozycji Tablicy Stron
/* Zapamiętaj w pamięci podręcznej wymiany parę (page,entry) */
add_to_swap_cache(page, entry);
Oznacz stronę jako 'brudną'
jeśli wiek_strony == 0 rób
przenieś ją do listy inactive_dirty_list
}
Warto też zwrócić uwagę na to, iż strony, które mają zgaszoną flagę PG_dirty - tzw. bit modyfikacji, wędrują na listę
inactive_dirty_list
bez przydzielenia im slotu w obszarze wymiany. Flaga ta jest bowiem ustawiana sprzętowo w momencie modyfikacji zawartości strony. Gdy jest zgaszona oznacza to, iż posiadamy na dysku kopię rozpatrywanej strony, więc nie trzeba jej wymieniać.
Nie wymieniamy też stron odwzorowujących pliki, bo odwzorowywany plik to przecież kopia strony.
2.7 Zmiany w stosunku do poprzednich wersji jądra
try_to_swap_out()
po znalezieniu strony nadającej się do wymiany dokonywała fizycznego transferu. Teraz jedynie znajduje wolny slot w obszarze wymiany, zaś sama wymiana jest inicjowana przez funkcję page_launder()
kreclaimd
, dawniej zaś robiła to głównie funkcja try_to_swap_out()
zaraz po wymianie strony na dysk.2.8 Źródła
mm/vmscan.c
include/linux/mmzone.h
mm/swap.c
Następna
Poprzednia
Spis
Autor: Tomasz Pylak, 2001