Przestrzeń adresowa procesu składa się z Regionów Pamięci, wyznaczających rozłączne przedziały adresów liniowych przydzielonych procesowi. Każdy Region Pamięci składa się z całkowitej liczby kolejnych stron pamięci, a więc i granice Regionów są całkowitymi wielokrotnościami wielkości strony (PAGE_SIZE, standardowo 4096). Każdy z tych Regionów opisywany jest przez strukturę typu struct vm_area_struct:
Jako że - jak już powiedzieliśmy - Regiony Pamięci są rozłączne, możemy naszkicować relacje między Deskryptorem Pamięci, Regionami Pamięci oraz Przestrzenią Adresową procesu w następujący sposób:
Jak widać, lista Regionów Pamięci jest posortowana rosnąco względem zakresów adresów
liniowych reprezentowanych przez jej elementy. Pomiędzy kolejnymi przedziałami adresów
istnieją przedziały wolne, nie przydzielone procesowi. Próba odwołania się przez
proces do ktoregoś z nieprzydzielonych mu adresów powoduje wyjątek błędu strony,
tzw. Page Fault.
Jak już wiemy, w jądrze (include/asm-i386/page.h) istnieje stała PAGE_OFFSET,
wyznaczająca adres, na którym kończy się przestrzeń adresowa procesu dostępna z
poziomu użytkownika, a zaczyna przestrzeń adresowa tegoż procesu dostępna tylko
z poziomu jądra. Najczęściej jest ona równa 0xC0000000, co oznacza
podział w stosunku 3/4 (3GB przestrzeni dla użytkownika, 1GB przestrzeni dla jądra).
Oprócz omówionych wcześniej flag związanych z pojedyńczymi stronami (flagi przechowywane w każdej pozycji Tablicy Stron i flagi przechowywane w polu flags każdego Deksryptora Strony), istnieją flagi związane z omawianymi Regionami Pamięci, czyli grupami stron. Są one przechowywane w polu vm_flags deskryptora Regionu Pamięci typu struct vm_area_struct. Używane są następujące flagi:
VM_READ | Strony można czytać |
VM_WRITE | Strony mogą być zapisywane |
VM_EXEC | Strony mogą być wykonywane |
VM_SHARED | Strony mogą być współdzielone przez kilka procesów |
VM_MAYREAD | Można ustawić flagę VM_READ |
VM_MAYWRITE | Można ustawić flagę VM_WRITE |
VM_MAYEXEC | Można ustawić flagę VM_EXEC |
VM_MAYSHARE | Można ustawić flagę VM_SHARED |
VM_GROWSDOWN | Region można rozszerzać w stronę dolnych adresów |
VM_GROWSUP | Region można rozszerzać w stronę górnych adresów |
VM_SHM | Strony są używane jako pamięć współdzielona IPC |
VM_DENYWRITE | Region odwzorowuje plik, do którego nie można pisać. Przy próbie zapisu zgłaszany jest błąd ETXTBSY |
VM_EXECUTABLE | Strony zawierają wykonywalny kod |
VM_LOCKED | Strony są zablokowane w pamięci RAM i nie można ich wymieniać |
VM_IO | Region odwzorowuje przestrzeń adresową I/O jakiegoś urządzenia |
VM_DONTCOPY | Nie powielać tego Regionu podczas wykonania funkcji fork() |
VM_DONTEXPAND | /* Cannot expand with mremap() */ |
VM_RESERVED | /* Don't unmap it from swap_out */ |
Dozwolone są wszystkie możliwe kombinacje powyższych flag - np. mozliwe jest
takie ustawienie, aby można było wykonywać strony danego Regionu Pamięci, ale
nie odczytywać. Aby efektywnie realizować ten mechanizm ochrony, związane z Regionem
Pamięci prawa muszą być powielone we wszystkich odpowiednich pozycjach tablic stron,
tak by sprawdzania ich dokonywała bezpośrednio sprzętowa jednostka stronicowania.
Praw dostępu do Regionu Pamięci jest jednak kilka - dokładnie cztery:
VM_READ, VM_WRITE, VM_EXEC, VM_SHARE.
Bitów ochrony strony w procesorach Intel jest zaś tylko dwa (flagi
User/Supervisor i Read/Write). W dodatku
flaga User/Supervisor musi zawsze być ustawiona, w przeciwnym
bowiem przypadku proces nigdy nie mógłby uzyskać dostępu do swojej własnej strony z poziomu
użytkownika.
Konieczne było zatem wprowadzenie pewnych ograniczeń:
Trzecim - oprócz rozłączności i posortowania - niezmiennikiem zachowywanym przez jądro przy operacjach na Przestrzeniach Adresowych procesów, jest łączenie sąsiadujących ze sobą Regionów Pamięci (takich, ze vm_start jednego jest równe vm_end drugiego), których prawa dostępu są takie same.