Do spisu
tresci tematu 4
4.3.3 Bledy braku i ochrony strony
Spis tresci
Wprowadzenie
Obsluga bledow stron wespol z mechanizmami wymiany stron (ang. swap out -
przepisanie do urzadzenia wymiany; swap in - pobranie z urzadzenia wymiany) odpowiadaja za realizacje
stronicowania - podstawowej strategii zarzadzania pamiecia w Linuxie. Termin blad
strony (ang. page fault) zostal nieco niefortunnie przetlumaczony. W praktyce powinien byc rozumiany jako
programowo kontrolowany dostep do pamieci wirtualnej (tzn. w jej pelnym logicznym zakresie realizowanym przez
system). Zatem zazwyczaj nie jest to blad w sensie niepoprawnie dzialajaacego procesu, a jedynie w sensie braku
mozliwosci zrealizowania przez CPU (a dokladniej MMU - memory management unit - sprzetowy mechanizm translacji
adresow i kontroli praw dostepu do pamieci) zadania bezposredniego dostepu do pamieci fizycznej. Bledy stron sa bowiem
zglaszane przez MMU w postaci przerwan w pewnych scisle okreslonych
sytuacjach . Bledy strony sa bledami dwojakiego rodzaju: braku strony oraz
naruszenia prawa dostepu do strony. Ponadto kazdy z nich moze miec wiele przyczyn zaleznych
zarowno od szeroko pojetego stanu systemu jak rowniez typu i mozliwosci sprzetu. Poniewaz
jednak w obu przypadkach
postepowanie wstepne jest bardzo podobne, w toku prac nad wersja 2.1 przewiduje sie
polaczenie dwoch oddzielnych funkcji do_no_page() i
do_wp_page() w jedna uniwersalna handle_mm_fault().
Przy obsludze bledow stron linux nie wykorzystuje zadnych specjalnych struktur poza
opisanymi przy okazji wyjasniania
dzialania strategii stronicowania.
Czesc zalezna od sprzetu
Blad strony.
Na najnizszym poziomie, funkcja niemal bezposrednio obslugujaca przerwanie bledu strony jest
do_page_fault(struct pt_regs *regs, unsigned long error_code)
Jedynie fragmenty kodu w asemblerze posrednicza w przygotowaniu argumentow dla funkcji po wystapieniu przerwania.
- Argumenty to wskaznik do kopii zawartosci rejestrow w momencie powstania wyjatku oraz kod bledu. Pierwsze trzy bity
kodu bledu informuja czy nastapil blad braku strony, czy ochrony strony,
czy dotyczy on odczytu, czy zapisu i czy w momencie wystapienia procesor byl w trybie uzytkownika, czy w trybie jadra.
- Na wstepie sprawdzane jest czy adres, ktory spowodowal blad, nalezy do jakiegos z istniejacych obszarow
vm_area.
- Jezeli nie znaleziono takiego obszaru (badz jezeli nie spelnione sa pewne warunki wewnatrz znalezionego obszaru
dotyczace glownie stosu; np. spojnosc stosu lub brak miejsca na stosie) do procesu wysylany jest sygnal SIGSEGV.
Sygnal ten jest rowniez wysylany do procesu juz w tym momencie jezeli nastapilo rzeczywiste naruszenie
ochrony przed zapisem.
- W przeciwnym razie dalej determinowane jest czy blad dotyczy ochrony, czy tez
braku strony i wolane sa odpowiednie funkcje.
Szczegoly techniczne.
Warto byloby wspomniec o tym, ze w zaleznosci od architektury struktura drzewiasta
slownika adresow stron moze byc
mniej lub bardziej rozbudowana (co ma wplyw na efektywnosc sprzetowych i programowych
algorytmow realizujacych
strategie stronicowania). W szczegolnosci Linux jest w stanie obslugiwac trzy poziomy zagniezdzen; w Intelu sprzet
zapewnia realizacje dwoch poziomow (podobnie MIPS i PPC), Alpha umozliwia trzypoziomowe zagniezdzanie, natomiast
procesory Motoroli udostepniaja az szesc poziomow zagniezdzen, podczas gdy w procesorach Sparc istnieje nawet mozliwosc
dynamicznej kontroli glebokosci drzewa. W przypadku mplementacji
Linuxa/AXP,
Linuxa/68k,
Linuxa/SPARC w pelni wykorzystane sa mozliwosci
trzypoziomowego zagniezdzania.
Ponadto trzeba by jeszcze zaznaczyc, ze w wiekszosci dokumentacji, opis pewnych stalych sprzetowych (a w konsekwencji
rowniez implementacji pewnych
fragmentow kodu) prawdziwy jest tylko dla procesora Intel. Np. w MMU Motoroli istnieja bity odpowiadajace za page write
thru, page cache disabe, i jeszcze kilka innych flag okreslajacych dzialanie cache'a, sa rowniez zaimplementowane w
linuxie/68k metody to wykorzystujace; na Alphie
natomiast jest od groma bitow okreslajacych prawa dostepu
(odczyt/zapis/wykonywane/ostatno odczytana/ostatnio zapisana...) i rowniez mozna sie doszukac wykorzystania tych
mozliwosci w Linuxie/AXP.
Czesc niezalezna od sprzetu
Struktury.
Slowo struktury jest tu nieco na wyrost, jednak jest kilka zmiennych globalnych, o ktorych warto wspomniec przy okazji
tego tematu. Zmienne te dotycza statystyk systemowych zwiazanych z istnieniem procesu i znajduja sie w strukturze
task_struct oraz
mm_struct.
Brak strony.
Generalnie blad ten pojawia sie w momencie gdy zadamy dostepu do strony pamieci wirtualnej, ktorej zawartosc nie
istnieje w pamieci RAM adresowanej przez CPU. Obejmuje to zarowno przypadek gdy strona moze byc przeczytana z
urzadzenia wymiany jak i fakt odwolania sie do strony, ktora w danym momencie wogole nie jest przydzielona. Jednak w
zaleznosci od mozliwosci sprzetu (wiekszosc architektur RISCowych umozliwia przechowywanie dosyc ograniczonego zestawu
informacji dotyczacych przyczyn wystapienia bledu strony. Nie nalezy jednak wyciagac z tego faktu mylnych wnioskow iz
procesory Intela posiadaja wiecej cech charakterystycznych dla modelu RISC) przerwanie braku strony moze byc rowniez
wykorzystane w celu implementacji brakujacych bitow takich jak wiek strony, rozne tryby obslugi sprzetowej pamieci
podrecznej (page write thru, page cache disable...), czy nawet naruszenie ochrony strony.
Funkcja odpowiedzialna za obsluge przerwania braku strony jest
void do_no_page(struct task_struct * tsk, struct vm_area_struct * vma,
unsigned long address, int write_access)
- Przyjmowane sa argumenty z informacjami dotyczacymi bierzacego procesu, adresu, ktory spowodowal przerwanie, struktury
vm_area, do ktorej nalezy adres oraz flage informujaca, czy dane maja byc czytane, czy pisane pod dany adres
(umozliwia to efektywne wolanie metod odpowiedzialnych za kopiowanie przy zapisie, bez potrzeby
prowokowania zbednego bledu ochrony pamieci).
- Rezerwowane sa miejsca na deskryptory strony (w przypadku braku pamieci - no_memory wolana jest funkcja
oom()).
- Ostatecznie rozpatrywane sa nastepujace przypadki:
- is_present - strona znajduje sie w pamieci (przydzielona innemu procesowi).
Wtedy deskryptor strony procesu
zostanie uaktualniony tak aby odpowiadal istniejacej w pamieci stronie i na tym konczy sie dzialanie funkcji.
- swap_page - strona dostepna na urzadzeniu wymiany.
W tym przypadku wolana jest funkcja do_swap_page()
oraz flush_page_to_ram().
Zwiekszany jest rowniez licznik tzw. duzych bledow strony - maj_flt; tzn. licznik zadan
sprowadzenia strony znajdujacej sie na urzadzeniu wymiany, oraz licznik rozmiaru pamieci RAM fizycznie zajmowanej
przez proces w danej chwili - rss.
- anonymous_page - strona niezainicjalizowana.
Strona zostala przydzielona logicznie, istnieje w tablicy
deskryptorow, lecz jest to pierwsze odwolanie sie do niej. W tym momencie dopiero jest fizycznie przydzielany
obszar pamieci odpowiadajacy rozmiarowi strony poprzez wywolanie __get_free_page(). Ponadto zwiekszany
jest licznik tzw. malych bledow strony - min_flt; tzn. licznik zadan
uaktualnienia zawartosci
strony bez potrzeby odwolywania sie do urzadenia wymiany. I w tym przypadku zwiekszany jest licznik zajmowanej
przez proces pamieci RAM - rss.
- sig_bus - strona niezidentyfilkowana.
Nastapila proba odwolania sie do adresu, ktory nie nalezy do zadnej ze stron zarejestrowanych w bierzacym
drzewie deskryptorow. Inaczej mowiac translacja adresu logicznego na
liniowy jest niezdefiniowana. W tym
przypadku do procesu, ktory probowal odwolac sie do nieistniejacego adresu wysylany jest sygnal SIGBUS.
Jako ciekawostke mozna dodac, ze na poziomie sprzetowym, sygnal BUS_ERROR (blad szyny), generuje wlasnie
przerwanie bledu strony. Obsluga tego przerwania na poziomie programowym umozliwia (bez angazowania zaawansowanych
ukladow MMU) realizacje prostych modeli pamieci wirtualnej.
Naruszenie praw ochrony strony.
Z tym bledem mamy do czynienia w przypadku gdy zapis danych do strony pamieci jest
zabroniony, badz zapis ten wiaze sie z koniecznoscia wykonania
dodatkowych czynnosci na poziome programowym. I tak:
- W przypadku gdy zapis do strony pamieci jest zabroniony proces otrzymuje sygnal SIGSEGV. W obecnej
implementacji, rzeczywiste prawa zapisu na stronie zostaly przeniesione na nizszy poziom zalezny od sprzetu i sa w
zasadzie realizowane przez funkcje do_page_fault(). Naruszenie ochrony zapisu na stronie
moze nastapic gdy:
- Proces probuje pisac do wlasnej przestrzeni kodu. Generalnie praktyka taka jest pod Linuxem (jak rowniez w
znakomitej wiekszosci pozostalych systemow unixowych) zakazana. Jednak zmiana kodu jest mozliwa poprzez umiejetna
obsluge otrzymanego sygnalu SIGSEGV (tyle mozna dowiedziec sie z
KHG).
- Proces probuje pisac do specjalnie zdefiniowanej przestrzeni danych tylko do odczytu. W jezyku C jest to
posrednio realizowane poprzez zadeklarowanie stalych (np. const int i;) globalnych. Pisze posrednio, poniewaz
pod linuxem implementacja tego mechanizmu jest niepelna (pelna implementacja bylaby dosyc probelamtyczna). Mianowicie
struktury globalne, ktore lacza w sobie stale i zmienne nie beda chronione tym mechanizmem. W tym wypadku ochrona
stalych przed zapisaem konczy sie na odpowiednim ostrzerzeniu kompilatora gcc.
- Proces probuje pisac do strony pamieci dzielonej, nie majac przy tym praw zapisu do tej strony.
- Co sie natomiast kryje pod pojeciem koniecznosci wykonania dodatkowych czynnosci?
- Strona pamieci moze nie byc dzielona, a jednoczesnie moze w pewnych okolicznosciach zawierac dane wspolne dla
kilku procesow (np w przypadku polecenia fork; strona ma ustawiona wtedy flage
COW - Kopiowanie Przy
Zapisie). Wtedy strona taka zostanie skopiowana na uzytek procesu, ktory spowodowal blad ochrony i dopiero prywatna
kopia bedzie mogla zostac zmodyfikowana (jest w tym celu wydzelona prosta, acz czesto wykorzystywana
funkcja).
- Szegolnym przypadkiem jest pierwsze odwolanie sie do zaallokowanej
strony pamieci (w mapowaniu stron obszar taki
zawsze wskazuje wpierw na wspolna strone zerowa z ustawionym bitem COW). Jednak
ten przypadek obsluguje funkcja
braku strony.
- Inny szczegolny przypadek ma miejsce, gdy strona ma ustawiony bit COW, lecze
jedynie obecny proces ma ta strone przydzielona. Wtedy zwyczajnie usuwana jest flaga
COW i ustawiana flaga DIRTY.
Funkcja odpowiedzialna za obsluge bledu ochrony strony jest
void do_wp_page(struct task_struct * tsk, struct vm_area_struct * vma,
unsigned long address, int write_access)
- Przyjmowane argumenty sa identyczne do tych dla funkcji
do_no_page.
- Na poczatku funkcji sprawdzane jest, czy w razie potrzeby skopiowania strony jest szansa na uzyskanie wolnej
strony.
- Nastepnie, na podstawie drzewa deskryptorow stron i podanego adresu, dekodowana jest strona, do ktorej odwolanie
spowodowalo blad ochrony. Tu rowniez, w przypadku wykrycia niespojnosci w budowie drzewa deskryptorow, do procesu
wysylany jest sygnal SIGKILL.
- Teraz, gdy wiadomo juz, ze proces mial prawo odwoania sie do strony, zwiekszany jest licznik tzw.
malych bledow strony - min_flt.
- Dalej sprawdzane jest czy istnieje praktyczna koniecznosc skopiowania strony, czy tez wystarczy zamienic flage
COW na DIRTY.
Nowe rozwiazanie.
Jak mozna zauwazyc, wiele przypadkow dla funkcji obslugujacych bledy braku i
ochrony strony, przeplata sie nawzajem, a nawet istotna czesc kodu dla obu funkcji jest bardzo
podobna. Dlatego postanowiono, ze w najblizszym czasie obie funkcje zostana polaczone w jedna funkcje
handle_mm_fault(). W chwili obecnej w wersji 2.1.5 z polaczonego kodu korzystaja platformy z procesorami MIPS,
SPARC, ALPHA i PPC, jednak wlasciwa funkcja zajmuje sie w tej chwili tylko przygotowaniem odpowiednich argumentow
natomiast znaczna czesc kodu jest zaimplementowana w czesci zaleznej od sprzetu (poniewaz realizacja jest w wiekszosci
bardzo podobna, nalezy sie spodziewac stworzenia wspolnej implementacji w oparciu o te powstale dotychczas). Jedynie
platformy i386 i m68k wykorzystuja stare funkcje do_no_page() i
do_wp_page(), w oczekiwaniu na dzialajaca funkcje wspolna.
Bibliografia.
- Pliki zrodlowe Linuxa:
-
Kernel Hacker's Guide.
- Mlitary Intel 486 processor family.
- M68030 USER'S MANUAL.
- M68040 USER'S MANUAL.
- M68060 USER'S MANUAL.
- Maurice J. Bach, Budowa systemu operacyjnego Unix, WNT 1995.
Autor: (C)1997 Szymon Stasik