Do strony głównej modułu Pamięć


Błędy braku i ochrony strony

  1. Wstęp
  2. Sprzętowe mechanizmy obsługi błędów
  3. Powstawanie błędów braku strony
  4. Niskopoziomowa obsługa błędów braku i ochrony strony
  5. Funkcje wysokopoziomowe
    1. Błąd braku strony
    2. Błąd ochrony strony
  6. Struktury danych
  7. Jak to wszystko działa
  8. Więcej informacji na temat powstawania wyjątków
  9. Szczegóły techniczne
    1. Translacja adresów logicznych na fizyczne
    2. Wyznaczanie miejsca wystąpienia błędu
  10. Bibliografia

  1. Wstęp

    Błędy braku i ochrony strony są to specjalne sytuacje wyjątkowe, zgłaszane przez procesor w przypadku gdy program użytkownika próbuje się odwołać do obszaru pamięci, do którego z rozmaitych powodów nie ma praw dostępu. U źródeł powstania takich mechanizmów leżała chęć zabezpieczenia systemów operacyjnych przed wpływem źle napisanych programów a także umożliwienie korzystania z zaawansowanych metod zarządzania pamięcią wirtualną (np. stronicowanie na żądanie - ang. paging on-demand)

    W dokumencie tym postaramy się skupić na środkach sprzętowych dostępnych w procesorze Intel i386 i nowszych, jako że ich znajomość jest konieczna do zrozumienia niektórych metod zarządzania pamięcią. Pokażemy również, jak możliwości sprzętu zostały wykorzystane przez twórców systemu do implementacji poszczególnych strategii gospodarowania tym zasobem.

    Powrót do spisu treści


  2. Sprzętowe mechanizmy obsługi błędów

    Realizacja mechanizmu ochrony pamięci wymaga wsparcia sprzętowego. Procesor musi mieć możliwość definiowania praw odczytu/zapisu poszczególnych jej regionów a także zgłaszania naruszenia tych praw za pomocą mechanizmu przerwań.

    Procesory Intela posiadają mechanizm wyjątków ("exceptions"), czyli przerwań generowanych przez procesor podczas wykonywania programu. Istnieje możliwość zdefiniowania własnych procedur ich obsługi, co sprawia, że mechanizm ten staje się użytecznym narzędziem przy implementacji systemu zarządzającego pamięcią. .

    Powrót do spisu treści


  3. Powstawanie błędów braku strony

    Z punktu widzenia zarządzania pamięcią interesują nas jedynie dwa zgłaszane przez procesor wyjątki. Pierwszy z nich powstaje w przypadku wykrycia błędu już na etapie segmentacji, drugi generowany jest w przypadku błędu translacji adresu logicznego na fizyczny na poziomie mechanizmu stronicowania. Więcej informacji na temat powstawania wyjątków znajduje się w dalszej części dokumentu.

    Powrót do spisu treści


  4. Niskopoziomowa obsługa błędów braku i ochrony strony

    Procesor wywołując odpowiednią procedurę obsługi wyjątku związanego z odwołaniem do pamięci przekazuje jej następujące informacje:

    Podczas startu systemu Linux funkcja inicjalizacyjna instaluje procedurę obsługi wyjątku. Została ona napisana w assemblerze i386 i służy tylko do zachowania stanu procesora, zawartości rejestrów a następnie przekazania tych informacji do procedury " do_page_fault" zapisanej w języku C, która zajmuje się dalszą obsługą błędu.

    asmlinkage void do_page_fault(struct pt_regs *regs,
                                  unsigned long error_code)

    Parametr regs wskazuje na kopię zawartości rejestrów procesora w momencie wystąpienia błędu, potrzebną do wznowienia działania procesu po obsłużeniu sytuacji wyjątkowej, a error_code niesie informacje o przyczynie jego wystąpienia.

    Aby obsłużyć błąd strony procedura do_page_fault wyznacza adres liniowy miejsca jego wystąpienia, a następnie na jego podstawie obszar pamięci, którego błąd dotyczy.

    W przypadku gdy poprawne zidentyfikowanie obszaru gdzie wystąpił wyjątek okaże się niemożliwe, aktualnie wykonywany proces zostaje przerwany, gdyż prawdopodobnie doszło do zniszczenia jego struktur danych i istniałoby niebezpieczeństwo załamania systemu.

    W przypadku gdy obszar pamięci okazał się poprawny, następuje wywołanie odpowiedniej procedury obsługi zaistniałej sytuacji.

    Powrót do spisu treści


  5. Funkcje wysokopoziomowe

    1. Błąd braku strony

      Obsługą przypadku poprawnego odwołania do nieistniejącej w pamięci strony zajmuje się procedura

      void do_no_page(struct task_struct * tsk,
                      struct vm_area_struct * vma,
                      unsigned long address,
                      int write_access)
      Sprawdza ona, czy wszystkie struktury niezbędne do translacji adresu zawierają odpowiednie dane, jeśli nie - tworzy takowe i wypełnia właściwymi informacjami.

      Możliwe są następujące przyczyny wystąpienia wyjątku nazywane tak od odpowiednich etykiet, do których skacze procedura:

      • is_present - strona obecna w pamięci, a błąd został zgłoszony "zbędnie". Sytuacja taka może wystąpić, gdy procesy dzielą pamięć, jeden z nich sprowadza ramkę do pamięci, a drugi myśli, że znajduje się ona wciąż na urządzeniu wymiany. W takim przypadku odpowiednia tablica stron drugiego procesu zostaje uaktualniona.
      • swap_page - strona została usunięta z pamięci i zachowana na urządzeniu wymiany - w takiej sytuacji następuje sprowadzenie strony do pamięci operacyjnej przy użyciu procedury do_swap_page.
      • anonymous_page - strona została przydzielona logicznie, została utworzona odpowiednia pozycja w tablicy stron, jednak nie nastąpił fizyczny przydział pamięci.

        System stara się zwlekać z przydziałem pamięci dla procesu aż do chwili kiedy ten zechce jej użyć, dlatego żądanie przydziału jest wykonywane poprzez logiczne uaktualnienie tablicy stron procesu, podczas gdy fizyczna alokacja odbywa się podczas błędów braku strony generowanych przez program.

        Czasami może się zdarzyć, że taka operacja się nie powiedzie, jest to wtedy traktowane jako błąd krytyczny i proces zostaje przerwany sygnałem SIGBUS.

        Przypadek anonymous_page ma miejsce także w sytuacji, kiedy w przestrzeń adresową procesu wmapowany zostaje plik dyskowy, (np. w celu wykonania zawartego w nim programu). Wtedy, o ile zdefiniowana jest odpowiednia funkcja ładująca ramkę z dysku, zostaje ona wywołana zamiast pobrania nowej ramki z puli systemu.

      Podczas obsługi sytuacji nieobecnej strony może wystąpić brak pamięci. Wtedy bieżący proces jest przerywany odpowiednim sygnałem.

    2. Błąd ochrony strony

      Obsługą przypadku odwołania do zabezpieczonej przed zapisem strony w pamięci zajmuje się procedura:

      void do_wp_page(struct task_struct * tsk,
                      struct vm_area_struct * vma,
                      unsigned long address,
                      int write_access)
      Procedura do_wp_page zajmuje się rozwiązywaniem konfliktów w dostępie do pamięci dzielonej do odczytu przez 2 procesy w sytuacji kiedy jeden z nich próbuje do niej pisać. Procesy dzielą pamięć do odczytu w sytuacji kiedy jeden z nich wykona operację fork().

      Jest to część mechanizmu COW - copy on write, gdzie nie dokonuje się fizycznego kopiowania stron procesu w momencie uruchomienia procesu potomnego, a jedynie powiela się katalogi tablic i tablice stron. Uaktualnienie to polega na zabezpieczeniu wszystkich stron obydwu procesów przed zapisem i ustawieniu im flagi copy_on_write.

      Dzięki zabezpieczeniu przed zapisem uzyskamy wywołanie procedury błędu ochrony gdy którykolwiek z procesów zechce pisać do swojej pamięci. Wtedy nastąpi "rozdzielenie" ich przestrzeni adresowych przez wykonanie kopii kolidującej strony.

    Powrót do spisu treści


  6. Struktury danych

    System obsługi błędów strony nie posiada żadnych poważniejszych struktur danych. Gromadzona jest jedynie statystyka ilości wystąpień wyjątków dla każdego zadania.

    Oprócz tego, duży wpływ na obsługę błędów strony ma konstrukcja katalogu tablic i tablic stron, ale informacje te wykraczają poza ramy tego tematu. Dokładniej można się o tym dowiedzieć w rozdziale na temat stronicowania.

    Powrót do spisu treści


  7. Jak to wszystko działa

    Najlepszym sposobem na przybliżenie sposobu działania mechanizmu wyjątków i pokazania jego znaczenia dla zarządzania pamięcią jest podanie kilku przykładów.

    Powrót do spisu treści


  8. Więcej informacji na temat powstawania wyjątków

    Z punktu widzenia procesora podczas odwołania do pamięci możliwe jest powstanie dwóch wyjątków. Są to: wyjątek nr 13, tzw. "general protection fault" oraz wyjątek nr 14, tzw. "page fault".

    Pierwszy z nich powstaje na etapie translacji adresu na poziomie segmentów. Obejmuje on następujące przypadki:

    Drugi z wyjątków ("page fault") zostaje zgłoszony w przypadku błędu translacji adresu na poziomie mechanizmu stronicowania. Obejmuje to następujące przypadki:

    Powrót do spisu treści

  9. Szczegóły techniczne

    1. Translacja adresu logicznego na fizyczny

      Ogólnie system Linux zakłada 3-poziomowy mechanizm translacji adresu logicznego na fizyczny. W przypadku procesora i386 mamy dostępne sprzętowo tylko 2 poziomy struktur danych: katalog tablic stron i tablice stron.

      Katalog tablic stron to specjalna strona (o rozmiarze 4KB) w pamięci związana z procesem, która zawiera 1024 adresy poszczególnych tablic stron. Odpowiednią tablicę wybieramy na podstawie 10 najbardziej znaczących bitów adresu logicznego. Po odczytaniu z katalogu adresu tablicy stron odczytujemy z niej adres właściwej strony pamięci, do której chcemy się odwołać. Każda tablica stron zawiera informacje o maks. 1024 stronach w pamięci, więc teoretycznie może służyć do zarządzania 4 MB danych.

      Poszczególne pozycje tablicy stron oprócz adresów ramek zawierają także pewne bity dodatkowe, takie jak informacja czy strona znajduje się w pamięci czy była ostatnio odczytywana i/lub zapisywana, jakie są uprawnienia do czytania/pisania itp.

      Operacja translacji adresu logicznego na fizyczny jest dokonywana przed wykonaniem każdej instrukcji maszynowej przez procesor. W przypadku gdy na podstawie informacji odczytanej z tablicy stron okaże się, że żądanej strony nie ma w pamięci, procesor generuje przerwanie błędu braku strony (page fault). Jeśli strona jest, natomiast odpowiednie bity w tablicy stron kolidują z trybem dostępu do niej (np. jeśli strona jest tylko do odczytu a program użytkowy próbuje pisać) to także zgłaszane jest przerwanie błędu strony.

      Poprzez odpowiednią konstrukcję katalogów oraz tablic stron dla każdego procesu można uzyskać dwie rzeczy - ochronę przestrzeni adresowej procesów oraz pamięć wirtualną. W celu uzyskania ochrony wystarczy tak skonstruować odpowiednie tablice, by odwołanie do niepożądanego adresu spowodowało zgłoszenie przez procesor wyjątku. W przypadku pamięci wirtualnej wyzerowanie bitu "present" na odpowiedniej pozycji w tablicy stron również spowoduje zgłoszenie wyjątku. Wystarczy więc tak skonstruować procedurę jego obsługi, by na podstawie informacji dostarczonych przez procesor rozpoznała typ wyjątku i podjęła stosowną akcję, kończąc niebezpieczny proces lub sprowadzając nieobecną stronę z urządzenia wymiany. Aby cały ten mechanizm okazał się skuteczny, musi być przezroczysty dla działających w systemie procesów.

      Aby to zapewnić niezbędna jest możliwość wznowienia instrukcji powodującej zgłoszenie wyjątku. Tak też jest w przypadku procesorów Intela. Wykonanie instrukcji podzielone jest na kilka etapów. Jednym z nich jest translacja adresów fizycznych na logiczne. Wtedy też następuje ewentualne zgłoszenie wyjątku, bez wykonywania instrukcji. Tak więc po usunięciu warunków uniemożliwiających wykonanie instrukcja może być powtórzona, bez obaw o skutki uboczne.

    2. Wyznaczanie miejsca wystąpienia błędu

      Aby obsłużyć błąd strony procedura do_page_fault wyznacza adres liniowy miejsca wystąpienia błędu poprzez odczytanie go z rejestru CR2 procesora, następnie na jego podstawie i na podstawie informacji o aktualnie wykonywanym procesie wyznacza obszar pamięci (vm_area_struct), którego błąd dotyczy. W przypadku gdy zidentyfikowanie obszaru gdzie wystąpił wyjątek okaże się niemożliwe, aktualnie wykonywany proces zostaje przerwany, gdyż prawdopodobnie doszło do zniszczenia jego struktur danych i istniałoby niebezpieczeństwo załamania systemu.

      Odnalezienie obszaru pamięci wcale nie oznacza że nastąpiło odwołanie do poprawnego adresu. Funkcja find_vma daje w wyniku obszar pamięci, którego górne ograniczenie jest najbliższe danemu adresowi, ale niekoniecznie jest prawdą że adres zawiera się w bloku pamięci odpowiadającym tej strukturze. Jeśli wykryjemy że wszystko jest w porządku, przechodzimy do kolejnej fazy obsługi błędu.

      Fakt, że adres liniowy błędu nie należy do znalezionego bloku pamięci z reguły oznacza sytuację błędną. Z reguły, albowiem istnieją specjalne obszary pamięci - tzw. segmenty stosu, które mogą się rozrastać w dół.


      Rys. 1 Mechanizm działania stosu procesora

      W przypadku gdy znaleziony obszar okaże się być segmentem stosu, następuje jego rozszerzenie pod warunkiem że błąd strony nastąpił podczas odwołania, które przekroczyło wskaźnik stosu o co najwyżej 32 bajty. Istnieją specjalne instrukcje procesora (pusha i popa) operujące na stosie, które jednorazowo zapisują/odczytują na nim 32 bajty danych, poza tym nie dopuszcza się żeby jakakolwiek inna instrukcja operowała na pamięci poniżej wskaźnika stosu.

      Powrót do spisu treści


  10. Bibliografia

    1. Pliki źródłowe systemu Linux mm/memory.c oraz arch/i386/fault.c
    2. www.intel.com/developer

Autorzy: