Laden von gemeinsam genutzten Bibliotheken und RAM-Nutzung

40

Ich frage mich, wie Linux gemeinsam genutzte Bibliotheken verwaltet. (Eigentlich spreche ich von Maemo Fremantle, einer Debian-basierten Distribution, die 2009 auf 256 MB RAM veröffentlicht wurde.)

Nehmen wir an, wir haben zwei ausführbare Dateien, die mit libQtCore.so.4 verknüpft sind und deren Symbole (mithilfe ihrer Klassen und Funktionen) verwenden. Nennen wir sie der Einfachheit halber aund b. Wir gehen davon aus, dass beide ausführbaren Dateien auf die gleichen Bibliotheken verweisen.

Zuerst starten wir a. Die Bibliothek muss geladen werden. Ist es vollständig geladen oder wird es nur in dem Teil in den Speicher geladen, der benötigt wird (da wir nicht jede Klasse verwenden, wird nur der Code für die verwendeten Klassen geladen)?

Dann starten wir b. Wir gehen davon aus, dass adas noch läuft. bverlinkt auch auf libQtCore.so.4 und verwendet einige der Klassen, die averwendet werden, aber auch einige, die nicht von verwendet werden a. Wird die Bibliothek doppelt geladen (getrennt für aund getrennt für b)? Oder verwenden sie dasselbe Objekt, das sich bereits im RAM befindet. Wenn bkeine neuen Symbole verwendet werden und abereits ausgeführt werden, wird der von gemeinsam genutzten Bibliotheken verwendete Arbeitsspeicher erhöht? (Oder wird der Unterschied unwesentlich sein)

marmistrz
quelle

Antworten:

53

HINWEIS: Ich gehe davon aus, dass Ihr Computer über eine Memory Mapping Unit (MMU) verfügt. Es gibt eine Linux-Version (µClinux), für die keine MMU erforderlich ist, und diese Antwort gilt dort nicht.

Was ist eine MMU? Dies ist Hardware - ein Teil des Prozessors und / oder des Speichercontrollers. Um die Verknüpfung von gemeinsam genutzten Bibliotheken zu verstehen, müssen Sie nicht genau wissen, wie eine MMU funktioniert, nur dass eine MMU einen Unterschied zwischen logischen Speicheradressen (die von Programmen verwendet werden) und physischen Adressen zulässtSpeicheradressen (die tatsächlich auf dem Speicherbus vorhandenen). Der Arbeitsspeicher ist in Seiten unterteilt, die unter Linux normalerweise 4 KB groß sind. Bei 4k-Seiten sind die logischen Adressen 0 bis 4095 Seite 0, die logischen Adressen 4096 bis 8191 Seite 1 usw. Die MMU ordnet diese physischen Seiten des RAM zu, und jede logische Seite kann normalerweise 0 oder 1 physischen Seiten zugeordnet werden. Eine bestimmte physische Seite kann mehreren logischen Seiten entsprechen (so wird der Speicher gemeinsam genutzt: Mehrere logische Seiten entsprechen derselben physischen Seite). Beachten Sie, dass dies unabhängig vom Betriebssystem gilt. Es ist eine Beschreibung der Hardware.

Beim Prozesswechsel ändert der Kernel die MMU-Seitenzuordnungen, sodass jeder Prozess seinen eigenen Speicherplatz hat. Die Adresse 4096 in Prozess 1000 kann sich von der Adresse 4096 in Prozess 1001 vollständig unterscheiden (und ist dies normalerweise).

So ziemlich immer, wenn Sie eine Adresse sehen, ist es eine logische Adresse. User-Space-Programme beschäftigen sich kaum mit physikalischen Adressen.

Es gibt jetzt auch mehrere Möglichkeiten, Bibliotheken zu erstellen. Angenommen, ein Programm ruft die Funktion foo()in der Bibliothek auf. Die CPU weiß nichts über Symbole oder Funktionsaufrufe - sie weiß nur, wie sie zu einer logischen Adresse springt und den dort gefundenen Code ausführt. Es gibt verschiedene Möglichkeiten, dies zu tun (und ähnliche Möglichkeiten, wenn eine Bibliothek auf ihre eigenen globalen Daten usw. zugreift):

  1. Es könnte eine logische Adresse, unter der es aufgerufen werden kann, fest codieren. Dies erfordert, dass die Bibliothek immer an genau der gleichen logischen Adresse geladen wird. Wenn zwei Bibliotheken dieselbe Adresse benötigen, schlägt die dynamische Verknüpfung fehl und Sie können das Programm nicht starten. Bibliotheken können andere Bibliotheken erfordern, weshalb grundsätzlich jede Bibliothek im System eindeutige logische Adressen haben muss. Es ist jedoch sehr schnell, wenn es funktioniert. (So ​​hat a.out die Dinge gemacht, und die Art der Einrichtung, die das Prelinking macht, irgendwie).
  2. Es könnte eine gefälschte logische Adresse fest codieren und den dynamischen Linker anweisen, beim Laden der Bibliothek die richtige zu bearbeiten. Dies kostet einiges an Zeit beim Laden der Bibliotheken, ist danach aber sehr schnell.
  3. Es könnte eine Indirektionsebene hinzufügen: Verwenden Sie ein CPU-Register , um die logische Adresse zu speichern, unter der die Bibliothek geladen wird, und greifen Sie dann auf alles als Offset von diesem Register aus zu. Dies verursacht bei jedem Zugriff Leistungskosten.

Ziemlich niemand benutzt mehr die Nummer 1, zumindest nicht auf Allzwecksystemen. Auf 32-Bit-Systemen ist es unmöglich, diese eindeutige logische Adressliste beizubehalten (es gibt nicht genug, um sie zu umgehen), und auf 64-Bit-Systemen ist dies ein administrativer Albtraum. Eine Vorverknüpfung erledigt dies jedoch auf Systembasis.

Ob # 2 oder # 3 verwendet wird, hängt davon ab, ob die Bibliothek mit der -fPICOption GCC (Position Independent Code) erstellt wurde. # 2 ist ohne, # 3 ist mit. Im Allgemeinen werden Bibliotheken mit erstellt -fPIC, also ist # 3 das, was passiert.

Weitere Informationen finden Sie in Ulrich Dreppers How to Write Shared Libraries (PDF) .

So kann endlich Ihre Frage beantwortet werden:

  1. Wenn die Bibliothek mit -fPIC (wie es mit ziemlicher Sicherheit sein sollte) erstellt wird, ist die überwiegende Mehrheit der Seiten für jeden Prozess, der sie lädt, genau gleich. Ihre Prozesse aund bgut kann die Bibliothek in unterschiedlichen logischen Adressen laden, aber die auf die gleichen physischen Seiten zeigen wird: der Speicher gemeinsam genutzt wird. Darüber hinaus stimmen die Daten im RAM genau mit den Daten auf der Festplatte überein, sodass sie nur geladen werden können, wenn sie vom Seitenfehler-Handler benötigt werden.
  2. Wenn die Bibliothek ohne erstellt wird -fPIC , müssen die meisten Seiten der Bibliothek über Verknüpfungen bearbeitet werden und unterscheiden sich. Daher müssen sie separate physische Seiten sein (da sie unterschiedliche Daten enthalten). Das heißt, sie werden nicht geteilt. Die Seiten stimmen nicht mit denen auf der Festplatte überein, daher wäre ich nicht überrascht, wenn die gesamte Bibliothek geladen wäre. Es kann natürlich nachträglich auf die Festplatte ausgelagert werden (in der Auslagerungsdatei).

Sie können dies mit dem pmapTool oder direkt durch Einchecken verschiedener Dateien überprüfen /proc. Hier ist zum Beispiel eine (teilweise) Ausgabe von pmap -xzwei verschiedenen neu erzeugten bcs. Beachten Sie, dass die von pmap angezeigten Adressen normalerweise logische Adressen sind:

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

Sie können sehen, dass die Bibliothek in mehreren Teilen geladen ist, und pmap -xSie erhalten Details zu jedem Teil separat. Sie werden feststellen, dass sich die logischen Adressen zwischen den beiden Prozessen unterscheiden. Sie würden vernünftigerweise erwarten, dass sie gleich sind (da dasselbe Programm ausgeführt wird und Computer normalerweise so vorhersehbar sind), aber es gibt eine Sicherheitsfunktion namens Adressraum-Layout-Randomisierung , die sie absichtlich zufällig anordnet.

Sie können anhand des Unterschieds in der Größe (KB) und der residenten Größe (RSS) erkennen, dass nicht das gesamte Bibliothekssegment geladen wurde. Schließlich können Sie sehen, dass für die größeren Zuordnungen Dirty 0 ist, was bedeutet, dass es genau dem entspricht, was sich auf der Festplatte befindet.

Sie können es erneut ausführen. pmap -XXAbhängig von der ausgeführten Kernel-Version wird angezeigt, dass die erste Zuordnung einen Wert Shared_Cleanvon 176 hat, der genau mit dem übereinstimmt, der für die Ausgabe von -XX je nach Kernel-Version unterschiedlich ist RSS. SharedSpeicher bedeutet, dass die physischen Seiten von mehreren Prozessen gemeinsam genutzt werden. Da sie mit dem RSS-Code übereinstimmen, bedeutet dies, dass die gesamte im Speicher befindliche Bibliothek gemeinsam genutzt wird (siehe auch unten für weitere Erläuterungen zu Shared vs. Private):

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


Siehe auch

derobert
quelle
Das heißt, das Prelinking nützt nichts mehr (und die -fPICNutzung hat sich vor einiger Zeit komplett geändert)?
Hauke ​​Laging
@crisron Danke für die Korrekturen. Zu Ihrer Information, Markdown wird für Sie zählen - die gerenderte Ausgabe meiner Wiederholung 1. war korrekt. Außerdem habe ich ein paar Änderungen an Ihrer Arbeit vorgenommen - "Startadresse" ist Fachsprache, ich habe wahrscheinlich Verwirrung gestiftet, indem ich "logisch" in die Mitte gesetzt habe. Ich habe es geändert, um den Jargon loszuwerden. Außerdem entsprechen Seiten diesen Adressen. AFAIK: Es ist nicht möglich, dass diese Adressen jemals eine andere Seite sind. Ich habe es erneut versucht und die Bestellung getauscht, hoffentlich ist das klarer.
Derobert
Verdammt, das ist eine Antwort !!!
Evan Carroll