[OSDB] Memory Manager Cz.1

Karol Gasiński - MrKaktus v. 2.0
1.0 Memory Manager
1.1 – Ogólny zarys 1.2 – Część sprzętowa
W przyszłości ukażą się też:
1.3 – Zarządzanie niskiego poziomu 1.3.1 – Ochrona niskiego poziomu 1.4 – Wirtualna powłoka procesu 1.5 – Pamięć wirtualna 1.6 – Przestrzenie współdzielone 1.6.1 – Współdzielenie z Systemem 1.6.2 – Współdzielenie z innymi aplikacjami 1.7 – Zwiększenie efektywności
1.8 – Bibliografia rozdziału
1.0 Memory Manager
1.1 Ogólny zarys
W systemie wieloprocesowym niezbędny jest sposób sprawiedliwego podziału zasobów pomiędzy wszystkimi procesami. Ponieważ najbardziej znaczącym zasobem jest pamięć operacyjna, system musi dostarczyć odpowiednie procedury umożliwiające jej efektywne zarządzanie. Procedury te zebrane razem wraz z używanymi przez nie strukturami danych nazywamy Menedżerem Pamięci (w skrócie z angielskiego MM).
Ze względu na architekturę komputerów klasy x86 (zwanej też IA-32) zarządzanie pamięcią odbywa się na dwa różne sposoby. Część mechanizmów przejmuje warstwa sprzętowa (procesor), część jest wykonywana przez System Operacyjny (w dalszej części będę stosował zamiennie z OS). Część obsługiwana przez OS często jest dodatkowo dzielona na tak zwaną część podstawową (niskiego poziomu) i część wysoką (wirtualną pamięć procesu). Podsumowując MM dzielimy na:
- część sprzętową
- część systemową niskiego poziomu
- wirtualną pamięć procesu
1.2 Część sprzętowa
Procesor umożliwia systemowi zarządzanie pamięcią RAM za pomocą dwóch metod: Stronicowania i Segmentacji. Twórca systemu może samemu wybrać, którą z nich (lub obie) ma zamiar stosować. Ponieważ jednak już teraz w najnowszych procesorach nie implementuje się Segmentacji kurs ten oprę na mechanizmie Stronicowania.
Proces Stronicowania polega na podzieleniu całej pamięci operacyjnej na bloki o identycznym rozmiarze. Bloki te następują po sobie i nie mogą zmieniać swojego położenia w RAM, co oznacza, że ich adres jest stały (patrz rys 1.0). Bloki te nazywamy ramkami. W architekturze IA-32 mamy do wyboru bloki o rozmiarze 4KB lub 4MB a w rozszerzonej wersji IA-32e także 2MB. Omówimy ramki 4KB, ponieważ są najczęściej stosowane i wykazują największą efektywność. Każdy proces podczas inicjacji otrzymuje pamięć wirtualną, którą postrzega jako prawdziwą pamięć operacyjną, w której znajduje się tylko on. Pamięć tę dzielimy z kolei na tak zwane strony. Strony tak samo jak ramki są identycznych rozmiarów i są ułożone jedna po drugiej. Rozmiar stron jest identyczny jak rozmiar ramek, a to dlatego, że konkretnej stronie w pamięci wirtualnej przypisuje się ramkę w prawdziwej pamięci RAM. Wszystkie operacje wykonywane przez proces w miejscu danej strony w rzeczywistości wykonywane są na przypisanej jej ramce. Co istotne, proces nie widzi podziału swojej pamięci na strony lecz postrzega ją jako ciągły blok (patrz rys 1.1).
Jak widać to że strony są ciągłe w wirtualnej pamięci, nie oznacza że ramki których używają muszą być ułożone po kolei. Ramka X-tej strony może się znajdować za ramką strony X+1. W ten sposób tworzenie wirtualnej pamięci procesu polega na alokacji kolejnych wolnych ramek, a jej zmienianie nie przysparza problemu. Wiemy, że stronom przypisane są ramki. Aby więc opisać atrybuty danej strony procesu, stosuje się czterobajtowy blok zawierający wszystkie niezbędne informacje. Oto jego struktura:
Pole - AFS - Pole to przechowuje 20 starszych bitów adresu ramki, która jest przypisana do tej strony. Pozostałe 12 bitów nie jest potrzebnych do ustalenia adresu ramki, ponieważ adresy wszystkich ramek w tym przypadku są wielokrotnościami 4KB, więc ich adresy 12 niskich bitów mają zawsze wyzerowane. Pole to możesz także utożsamiać z numerem ramki. Pole - W - Te trzy bity są udostępnione dla twórcy menedżera pamięci i nie mają określonego przeznaczenia. Bit - D - Ustawiony bit oznacza, że dokonano zapisu w ramce tej strony. Bit - A - Ustawiony bit oznacza, że ramka tej strony była przynajmniej raz odczytywana. Bit - U/S - Wskazuje czy dana strona należy do użytkownika (bit = 1) czy jest ona systemowa (bit = 0). Bit - R/W - Określa czy ramka danej strony jest do odczytu i zapisu (bit = 1) czy tylko do odczytu (bit = 0). Bit - O - Określa czy ramka przypisana danej stronie znajduje się w pamięci czy też jej zawartość została przeniesiona na dysk twardy (dokładniejszy opis tego mechanizmu znajdziesz w rozdziale Pamięć Wirtualna).
UWAGA: Bity oznaczone jako zarezerwowane przez Intela nie zostały opisane w tym kursie ze względu na mniejsze znaczenie, w rzeczywistości są one jednak zdefiniowane a sposób ich użycia opisany jest w dokumentacji Intela (patrz bibliografia rozdziału).
Takie bloki uszeregowane w kolejności odpowiadającej kolejności stron tworzą Tablice Stron (Page Table - PT) a je same nazywamy też PTE (od angielskiego, Page Table Entry). Każda tablica stron mieści 1024 bloki opisu stron, a ponieważ każdy z nich ma rozmiar czterech bajtów, więc rozmiar jednej PT wynosi 4KB. Ta zbieżność z rozmiarem ramki jest celowa, ponieważ tablice stron także muszą znajdować się w pamięci operacyjnej a to oznacza, że muszą być traktowane jak zwyczajne ramki z danymi.
Zauważmy, że pamięć operacyjna komputerów klasy x86 standardowo jest obsługiwana 32 bitową szyną adresową, co oznacza, że możemy zaadresować do 4GB RAM (2^32 bajtów). Ponieważ używamy ramek o rozmiarze 4KB, więc maksymalnie możemy ich mieć 2^32 / 2^12 = 2^20 = 1048576 ramek. Jak widać jest to o wiele więcej niż 1024, ilość jaka mieści się w jednej PT, a uściślając jest to 1024 * 1024 ramek, czyli aby je wszystkie opisać potrzebowalibyśmy 1024 tablic stron. Potrzebujemy, więc czegoś, co będzie przechowywało informacje o adresach wszystkich tablic stron.
Rozwiązanie tego problemu nasuwa się samoistnie. Wystarczy utworzyć tablicę o maksymalnie 1024 nagłówkach opisujących położenie tablic stron w pamięci (patrz rys. 1.3). Tablice tę nazwiemy katalogiem stron (od angielskiego Page Directory - PD) a nagłówki te będziemy nazywać w skrócie PDE (z angielskiego, Page Directory Entry). PDE są także cztero-kilobajtowe a ich struktura wygląda następująco:
Pole - AFTS - Pole to przechowuje 20 starszych bitów adresu ramki, która przechowuje daną Tablice Stron. Pozostałe 12 niskich bitów oczywiście nie jest nam potrzebnych do ustalenia adresu PT (jak już o tym wcześniej wspomniałem). Pole - W - Te trzy bity są udostępnione dla twórcy menedżera pamięci i nie mają określonego przeznaczenia. Bit - G - Bit ten jest ignorowany. Ma on znaczenie jedynie w bloku opisu strony (PTE). Bit - PS - Jest to flaga rozmiaru używanych stron (PS – Page Size). Jeżeli ustawimy ją na 1 zmienia się całkowicie znaczenie całego PDE. W takim wypadku nie wskazuje on do Tablicy Stron, ale bezpośrednio na 4MBajtową ramkę. Ponieważ omawiam tu tylko stronicowanie przy użyciu 4KB ramek po więcej informacji zajrzyj do dokumentacji Intela (patrz bibliografia rozdziału). Bit - A- Ustawiony bit oznacza, że ramka tej strony była przynajmniej raz odczytywana. Bity - PCD, PWT - Bity te nie będą opisane w tej wersji kursu, ponieważ są one rozszerzeniem podstawowego mechanizmu stronicowania. Bit - U/S - Wskazuje czy strony opisywane we wskazywanej Tablicy Stron należą do użytkownika (bit = 1) czy są one systemowe (bit = 0). Bit - R/W- Określa czy ramki stron opisywanych we wskazywanej Tablicy Stron są do odczytu i zapisu (bit = 1) czy tylko do odczytu (bit = 0). Bit - O - Określa czy ramka przypisana danej Tablicy Stron znajduje się w pamięci czy też jej zawartość została przeniesiona na dysk twardy (dokładniejszy opis tego mechanizmu znajdziesz w rozdziale Pamięć Wirtualna).
Mamy już prawie kompletny mechanizm, dzięki któremu możemy tworzyć wirtualne pamięci dla procesów. Pozostaje jeszcze tylko jedno „ale”. Skąd procesor wie gdzie ma szukać katalogu stron w pamięci? Do tego właśnie służy rejestr CR3. Umieszczamy w nim fizyczny adres PD w pamięci (adres w wirtualnej pamięci procesu nazywamy adresem wirtualnym).
Podsumowując, aby stworzyć wirtualną pamięć procesu należy:
- utworzyć Katalog Stron procesu
- zarezerwować odpowiednią ilość ramek pod Tablice Stron procesów (jedna Tablica Stron opisuje do 4MB pamięci)
- zapisać odpowiednie wpisy dla tych PT w Katalogu Stron
- zarezerwować ramki pod opisywane strony dla każdej z PT
- zapisać odpowiednie wpisy dla tych ramek we wszystkich utworzonych PT
Następnie, aby uruchomić WPP należy:
- zapisać fizyczny adres do jej PD w rejestrze CR3
- ustawić 31 bit w rejestrze CR0 (odpowiedzialny za włączenie mechanizmu stronicowania)
Na pewno po przeczytaniu powyższych kroków zaintrygowało cię stwierdzenie „zarezerwować odpowiednią ilość ramek”. Tym właśnie zajmuje się część systemowa niskiego poziomu, zwana też lokatorem pamięci niskiego poziomu (od tego że zajmuje się tylko alokacją i zwalnianiem pojedynczych ramek). Ale o tym jak on działa dowiesz się w podroździale 1.3.
1.3 Zarządzanie niskiego poziomu
W tym miejscu znajdować się będzie dalszy ciąg tego kursu, na razie jednak dostępne są tylko notatki do tego podrozdziału zatytułowane: „Tutorial Notes 1 – MM Tablice Bitowe” (na stronie www.areoos.com/osdevpl/). Zamierzam kontynuować ten kurs i rozbudowywać go także o kolejne rozdziały poświęcone zarządzaniu procesami, trybowi chronionemu itd. Gdy uzbiera się tego znaczniejsza ilość postaram się przeredagować wszystko do postaci poradnika przydatnego hobbistom przy pisaniu ich systemów.
Do tej pory korzystałem z dokumentacji Intela: „Inlet Architecture Software Developers Manual”
Wszystkie uwagi odnośnie kursu mile widziane – mrkaktus@wp.pl
Aby dodawać komentarze musisz być zalogowany!
