Ładowanie bibliotek współdzielonych i użycie pamięci RAM


40

Zastanawiam się, w jaki sposób Linux zarządza bibliotekami współdzielonymi. (właściwie mówię o Maemo Fremantle, dystrybucji opartej na Debianie wydanej w 2009 roku, działającej na 256 MB pamięci RAM).

Załóżmy, że mamy dwa pliki wykonywalne łączące się z libQtCore.so.4 i wykorzystujące jego symbole (wykorzystujące jego klasy i funkcje). Dla uproszczenia nazwijmy je ai b. Zakładamy, że oba pliki wykonywalne łączą się z tymi samymi bibliotekami.

Najpierw uruchamiamy a. Biblioteka musi zostać załadowana. Czy jest ładowany w całości, czy jest ładowany do pamięci tylko w wymaganej części (ponieważ nie używamy każdej klasy, ładowany jest tylko kod dotyczący używanych klas)?

Następnie uruchamiamy b. Zakładamy, że anadal działa. bprowadzi również do libQtCore.so.4 i korzysta z niektórych klas, z których akorzysta, ale także z tych, które nie są używane przez a. Czy biblioteka zostanie podwójnie załadowana (osobno dla ai osobno dla b)? Czy będą używać tego samego obiektu już w pamięci RAM. Jeśli bnie używa żadnych nowych symboli i ajuż działa, czy zwiększy się pamięć RAM używana przez biblioteki współdzielone? (Czy różnica będzie nieznaczna)

Odpowiedzi:


53

UWAGA: Zakładam, że twój komputer ma jednostkę mapowania pamięci (MMU). Istnieje wersja systemu Linux (µClinux), która nie wymaga MMU, i ta odpowiedź nie ma zastosowania.

Co to jest MMU? To sprzęt - część procesora i / lub kontrolera pamięci. Zrozumienie łączenia bibliotek współdzielonych nie wymaga dokładnego zrozumienia działania MMU, tylko że MMU pozwala na różnicę między logicznymi adresami pamięci (używanymi przez programy) a fizycznymiadresy pamięci (te faktycznie obecne na magistrali pamięci). Pamięć jest podzielona na strony, zwykle w systemie Linux rozmiar 4K. W przypadku stron o wielkości 4k adresy logiczne 0–4095 to strony 0, adresy logiczne 4096–8191 to strona 1 itd. MMU mapuje je na fizyczne strony pamięci RAM, a każda strona logiczna może być zwykle mapowana na 0 lub 1 strony fizyczne. Dana strona fizyczna może odpowiadać wielu stronom logicznym (tak pamięć jest dzielona: wiele stron logicznych odpowiada tej samej stronie fizycznej). Uwaga: dotyczy to niezależnie od systemu operacyjnego; to opis sprzętu.

Po przełączeniu procesu jądro zmienia odwzorowania stron MMU, dzięki czemu każdy proces ma swoją własną przestrzeń. Adres 4096 w procesie 1000 może być (i zwykle jest) zupełnie inny niż adres 4096 w procesie 1001.

Prawie zawsze, gdy widzisz adres, jest to adres logiczny. Programy przestrzeni użytkownika rzadko zajmują się adresami fizycznymi.

Teraz istnieje wiele sposobów budowania bibliotek. Powiedzmy, że program wywołuje funkcję foo()w bibliotece. Procesor tak naprawdę nic nie wie o symbolach ani wywołaniach funkcji - po prostu wie, jak przeskoczyć na adres logiczny i wykonać dowolny znaleziony tam kod. Można to zrobić na kilka sposobów (i podobne rzeczy mają zastosowanie, gdy biblioteka uzyskuje dostęp do własnych danych globalnych itp.):

  1. Może na stałe zakodować jakiś logiczny adres, pod który można go będzie wywołać. Wymaga to, aby biblioteka zawsze była ładowana dokładnie pod tym samym adresem logicznym. Jeśli dwie biblioteki wymagają tego samego adresu, łączenie dynamiczne kończy się niepowodzeniem i nie można uruchomić programu. Biblioteki mogą wymagać innych bibliotek, więc w zasadzie wymaga to, aby każda biblioteka w systemie miała unikalne adresy logiczne. Jest jednak bardzo szybki, jeśli działa. (W ten sposób działało a.out i rodzaj konfiguracji, którą robi prelinkowanie).
  2. Może na stałe zakodować fałszywy adres logiczny i nakazać dynamicznemu linkerowi, aby dokonał edycji w odpowiednim podczas ładowania biblioteki. To kosztuje sporo czasu podczas ładowania bibliotek, ale potem jest bardzo szybkie.
  3. Może dodać warstwę pośrednią: użyj rejestru procesora do przechowywania logicznego adresu, na który biblioteka jest załadowana, a następnie uzyskaj dostęp do wszystkiego jako przesunięcie od tego rejestru. Nakłada to koszt wydajności dla każdego dostępu.

Prawie nikt już nie używa # 1, przynajmniej nie w systemach ogólnego przeznaczenia. Utrzymywanie tej unikalnej logicznej listy adresów jest niemożliwe w systemach 32-bitowych (nie ma wystarczająco dużo do obejrzenia) i koszmaru administracyjnego w systemach 64-bitowych. Wcześniejsze łączenie robi to jednak dla poszczególnych systemów.

To, czy zostanie użyta # 2 czy # 3, zależy od tego, czy biblioteka została zbudowana z -fPICopcją GCC (kod niezależny od pozycji). # 2 jest bez, # 3 jest z. Ogólnie rzecz biorąc, biblioteki są budowane -fPIC, więc # 3 dzieje się.

Aby uzyskać więcej informacji, zobacz Ulrich Drepper's How to Write Shared Library (PDF) .

Wreszcie można odpowiedzieć na twoje pytanie:

  1. Jeśli biblioteka jest zbudowany z -fPIC (jak to niemal na pewno powinno być), zdecydowana większość stron są dokładnie takie same dla każdego procesu, który ładuje go. Twoje procesy ai bmogą ładować bibliotekę pod różnymi adresami logicznymi, ale będą one wskazywać te same fizyczne strony: pamięć zostanie udostępniona. Ponadto dane w pamięci RAM dokładnie odpowiadają temu, co jest na dysku, więc można je załadować tylko wtedy, gdy jest to potrzebne przez moduł obsługi błędów strony.
  2. Jeśli biblioteka zostanie zbudowana bez niej -fPIC , to okaże się, że większość stron biblioteki będzie wymagać edycji linków i będzie inna. Dlatego muszą to być osobne strony fizyczne (ponieważ zawierają różne dane). Oznacza to, że nie są udostępniane. Strony nie pasują do tego, co jest na dysku, więc nie zdziwiłbym się, gdyby cała biblioteka została załadowana. Można go oczywiście później zamienić na dysk (w pliku wymiany).

Możesz to sprawdzić za pomocą pmapnarzędzia lub bezpośrednio, sprawdzając różne pliki /proc. Na przykład, tutaj jest (częściowe) wyjście pmap -xdwóch różnych nowo spawnowanych bcs. Zauważ, że adresy wyświetlane przez pmap są, jak zwykle, logicznymi adresami:

pmap -x 14739
Address           Kbytes     RSS   Dirty Mode  Mapping
00007f81803ac000     244     176       0 r-x-- libreadline.so.6.2
00007f81803e9000    2048       0       0 ----- libreadline.so.6.2
00007f81805e9000       8       8       8 r---- libreadline.so.6.2
00007f81805eb000      24      24      24 rw--- libreadline.so.6.2


pmap -x 17739
Address           Kbytes     RSS   Dirty Mode  Mapping
00007f784dc77000     244     176       0 r-x-- libreadline.so.6.2
00007f784dcb4000    2048       0       0 ----- libreadline.so.6.2
00007f784deb4000       8       8       8 r---- libreadline.so.6.2
00007f784deb6000      24      24      24 rw--- libreadline.so.6.2

Możesz zobaczyć, że biblioteka jest załadowana na wiele części i pmap -xzawiera szczegółowe informacje na temat każdej z nich osobno. Zauważysz, że logiczne adresy różnią się między dwoma procesami; można oczekiwać, że będą one takie same (ponieważ działa ten sam program, a komputery są zwykle w ten sposób przewidywalne), ale istnieje funkcja bezpieczeństwa zwana randomizacją układu przestrzeni adresowej, która celowo losowo je losuje.

Na podstawie różnicy wielkości (KB) i wielkości rezydenta (RSS) widać, że cały segment biblioteki nie został załadowany. Na koniec widać, że w przypadku większych mapowań wartość „brudna” wynosi 0, co oznacza, że ​​dokładnie odpowiada zawartości dysku.

Możesz uruchomić ponownie pmap -XX, a pokaże ci - w zależności od wersji jądra, z której korzystasz, ponieważ wyjście -XX różni się w zależności od wersji jądra - że pierwsze mapowanie ma Shared_Clean176, które dokładnie odpowiada RSS. Sharedpamięć oznacza, że ​​fizyczne strony są współużytkowane przez wiele procesów, a ponieważ pasuje do kanału RSS, oznacza to, że cała biblioteka znajdująca się w pamięci jest współużytkowana (spójrz do Zobacz też poniżej, aby uzyskać dalsze wyjaśnienia dotyczące współdzielenia i prywatnego):

pmap -XX 17739
         Address Perm   Offset Device   Inode  Size  Rss Pss Shared_Clean Shared_Dirty Private_Clean Private_Dirty Referenced Anonymous AnonHugePages Swap KernelPageSize MMUPageSize Locked                   VmFlagsMapping
    7f784dc77000 r-xp 00000000  fd:00 1837043   244  176  19          176            0             0             0        176         0             0    0              4           4      0       rd ex mr mw me sd  libreadline.so.6.2
    7f784dcb4000 ---p 0003d000  fd:00 1837043  2048    0   0            0            0             0             0          0         0             0    0              4           4      0             mr mw me sd  libreadline.so.6.2
    7f784deb4000 r--p 0003d000  fd:00 1837043     8    8   8            0            0             0             8          8         8             0    0              4           4      0       rd mr mw me ac sd  libreadline.so.6.2
    7f784deb6000 rw-p 0003f000  fd:00 1837043    24   24  24            0            0             0            24         24        24             0    0              4           4      0    rd wr mr mw me ac sd  libreadline.so.6.2


Zobacz też


Oznacza to, że prelinkowanie nie jest już użyteczne (i że -fPICużycie zmieniło się całkowicie jakiś czas temu)?
Hauke ​​Laging

@crisron Dzięki za poprawki. Do twojej wiadomości, Markdown będzie się liczyć - renderowany wynik mojego powtórzenia 1. był poprawny. Wprowadziłem też kilka zmian w tym, co zrobiłeś - „adres początkowy” to techniczny żargon, prawdopodobnie spowodowałem zamieszanie, umieszczając „logiczny” na środku. Zmieniłem to, żeby pozbyć się żargonu. Ponadto strony są równoważne z tymi adresami, AFAIK nie jest możliwe, aby adresy te były kiedykolwiek innymi stronami. Spróbowałem ponownie, zamieniając zamówienie, mam nadzieję, że jest to bardziej zrozumiałe.
derobert

cholera, teraz to odpowiedź !!!
Evan Carroll,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.