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.):
- 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).
- 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.
- 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 -fPIC
opcją 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:
- 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 a
i b
mogą ł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.
- 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ą pmap
narzędzia lub bezpośrednio, sprawdzając różne pliki /proc
. Na przykład, tutaj jest (częściowe) wyjście pmap -x
dwóch różnych nowo spawnowanych bc
s. 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 -x
zawiera 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_Clean
176, które dokładnie odpowiada RSS
. Shared
pamięć 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ż
-fPIC
użycie zmieniło się całkowicie jakiś czas temu)?