4. Procedura obsługi błędu strony, czyli czego szary programista zepsuć nie może
Wyjątek "Page Fault", czyli tzw. błąd stronicowania, jest zgłaszany przez
procesor wtedy, kiedy nastąpi nieprawidłowe odwołanie do jakiegoś liniowego
adresu pamięci. Nieprawidłowe oznacza, że strona wyznaczana przez dany adres
nie jest obecna (wyczyszczony bit Present), lub też charakter dostępu
do niej jest niezgodny z prawami dostępu ustawionymi na odpowiednich pozycjach
Katalogu lub Tablicy Stron (bity Read/Write i User/Supervisor).
Funkcją obsługująca błędy stronicowania jest do_page_fault(),
zaimplementowana w arch/i386/mm/fault.c. Diagram przepływu kontroli
wygląda w niej następująco:

Powyższy schemat domaga się kilku wyjaśnien:
-
GOOD_AREA, BAD_AREA, NO_CONTEXT,
DO_SIGBUS, OUT_OF_MEMORY - etykiety te oznaczają
poszczególne sekcje goto w kodzie źródłowym w C. Dzięki swoim
znaczącym nazwom umożliwiają łatwiejsze zrozumienie kodu.
-
Skąd funkcja do_page_fault() wie, w jakim modzie (użytkownik/jądro) był
procesor, oraz jakiego rodzaju dostęp (czytanie/pisanie) spowodował wyjątek?
Tę sprawę załatwiają niskopoziomowe routiny assemblera z arch/i386/kernel/entry.S.
Odkładają one na stos kod błędu, zawierający 3 informacje:
- Czy strona nie została znaleziona, czy też może wystąpił błąd ochrony
- Czy dostęp miał charakter zapisu czy odczytu
- Czy dostęp nastąpił w modzie użytkownika czy jądra
Oprócz tego na stos odkładana jest zawartość wszystkich rejestrów procesora.
-
Co oznacza "dostęp do stosu poniżej ESP"?
Podstawowym zyskiem z wprowadzenia pamięci wirtualnej/stronicowania jest
możliwość rozszerzania przestrzeni adresowej procesu w miarę potrzeby, bez
konieczności alokowania z góry ("na sztywno") takiej ilości pamięci dla procesu,
która na pewno mu wystarczy. Dotyczy to również stosu - sytuacja w której proces
próbuje odłożyć coś na kawałek stosu, który nie jest jeszcze fizycznie zaalokowany,
jest całkowicie poprawna; należy ją tylko poprawnie obsłużyć, powiększając stos.
Jak wiadomo, stos rośnie w dół. Może więc zdarzyć się tak, że następuje
odwołanie do stosu (push) trochę poniżej jego wierzchołka:
address < ESP . "+ 32" na diagramie bierze się stąd, że niektóre
instrukcje, takie jak pusha, zmniejszają zawartość rejestru ESP
dopiero po odlożeniu danych na stos.
-
Stronicowanie na żądanie - oznacza właśnie możliwość alokowania stron
dla procesu "z opóźnieniem", dopiero wtedy kiedy faktycznie będzie chciał z nich
skorzystać. Alokując Region Pamięci można wymusić natychmiastową fizyczną alokację stron przy
użyciu flagi MAP_LOCKED (w wywołaniu funkcji do_mmap)
-
Kopiowanie przy zapisie - polega na współdzieleniu stron pamięci przez
proces rodzicielski i proces potomny, i powielaniu ich dopiero przy próbie ich
modyfikacji przez któryś z nich. Jest to spora oszczędność, zwłaszcza w
(częstych skądinąd) sytuacjach, gdy bezpośrednio po wykonaniu fork()
proces potomny wykonuje którąś z funkcji exec(), całkowicie niszcząc
swój dotychczasowy kod i dane.