Środowiska POSIX zapewniają co najmniej dwa sposoby dostępu do plików. Jest średnia wywołania systemowe open()
, read()
, write()
i przyjaciół, ale istnieje również możliwość korzystania mmap()
zmapować plik do pamięci wirtualnej.
Kiedy lepiej jest używać jednego nad drugim? Jakie są ich indywidualne zalety, w tym dwa interfejsy?
Odpowiedzi:
mmap
jest świetny, jeśli masz wiele procesów uzyskujących dostęp do danych w trybie tylko do odczytu z tego samego pliku, co jest powszechne w tego typu systemach serwerów, które piszę.mmap
umożliwia wszystkim tym procesom współużytkowanie tych samych stron pamięci fizycznej, oszczędzając dużo pamięci.mmap
umożliwia także systemowi operacyjnemu optymalizację operacji stronicowania. Rozważmy na przykład dwa programy; program,A
który wczytuje1MB
plik do bufora tworzącego za pomocąmalloc
i program B, którymmaps
1 MB pliku do pamięci. Jeśli system operacyjny musi zamienić częśćA
pamięci, musi zapisać zawartość bufora do zamiany, zanim będzie mógł ponownie użyć pamięci. W takimB
przypadku wszelkie niezmodyfikowanemmap
strony mogą zostać ponownie użyte natychmiast, ponieważ system operacyjny wie, jak je przywrócić z istniejącego pliku, z którego byłymmap
. (System operacyjny może wykryć, które strony są niezmodyfikowane, początkowo oznaczając strony do zapisummap
jako tylko do odczytu i wychwytując błędy seg , podobnie jak w przypadku strategii Kopiuj przy zapisie).mmap
jest również przydatny do komunikacji między procesami . Plik możnammap
odczytywać / zapisywać w procesach, które muszą się komunikować, a następnie używać operacji podstawowych synchronizacji wmmap'd
regionie (po to jestMAP_HASSEMAPHORE
flaga).Jedno miejsce
mmap
może być niewygodne, jeśli musisz pracować z bardzo dużymi plikami na komputerze 32-bitowym. Wynika to z faktu, żemmap
musi znaleźć ciągły blok adresów w przestrzeni adresowej procesu, który jest wystarczająco duży, aby zmieścił się w całym zakresie mapowanego pliku. Może to stanowić problem, jeśli twoja przestrzeń adresowa zostanie pofragmentowana, gdzie możesz mieć 2 GB wolnej przestrzeni adresowej, ale żaden indywidualny jej zakres nie mieści się w mapowaniu plików 1 GB. W takim przypadku może być konieczne mapowanie pliku na mniejsze fragmenty, niż gdybyś chciał go dopasować.Inną potencjalną niezręcznością związaną z
mmap
zastępowaniem odczytu / zapisu jest konieczność mapowania na przesunięciach rozmiaru strony. Jeśli chcesz tylko uzyskać dane z przesunięciemX
, musisz naprawić to przesunięcie, aby było zgodnemmap
.I wreszcie, odczytu / zapisu są jedynym sposobem może działać z niektórymi typami plików.
mmap
nie można go używać do rur i tty .źródło
MAP_HASSEMAPHORE
jest specyficzny dla BSD.Jednym z obszarów, w którym uważam, że mmap () nie jest zaletą, było czytanie małych plików (poniżej 16 KB). Narzut związany z błędem odczytu strony przez cały plik był bardzo wysoki w porównaniu z wykonaniem pojedynczego wywołania systemowego read (). Wynika to z faktu, że jądro czasami może całkowicie zaspokoić odczyt w wycinku czasu, co oznacza, że kod się nie przełącza. W przypadku błędu strony bardziej prawdopodobne było zaplanowanie innego programu, co spowodowałoby większe opóźnienie operacji na pliku.
źródło
malloc
fragmentu pamięci i zrobienia z niego 1read
. To pozwala mieć ten sam kod, który obsługuje mapy pamięci, a Malloc'ed obsługuje.read
dostępami był większy niż narzut związany z manipulowaniem pamięcią wirtualną.mmap
należy zaktualizować 4 wpisy w tabeli stron. Ale użycieread
do skopiowania do bufora 16K obejmuje również aktualizację 4 pozycji tabeli stron, nie wspominając już o tym, że trzeba skopiować 16K do przestrzeni adresu użytkownika. Czy mógłbyś więc wyjaśnić różnice między operacjami w tabeli stron i jak to jest droższemmap
?mmap
ma tę zaletę, gdy masz losowy dostęp do dużych plików. Kolejną zaletą jest to, że uzyskujesz do niego dostęp za pomocą operacji pamięci (memcpy, arytmetyka wskaźnika), bez zawracania sobie głowy buforowaniem. Normalne operacje we / wy mogą czasami być dość trudne, gdy używasz buforów, gdy masz struktury większe niż bufor. Kod do obsługi, który jest często trudny do poprawnego wykonania, mmap jest ogólnie łatwiejszy. To powiedziawszy, istnieją pewne pułapki podczas pracy zmmap
. Jak już wspomniano,mmap
konfiguracja jest dość kosztowna, dlatego warto ją stosować tylko dla danego rozmiaru (w zależności od maszyny).W przypadku czystego sekwencyjnego dostępu do pliku nie zawsze jest to lepsze rozwiązanie, chociaż odpowiednie wywołanie
madvise
może złagodzić problem.Musisz być ostrożny z ograniczeniami wyrównania w swojej architekturze (SPARC, itanium), w przypadku we / wy odczytu / zapisu bufory są często odpowiednio wyrównane i nie pułapkują podczas dereferencji rzutowanego wskaźnika.
Musisz także uważać, aby nie uzyskać dostępu poza mapą. Może się to łatwo zdarzyć, jeśli użyjesz funkcji ciągów na mapie, a plik nie będzie zawierał \ 0 na końcu. Będzie działał przez większość czasu, gdy rozmiar pliku nie jest wielokrotnością rozmiaru strony, ponieważ ostatnia strona jest wypełniona cyfrą 0 (obszar odwzorowany ma zawsze rozmiar wielokrotności rozmiaru strony).
źródło
Oprócz innych fajnych odpowiedzi cytat z programowania systemu Linux napisany przez eksperta Google, Roberta Love'a:
źródło
Mapowanie pamięci ma potencjalnie ogromną przewagę prędkości w porównaniu do tradycyjnego IO. Pozwala systemowi operacyjnemu odczytać dane z pliku źródłowego po dotknięciu stron w pliku odwzorowanym w pamięci. Działa to poprzez tworzenie stron zawierających błędy, które system operacyjny wykrywa, a następnie system operacyjny automatycznie ładuje odpowiednie dane z pliku.
Działa to w taki sam sposób, jak mechanizm stronicowania i jest zwykle optymalizowane pod kątem szybkich operacji we / wy, odczytując dane na temat granic i rozmiarów stron systemowych (zwykle 4K) - rozmiaru, dla którego zoptymalizowana jest większość pamięci podręcznych systemu plików.
źródło
pread
. W Solarisie 9 Sparc (V890) dostęp do pready jest od 2 do 3 razy wolniejszy niżmemcpy
z mapy. Ale masz rację, że sekwencyjny dostęp nie jest koniecznie szybszy.Zaletą, której jeszcze nie ma na liście, jest możliwość
mmap()
utrzymania mapowania tylko do odczytu jako czystych stron. Jeśli ktoś przydzieli bufor w przestrzeni adresowej procesu, a następnie użyje goread()
do wypełnienia bufora z pliku, strony pamięci odpowiadające temu buforowi są teraz brudne, ponieważ zostały zapisane.Jądro nie może usunąć brudnych stron z pamięci RAM. Jeśli jest miejsce na zamianę, można je przywołać w celu zamiany. Jest to jednak kosztowne i w niektórych systemach, takich jak małe urządzenia wbudowane z tylko pamięcią flash, w ogóle nie ma wymiany. W takim przypadku bufor utknie w pamięci RAM, dopóki proces się nie zakończy lub może go zwróci
madvise()
.Nie zapisane na
mmap()
stronach są czyste. Jeśli jądro potrzebuje pamięci RAM, może je po prostu upuścić i użyć pamięci RAM, w której były strony. Jeśli proces, który miał mapowanie, ponownie do niego dostęp, spowoduje błąd strony, jądro ponownie ładuje strony z pliku, z którego pochodzi. . W ten sam sposób zostały zaludnione.Nie wymaga to więcej niż jednego procesu z wykorzystaniem mapowanego pliku.
źródło
read()
ze stron, na które są ostatecznie umieszczane dane, nie ma związku z plikiem, z którego mogły pochodzić. Dlatego nie można ich zapisać, z wyjątkiem zamiany miejsca. Jeśli plik jestmmap()ed
, a mapowanie jest zapisywalne (w przeciwieństwie do tylko do odczytu) i zapisywane, to zależy to od tego, czy mapowanie byłoMAP_SHARED
czyMAP_PRIVATE
. Wspólne mapowanie może / musi zostać zapisane do pliku, ale prywatne nie może.