Poznaliśmy algorytmy i struktury danych realizujące koncepcję
autonomicznej i wyabstrachowanej przestrzeni adresowej procesu użytkownika.
Pamięć wirtualna jest, obok wieloprocesowości, największym krokiem uczynionym
na polu systemów operacyjnych od czasu epoki DOS.
Jak widzielismy,
jeden Region Pamięci jest przydzielony dla jednego spójnego obszaru pamięci
ze spójnymi prawami dostępu i tym samym 'backing store' - 'zapleczem', czyli np.
urządzeniem lub plikiem. Stąd np. załadowanie biblioteki powoduje, w ogólnym
przypadku, utworzenie co najmniej dwóch Regionow - jednego dla kodu wykonywalnego
tej biblioteki, i drugiego dla jej danych.
Struktura Regionów Pamięci bieżącego procesu odwzorowana jest w filesystemie
proc - pisząc np.
Nawet bez wgłębiania się w specyfikację pliku /proc/self/maps widać,
że większość z używanych przez proces cat Regionów jest READ ONLY,
gdyż zajmowane są przez dzielone biblioteki. Zresztą te same, które można
zobaczyć oglądając ten sam plik /proc/self/maps komendą "F3" w programie Midnight Commander.
Dlaczego właściwie utrzymywanie liczby Regionów Pamięci procesu na niskim
poziomie jest takie ważne?
Po pierwsze, każdy Region Pamięci wymaga przechowywania pewnej dodatkowej ilości
danych w strukturach jądra (vm_area_struct).
Po drugie i najważniejsze, jądro, jak łatwo się domyslić, operuje na
listach/drzewach Regionów Pamięci co chwila, i to nie tylko przy alokacji
czy zwalnianiu obszarow pamieci. Np. efektywność całej procedury obsługi
wyjątku błędu strony opiera się w dużym stopniu na efektywności wyszukiwania
Regionów zawierających (sąsiednich do) dany adres liniowy
(find_vma()). To może się wydawać nieco przesadzone, gdy pomyśli się
o logarytmicznych drzewach AVL, nie zapominajmy jednak że sprawa dotyczy pojedyńczego
odwołania do pamięci, a więc już przy np. tysiącu Regionów na jedno odwołanie do
pamięci powodujące błąd stronicowania (a więc np. przy próbie zmiany wartości
zmiennej odziedziczonej po rodzicu, sięgnięcia do swojej własnej zaalokowanej
pamięci lub kolejnym wywołaniu rekurencyjnym) przypada 10 odwołań spędzonych
na samo wyszukiwanie Regionu. To sporo.
Właśnie poruszony przed chwilą problem łączenia Regionów Pamięci jest obecnie
"na topie" jeśli chodzi o rozwój i patchowanie bieżącej wersji jądra. Mianowicie
nie tak dawno zostało wykryte, że nie wszystko działa do końca tak jak powinno -
mianowicie Chris Wedgewood, poirytowany szybkoscią działania swojej przeglądarki
Mozilla (Netscape), zauważył, że zużywała ona ponad 5000 Regionów! Co więcej,
inne aplikacje (zwłaszcza GNOME) zachowywały się podobnie. Nastąpiła spora
konfuzja, gdy okazało się, że jest mnóstwo sąsiadujących ze sobą Regionów
'anonymous', które mogłyby być połączone w jeden. Na dzień dzisiejszy nie wiadomo
dokładnie, jakie są przyczyny takiego stanu rzeczy. Najbardziej prawdopodobna hipoteza
wskazuje na biblioteczną funkcję malloc(), której algorytm przydzielania
pamięci jest dosyć skomplikowany, i przewiduje m.in. alokowanie pewnych
adresów, następnie zwalnianie niektórych z nich, a na koniec dokonywanie
pewnych zmian w prawach dostępu do pamięci. I z tego właśnie wynika problem -
przy zmianie praw dostępu do fragmentu pamięci, stanowiącego tylko część
danego Regionu Pamięci, jądro musi podzielić ten region na dwa. Następnie
powinno spróbować dołączyć tę "nowszą" część do sąsiedniego Regionu, sprawdzając
czy prawa dostępu na to pozwalaja. Niestety, w obecnej wersji jądra nie jest
to wykonywane. W rezultacie pewne ciągi alokacji powodują tzw. "eksplozję VMA",
czyli szybki wzrost liczby Regionów Pamieci procesu.
Nie wiadomo czy i jak ten problem zostanie rozwiązany, jako że wielu spośród
programistów tworzących Linuxa wydaje się go bagatelizować, zrzucając całą winę
na bibliotekę C. Nie jest to zresztą pierwszy przypadek nieporozumień na linii
jądro - biblioteka C...