wake-up-neo.com

Was passiert mit globalen und statischen Variablen in einer gemeinsam genutzten Bibliothek, wenn diese dynamisch verknüpft wird?

Ich versuche zu verstehen, was passiert, wenn Module mit globalen und statischen Variablen dynamisch mit einer Anwendung verknüpft werden. Mit Modulen meine ich jedes Projekt in einer Lösung (ich arbeite viel mit Visual Studio!). Diese Module sind entweder in die * .lib- oder * .dll-Datei oder in die * .exe-Datei selbst integriert.

Ich verstehe, dass die Binärdatei einer Anwendung globale und statische Daten aller einzelnen Übersetzungseinheiten (Objektdateien) im Datensegment enthält (und schreibgeschütztes Datensegment, wenn const).

  • Was passiert, wenn diese Anwendung ein Modul A mit dynamischer Ladezeitverknüpfung verwendet? Ich nehme an, die DLL hat einen Abschnitt für ihre Globals und Statics. Lädt das Betriebssystem sie? Wenn ja, wohin werden sie geladen?

  • Und was passiert, wenn die Anwendung ein Modul B mit dynamischer Laufzeitverknüpfung verwendet?

  • Wenn meine Anwendung zwei Module enthält, die A und B verwenden, werden dann Kopien der globalen Elemente von A und B wie unten beschrieben erstellt (sofern es sich um unterschiedliche Prozesse handelt)?

  • Erhalten die DLLs A und B Zugriff auf die globalen Anwendungen?

(Bitte geben Sie auch Ihre Gründe an)

Zitat aus MSDN :

Variablen, die in einer DLL Quellcodedatei als global deklariert sind, werden vom Compiler und Linker als globale Variablen behandelt, aber jeder Prozess, der eine gegebene DLL lädt, erhält Eine eigene Kopie der globalen Variablen dieser DLL. Der Gültigkeitsbereich der statischen Variablen ist auf den Block beschränkt, in dem die statischen Variablen deklariert werden. Daher verfügt jeder Prozess über eine eigene Instanz der globalen DLL und statische Variablen standardmäßig.

und von hier :

Beim dynamischen Verknüpfen von Modulen kann unklar sein, ob verschiedene Bibliotheken eigene Instanzen von Globals haben oder ob die Globals gemeinsam genutzt werden.

Vielen Dank.

109
Raja

Dies ist ein ziemlich berühmter Unterschied zwischen Windows- und Unix-ähnlichen Systemen.

Egal was:

  • Jeder Prozess hat einen eigenen Adressraum, was bedeutet, dass zwischen Prozessen niemals Speicher freigegeben wird (es sei denn, Sie verwenden eine prozessübergreifende Kommunikationsbibliothek oder -erweiterungen). .
  • Die One Definition Rule (ODR) gilt weiterhin, dh Sie können nur eine Definition der globalen Variablen zum Zeitpunkt der Verknüpfung anzeigen lassen (statische oder dynamische Verknüpfung) ).

Das Hauptproblem hier ist also wirklich Sichtbarkeit .

In allen Fällen sind static globale Variablen (oder Funktionen) außerhalb eines Moduls (dll/so oder ausführbare Datei) niemals sichtbar. Der C++ - Standard verlangt, dass diese über eine interne Verknüpfung verfügen, dh dass sie außerhalb der Übersetzungseinheit (die zu einer Objektdatei wird), in der sie definiert sind, nicht sichtbar sind. Damit ist das Problem gelöst.

Kompliziert wird es, wenn Sie extern globale Variablen haben. Hier sind Windows- und Unix-ähnliche Systeme völlig unterschiedlich.

Im Fall von Windows (.exe und .dll) sind die globalen Variablen extern nicht Teil der exportierten Symbole. Mit anderen Worten, verschiedene Module kennen keine globalen Variablen, die in anderen Modulen definiert sind. Dies bedeutet, dass Sie Linker-Fehler erhalten, wenn Sie beispielsweise versuchen, eine ausführbare Datei zu erstellen, die eine in einer DLL definierte extern -Variable verwenden soll, da dies nicht zulässig ist. Sie müssten eine Objektdatei (oder statische Bibliothek) mit einer Definition dieser externen Variablen versehen und statisch mit sowohl der ausführbaren Datei als auch der DLL verknüpfen. Dies führt zu zwei unterschiedlichen globalen Variablen (eine zur ausführbaren Datei und eine zur DLL).

Um eine globale Variable in Windows tatsächlich zu exportieren, müssen Sie eine Syntax verwenden, die der Syntax für den Export/Import von Funktionen ähnelt, d. H .:

#ifdef COMPILING_THE_DLL
#define MY_DLL_EXPORT extern "C" __declspec(dllexport)
#else
#define MY_DLL_EXPORT extern "C" __declspec(dllimport)
#endif

MY_DLL_EXPORT int my_global;

Dabei wird die globale Variable zur Liste der exportierten Symbole hinzugefügt und kann wie alle anderen Funktionen verknüpft werden.

In Unix-ähnlichen Umgebungen (wie Linux) exportieren die dynamischen Bibliotheken, die "Shared Objects" mit der Erweiterung .so Genannt werden, alle extern globalen Variablen (oder Funktionen). In diesem Fall werden die globalen Variablen gemeinsam genutzt, dh als eine einzige verknüpft, wenn Sie eine Verknüpfung von einer beliebigen Stelle zu einer gemeinsam genutzten Objektdatei während des Ladevorgangs ausführen . Grundsätzlich sind Unix-ähnliche Systeme so konzipiert, dass es praktisch keinen Unterschied zwischen der Verknüpfung mit einer statischen oder einer dynamischen Bibliothek gibt. Auch hier gilt ODR in allen Modulen: Eine globale Variable extern wird von allen Modulen gemeinsam verwendet. Dies bedeutet, dass für alle geladenen Module nur eine Definition gelten sollte.

Schließlich können Sie in beiden Fällen für Windows- oder Unix-ähnliche Systeme die dynamische Bibliothek zur Laufzeit verknüpfen , dh entweder LoadLibrary()/GetProcAddress()/FreeLibrary() oder dlopen()/dlsym()/dlclose(). In diesem Fall müssen Sie manuell einen Zeiger auf jedes der Symbole abrufen, die Sie verwenden möchten, einschließlich der globalen Variablen, die Sie verwenden möchten. Für globale Variablen können Sie GetProcAddress() oder dlsym() genau wie für Funktionen verwenden, vorausgesetzt, die globalen Variablen sind Teil der exportierten Symbolliste (gemäß den Regeln der vorherigen) Absätze).

Und natürlich als notwendige letzte Anmerkung: globale Variablen sollten vermieden werden. Und ich glaube, der Text, den Sie zitiert haben (über Dinge, die "unklar" sind), bezieht sich genau auf die plattformspezifischen Unterschiede, die ich gerade erklärt habe (dynamische Bibliotheken sind nicht wirklich durch den C++ - Standard definiert, das ist plattformspezifisches Gebiet, das heißt ist viel weniger zuverlässig/tragbar).

153
Mikael Persson