Pamięć współdzielona - Wydział Fizyki i Informatyki Stosowanej
Transkrypt
Pamięć współdzielona - Wydział Fizyki i Informatyki Stosowanej
Model pamięci w systemie Linux Patryk Konopka Paweł Piecyk Wydział Fizyki i Informatyki Stosowanej 2013.05.08 Plan prezentacji 1. Tryb rzeczywisty i segmentowy model pamięci 2. Tryb chroniony i płaski model pamięci 3. Pamięć wirtualna 4. • Stronicowanie • Physical Address Extension • Swapowanie • Mapowanie pamięci logicznej na fizyczną • Problemy dynamicznej alokacji pamięci • Pamięć współdzielona Przydatne narzędzia Trochę historii – tryb rzeczywisty (real mode) • Tryb, który nie zapewnia ochrony pamięci ani wielozadaniowości • Procesor pracuje tak jak Intel 8086 • 20-bitowa szyna danych → można zaadresować max. 1MiB pamięci • Wszystkie procesory od 286 do obecnie stosowanych konstrukcji startują w real mode w celu zapewnienia kompatybilności • Charakterystyczny dla MS-DOS „640K ought to be enough for anybody” Trochę historii – segmentowy model pamięci • Program ma wydzielone w pamięci 3 segmenty: kodu, danych i stos (+ opcjonalne dodatkowe segmenty danych) • Adres – dwie 16-bitowe liczby: numer segmentu (segment) + przemieszczenie względem początku segmentu (offset) • Format adresu segment:offset • Segmenty mogą na siebie nachodzić – przez to wiele adresów logicznych może wskazywać na jeden adres fizyczny (dokładnie 4096) • Wszystkie procesory od 286 do obecnie stosowanych konstrukcji startują w real mode w celu zapewnienia kompatybilności PhysicalAddress = Segment * 16 + Offset Protected mode • Tryb pracy mikroprocesorów x86 wprowadzony w mikroprocesorze Intel 80286. • Umożliwienie adresowania pamięci przekraczającej 1MB. • Wprowadzenie wielozadaniowości • Podstawowy tryb pracy dla systemów Linux, Windows i BSD Protected mode W rejestrze segmentowym przechowujemy „selektor segmentu”: Do dyspozycji mamy dwie tablice: -GDT – Global Descriptor Table Jedna w systemie – zawiera informacje o kodzie OS i jego danych, dostępna dla wszystkich procesów -LDT – Local Descriptor Table Osobna dla każdego zadania - 8192 deskryptorów globalnych - 8192 deskryptorów lokalnych Physical or Protected mode – deskryptor segmentu • Limit segmentu – wielkość segmentu, połączenie obu pól (20 bitowa liczba) • Adres bazowy oraz Baza – offset segmentu, połączenie trzech pól (32 bitowa liczba) • Typ – prawa dostępu do segmentu (Write / Read / Execute) • DPL – poziom uprzywilejowania segmentu • S – typ deskryptora, jeżeli 0 to opisuje segment systemowy, w przeciwnym wypadku segment danych lub kodu • G – ziarnistość (0 – ziarnistość 1B, 1 – ziarnistość 4kB) • P – informuje czy segment jest załadowany do pamięci Protected mode Protected mode • To system musi zarządzać trybem chronionym. • Programy nie mają dostępu do danych lub kodu z wyższym priorytetem. • Stosowane w obecnych systemach operacyjnych. Pamięć wirtualna Pamięć wirtualna – mechanizm zarządzania pamięcią komputera zapewniający procesowi wrażenie pracy w jednym dużym, ciągłym obszarze pamięci operacyjnej podczas gdy fizycznie może być ona pofragmentowana, nieciągła i częściowo przechowywana na urządzeniach pamięci masowej. Implementacja w systemie Linux: segmentacja + stronicowanie Stronicowanie • Przestrzeń adresowa segmentu jest podzielona na równe części – strony • Rozmiar strony – 4kB dla procesora Intel • Pamięć fizyczna podzielona jest na części o tej samej wielkości co strony nazywane ramkami • Adres logiczny jest tłumaczony na adres fizyczny z wykorzystaniem tablic stron Stronicowanie – wielopoziomowe tablice stron • Jeśli używalibyśmy prostych jednopoziomowych tablic stron to musiałyby one zawierać 2^20 wpisów (4 bajty na wpis = 4 MB pamięci RAM dla każdego procesu) • Dwupoziomowe tablice stron redukują tą ilość przez tworzenie tablic stron tylko dla tych obszarów pamięci wirtualnej, które są aktualnie używane • Każdy aktywny proces ma przypisany Page Directory • Adres fizyczny PD aktualnie używanego procesu jest przechowywany w rejestrze cr3 Stronicowanie – wielopoziomowe tablice stron PAE – Physical Address Extension • Rozszerzenie umożliwiające procesorom x86 (32-bit) dostęp do fizycznej przestrzeni adresowej (RAM) o rozmiarze większym niż 4 GB. • Rejestr cr3 wskazuje na PDPT, który zawiera wskaźniki do 4 PD • Rozszerzenie to nie powiększa liniowej przestrzeni adresowej procesu, więc operowanie na pamięci > 4GB jest możliwe tylko z poziomu kernela PAE Disabled PAE Enabled Linux paging model 32bit – używane 2 poziomy. Nie używamy PUD i PMD 32bit z PAE – używane są 3 poziomy. Nie używamy PUD 64bit – używane 3 lub 4 poziomy w zależności od architektury procesora Swapping • Realizowane przez kswapd – kernel swap daemon (kernel thread) • kswapd jest inicjalizowany przy uruchamianiu systemu i jest uruchamiany okresowo zgodnie z licznikiem zwanym kernel swap timer • Przy każdym uruchomieniu kswapd sprawdza czy ilość wolnej pamięci nie jest zbyt niska, jeśli tak to stara się ją zwolnić • Do wyznaczenia stron, które mogą być zapisane na dysk używany jest algorytm postarzania • Każda strona ma licznik określający jej wiek • Licznik przy alokacji ustawiany jest domyślnie na 3 • Przy każdym użyciu strony licznik+=3 (maksymalnie 20) • Co pewien czas licznik-- dla każdej ze stron • Gdy licznik = 0 dana strona jest dobrym kandydatem do zapisania na dysk Swapping • Podczas projektowania aplikacji działającej w obrębie systemu czasu rzeczywistego chcemy zminimalizować czas dostępu do pamięci. • Nie chcemy, aby nasza pamięć była swap’owana, swap = długi czas odczytu pamięci. • Jak się uchronić przed swap’owaniem? mlock() lub mlockall() – gwarancja na to, że nasza pamięć pozostanie w pamięci fizycznej (nie będzie swap’owana) #include <sys/mman.h> int mlock(const void *addr, size_t len); int munlock(const void *addr, size_t len); int mlockall(int flags); int munlockall(void); MMU – Memory Management Unit User space Kernel space MMU – Memory Management Unit • Układ realizujący dostęp do pamięci fizycznej żądanej przez CPU • Realizacja translacji pamięci wirtualnej do pamięci fizycznej (hardware’owo) • Ochrona pamięci • Pamięć podręczna • Dzielenie pamięci wirtualnej na strony o rozmiarze 2N Motorola 68451 – MMU dla procesora Motorola 68010 w postaci osobnego układu MMU – Memory Management Unit • TLB – Translation Lookaside Buffer • Tablica buforująca strony • Szybki dostęp do stron MMU: •Odpowiedzialny za Segmentation fault ! •Sygnał SIGSEGV Segmentation fault - Dereferencja wskaźników wskazujących na NULL - Próba dostępu do pamięci do której nie mamy uprawnień - Próba dostępu do nieistniejącego adresu w pamięci - Próba zapisu do pamięci typu read-only - Przepełnienie bufora - Użycie niezainicjalizowanego wskaźnika - Próba odpalenia źle skompilowanego programu Text segment Text segment • Zawiera kod maszynowy przeznaczony do wykonania przez procesor komputera. • Segment kodu jest wskazywany przez rejestr segmentowy CS. • Pamięć oznaczona przez system jako read-only – dane nie mogą być modyfikowane przez proces. • Procesy mogą współdzielić kod, w momencie gdy uruchomiona jest kopia programu. • Można wyłączyć współdzielenie kodu w momencie kompilacji za pomocą flagi –N. Data segment Data segment Miejsce, w którym przechowywane są : -zmienne globalne z przypisaną wartością int i = 1; int main() {} - zmienne statyczne z przypisaną wartością int main() { static char *text = „Hello”; } BSS segment BSS segment Wykorzystywane przez kompilatory i linkery w celu przechowywania • zaalokowanych, ale niezainicjowanych zmiennych statycznych. void dummy() { static int i; } • Przechowywanie niezainicjowanych zmiennych globalnych. int i; int main() {} • Język C: int i; == int i = 0; Rozmiar początkowy BSS segment – 4 lub 8 bajtów. Funkcje sbrk() oraz brk() Funkcje sbrk() oraz brk() • Podstawowe funkcje do zarządzania pamięcią w systemach Unix • Kontrolowanie rozmiaru dostępnej pamięci konkretnego procesu w sekcji Heap • Funkcje wysokopoziomowe • Funkcje wykorzystywane przez malloc() • Kiedyś w systemach Unix’owych: jedyny sposób na zażądanie dostępu do dodatkowej pamięci. Funkcje sbrk() oraz brk() Program brake – wskazuje na pierwszy adres, który jest niedostępny dla procesu (w kontekście sterty). #include <unistd.h> int brk(void *end_data_segment) - ustawia Program brake na adres, który wskazuje end_data_segment i zmienia rozmiar dostępnej pamięci. void *sbrk(intptr_t increment) – przesuwa Program brake o ilość bajtów przekazaną w parametrze increment. void * ptr = sbrk(0); – zwróci bieżący adres, na który wskazuje Program break. Wywołanie systemowe mmap() Wywołanie systemowe mmap() mmap() – wywołanie systemowe, które nakazuje systemowi operacyjnemu odwzorowanie danej części wybranego pliku w przestrzeni adresowej procesu. • Do obszaru pliku odnosimy się jak do zwykłej tablicy bajtów w pamięci. • Eliminacja wywołań systemowych typu read/write. • Przyspieszenie działania na dużych plikach. Wywołanie systemowe mmap() #include <sys/mman.h> void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset); • start - określa adres, w którym chcemy widzieć odwzorowanie pliku. Nie jest wymagane – zazwyczaj „0”. • length - ilość bajtów jaką chcemy odwzorować w pamięci. • prot - flagi określające uprawnienia jakie chcemy nadać obszarowi pamięci. PROT_READ | PROT_WRITE | PROT_EXEC | PROT_NONE • flags - dodatkowe flagi określające sposób działania wywołania mmap. MAP_SHARED | MAP_PRIVATE | MAP_ANONYMOUS • fd - deskryptor pliku, który chcemy odwzorować w pamięci. • offset - liczba określająca od którego miejsca w pliku chcemy rozpocząć Wywołanie systemowe mmap() void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset); Wartość zwracana: • Sukces – zwrócenie wskaźnika na odwzorowaną pamięć. • Porażka – zwrócenie MAP_FAILED == (void*)-1. Wywołanie systemowe munmap() munmap() - Wywołanie systemowe nakazujące zlikwidowanie odwzorowania pliku w pamięci. int munmap(void *start, size_t length); •start - określa adres odwzorowania do skasowania. •length - liczba bajtów zajmowana przez odwzorowanie. Wartość zwracana: •Sukces – zwrócenie 0. •Porażka – zwrócenie -1. Problem dynamicznej alokacji pamięci • Funkcja malloc() alokuje pamięć z wykorzystaniem dwóch funkcji: - sbrk() - mmap() • Alokacja pamięci za pomocą sbrk(), gdy n < MMAP_THRESHOLD • Alokacja pamięci za pomocą mmap(), gdy n >= MMAP_THRESHOLD, gdzie n – rozmiar w bajtach • Możliwość zmiany MMAP_THRESHOLD za pomocą funkcji mallopt(). int result = mallopt(M_MMAP_THRESHOLD, 128*1024); - wartość domyślna Problem dynamicznej alokacji pamięci • Pojedynczy blok pamięci to tzw. chunk • Puste (zwolnione) bloki pamięci tworzą listę • Ważne: wywołanie malloc(96) wcale nie zaalokuje bloku pamięci o rozmiarze 96 bajtów !!! Wywołanie malloc() Chunk size Pointer to next free chunk Data Rys 1. Pojedynczy chunk Rys 2. Przykład wywołania malloc(96); Wywołanie free() Rys 3. Przykład wywołania free(ptr); Negatywne skutki dynamicznej alokacji fragmentacja Problem pośredni: •długi czas przejścia listy w celu znalezienia odpowiedniego chunk’a Fragmentacja – rozwiązanie problemu Zastosowanie innego algorytmu alokacji np. Best-Fit Allocation • Przed: • Po: Czas działania algorytmu O(n), gdzie n – ilość wolnych bloków Stos Stos • Liniowa struktura danych, w której dane są dokładane na wierzch stosu. • Dwie operacje: - push – umieszczenie wartości na szczycie stosu, czyli przesunięcie rejestru ESP o odpowiednią ilość bajtów do tyłu i wpisanie tam wartości - pop – zdjęcie wartości ze stosu, czyli przesunięcie ESP do przodu • Odpowiedzialne rejestry: SS – rejestr segmentowy, wskazujący na początek stosu ESP – rejestr wskazujący na element znajdujący się na szczycie stosu Stos • Jak sprawdzić maksymalny rozmiar stosu? Wywołanie: ulimit -s Kernel space Kernel space Przechowuje: •Kod kernela •Dane wykorzystywane przez kernel •Page tables Kernel też potrzebuje swojej przestrzeni adresowej ! •Cała pamięć (1GB) jest od razu mapowana na pamięć fizyczną. •Komunikacja tylko za pomocą wywołań systemowych. Pamięć współdzielona Pamięć współdzielona – pamięć do której może mieć jednoczesny dostęp kilka procesów Gdzie może się przydać: - komunikacja między procesami (inter-process communication) - współdzielenie bibliotek Pamięć współdzielona – komunikacja między procesami int segment_id = shmget(segment_id, size, flags) - alokuje pamięć współdzieloną void* shmat(segment_id, NULL, flags) - mapuje pamięć współdzieloną do przestrzeni pamięci procesu void shmdt(adress) – odłącza pamięć współdzieloną shmctl() - służy m.in. do zwalniania pamięci współdzielonej Przydatne narzędzia: ipcs – pozwala uzyskać informacje dotyczące pamięci współdzielonej ipcrm – pozwala usuwać obiekty związane z IPC Przydatne narzędzia - top top – narzędzie do monitorowania systemu W kontekście pamięci najbardziej powinny interesować nas kolumny: VIRT – ilość pamięci do której proces ma dostęp w aktualnym momencie, czyli suma pamięci aktualnie używanej, plików z HDD, która są do niej zmapowane oraz pamięci współdzielonej. RES – określa ile pamięci fizycznej jest używane przez proces. SHR – określa jaka część pamięci z kolumny VIRT jest współdzielona. Przydatne narzędzia - /proc/pid/status free – proste narzędzie do statystyki, wyświetla ilość dostępnej/zajętej pamięci w systemie. /proc/pid/status – interesujące informacje z przedrostkiem „Vm” • VmPeak – maksymalny rozmiar pamięci użyty przez proces w ciągu jego życia, • VmSize – rozmiar pamięci wirtualnej, • VmLck – ilość zablokowanej pamięci, • VmHWM – „High water mark”, maksymalny rozmiar pamięci fizycznej użyty przez proces, • VmRSS – aktualny rozmiar wykorzystywanej pamięci fizycznej, • VmData, VmStk, VmExe – poszczególne segmenty, • VmPTE – rozmiar tablic stron • VmLib – rozmiar kodu bibliotek współdzielonych Przydatne narzędzia - pmap pmap -x pid – wyświetla mapę pamięci procesu Kolumny: Address – adres początku mapy Kbytes – rozmiar mapy RSS – rozmiar zajmowanej pamięci fizycznej Dirty - dirty pages (both shared and private) Mode - uprawnienia: read, write, execute, shared, private (kopia przy zapisie) Mapping – plik z którego pochodzi mapa, lub '[ anon ]' dla zaalokowanej pamięci, lub '[ stack ]' dla stosu procesu Przydatne narzędzia – pmap – przykładowy wynik $ pmap -x 4195 4195: ./process1 Address Kbytes 0000000000400000 4 0000000000600000 4 0000000000601000 4 00007f770aae7000 1784 00007f770aca5000 2044 00007f770aea4000 16 00007f770aea8000 8 00007f770aeaa000 20 00007f770aeaf000 140 00007f770b0a9000 12 00007f770b0c7000 28 00007f770b0ce000 12 00007f770b0d1000 4 00007f770b0d2000 8 00007fff62113000 132 00007fff621fe000 8 ffffffffff600000 4 ---------------- -----total kB 4260 RSS 4 4 4 276 0 16 8 12 116 12 4 12 4 8 12 4 0 -----496 Dirty 0 4 4 0 0 16 8 12 0 12 4 12 4 8 12 0 0 -----96 Mode r-x-r---rw--r-x-----r---rw--rw--r-x-rw--rw-srw--r---rw--rw--r-x-r-x-- Mapping process1 process1 process1 libc-2.17.so libc-2.17.so libc-2.17.so libc-2.17.so [ anon ] ld-2.17.so [ anon ] [ shmid=0x2e0001 ] [ anon ] ld-2.17.so ld-2.17.so [ stack ] [ anon ] [ anon ] Analiza pamięci - narzędzia Valgrind – narzędzie do debugowania pamięci, wykrywania wycieków pamięci oraz profilowania aplikacji. Geneza nazwy: nazwa głównego wejścia do Valhali w nordyckiej mitologii. • Współpraca z gdb: 1. valgrind --vgdb=yes --vgdb-error=0 ./small_app 2. gdb ./small_app 3. (gdb) target remote | vgdb --pid=xxxx 4. (gdb) monitor leak_check full reachable any Bibliografia 1. http://duartes.org/gustavo/blog/post/anatomy-of-a-program-in-memory 2. http://www.advancedlinuxprogramming.com 3. http://www.cs.rit.edu/~ark/lectures/gc/03_03_03.html Best-Fit Algorithm 4. http://www.ualberta.ca/CNS/RESEARCH/LinuxClusters/mem.html Understanding Memory 5. http://technology-shettyprasad.blogspot.com/2010/08/dynamic-memory-management.html 6. http://en.wikipedia.org 7. Linux man pages