Eksperymentujemy ze zmianą SQLite, wbudowanego systemu baz danych, , aby użyć mmap() zamiast zwykłych wywołań read() i write() w celu uzyskania dostępu do bazy danych plik na dysku. Używanie jednego dużego odwzorowania dla całego pliku . Załóżmy, że plik jest wystarczająco mały, abyśmy nie mieli problemu ze znalezieniem dla niego miejsca w pamięci wirtualnej.Jak rozszerzyć dostępnie do pliku dostępnego za pomocą mmap()
Jak dotąd tak dobrze. W wielu przypadkach użycie mmap() wydaje się być trochę szybsze niż read() i write(). A w niektórych przypadkach znacznie szybciej.
Zmiana rozmiaru mapowania w celu zatwierdzenia transakcji zapisu, która rozszerza plik bazy danych o , wydaje się być problemem. W celu przedłużenia plik bazy danych, kod może zrobić coś takiego:
ftruncate(); // extend the database file on disk
munmap(); // unmap the current mapping (it's now too small)
mmap(); // create a new, larger, mapping
skopiuj nowe dane do końca nowego mapowania pamięci. Jednak munmap/mmap jest niepożądany, ponieważ oznacza, że następnym razem każda strona pliku bazy danych jest dostępna, występuje błąd małej strony i system musi przeszukać pamięć podręczną strony OS dla poprawnej ramki do powiązana z wirtualną adres pamięci. Innymi słowy, spowalnia ona kolejne odczyty bazy danych.
W systemie Linux możemy użyć niestandardowego wywołania systemowego mremap() zamiast z munmap()/mmap(), aby zmienić rozmiar odwzorowania. Wydaje się, że unika to błędów związanych z mniejszą stroną.
PYTANIE: Jak to zrobić w przypadku innych systemów, takich jak OSX, , które nie mają mremap()?
Mamy obecnie dwie pomysły. I pytanie dotyczące każdego z nich:
1) Utwórz odwzorowania większe niż plik bazy danych. Następnie, rozszerzając plik bazy danych o , po prostu wywołaj funkcję ftruncate(), aby rozszerzyć plik na dysk i dalej używać tego samego odwzorowania.
To byłoby idealne i wydaje się działać w praktyce. Jednak jesteśmy martwi tego ostrzeżenia w manualu:
„Wpływ zmiany rozmiaru pliku bazowych mapowania na stronach, które odpowiadają dodanych lub usuniętych regionów plik jest nieokreślona . "
PYTANIE: Czy to coś, o co powinniśmy się martwić? A może anachronizm w tym momencie?
2) Jeżeli rozszerzenie pliku bazy danych, wykorzystanie pierwszego argumentu mmap() żądania odwzorowania odpowiadające nowej strony pliku bazy umieszczony bezpośrednio po bieżącej mapowania w wirtualnej pamięci. Skuteczne rozszerzenie początkowego mapowania. Jeśli system nie może spełnić żądania umieszczenia nowego odwzorowania natychmiast po pierwszym, cofnij się do munmap/mmap.
W praktyce okazało się, że OSX jest całkiem niezły w pozycjonowaniu mapowania w ten sposób, więc ta sztuczka działa tam.
pytanie: Jeśli system ma natychmiast przeznaczyć drugie odwzorowanie po pierwsze w pamięci wirtualnej, to jest wtedy bezpieczne ostatecznie unmap nimi zarówno przy użyciu jednego dużego wezwanie do munmap()?
Robiłem dokładnie to samo. W systemie Solaris 10 'munmap' wykonuje synchroniczne' msync', jeśli dobrze pamiętam. W rzeczywistości 'msync' był zawsze synchroniczny w systemie Solaris 10, nawet jeśli podano' MS_ASYNC'. To były dwa ostatnie gwoździe w trumnie Solaris. –
Nie sądzę, że # 1 jest wykonalne. Utworzenie odwzorowania większego niż plik spowoduje, że koniec pliku nie będzie dostępny (chociaż może być "odwzorowany"), a 'ftruncate()' nie zaktualizuje odwzorowania. – twalberg