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 * .lib oder * .dll oder in die * .exe 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 gehe davon aus, dass die DLL einen Abschnitt für ihre globalen und statischen Elemente hat. 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 ich zwei Module in meiner Anwendung habe, die beide A und B verwenden, werden Kopien der Globals von A und B wie unten erwähnt erstellt (wenn 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 bestimmte DLL lädt, erhält eine eigene Kopie der globalen Variablen dieser DLL. Der Umfang statischer Variablen ist auf den Block beschränkt, in dem die statischen Variablen deklariert sind. Infolgedessen verfügt jeder Prozess standardmäßig über eine eigene Instanz der globalen und statischen DLL-Variablen.
und von hier :
Bei der dynamischen Verknüpfung von Modulen kann unklar sein, ob verschiedene Bibliotheken ihre eigenen Instanzen von Globals haben oder ob die Globals gemeinsam genutzt werden.
Vielen Dank.
Antworten:
Dies ist ein ziemlich berühmter Unterschied zwischen Windows- und Unix-ähnlichen Systemen.
Egal was:
Das Hauptproblem hier ist also die Sichtbarkeit .
In allen Fällen sind
static
globale Variablen (oder Funktionen) niemals von außerhalb eines Moduls (dll / so oder ausführbar) sichtbar. Der C ++ - Standard verlangt, dass diese über eine interne Verknüpfung verfügen, was bedeutet, 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.Bei Windows (.exe und .dll) sind die
extern
globalen Variablen nicht Teil der exportierten Symbole. Mit anderen Worten, verschiedene Module kennen in keiner Weise globale Variablen, die in anderen Modulen definiert sind. Dies bedeutet, dass Sie Linkerfehler erhalten, wenn Sie beispielsweise versuchen, eine ausführbare Datei zu erstellen, die eineextern
in einer DLL definierte Variable verwenden soll , da dies nicht zulässig ist. Sie müßten eine Objektdatei (oder statische Bibliothek) mit einer Definition dieser externen Variablen bieten und sie verknüpfen statisch mit sowohl der ausführbaren Datei und den DLL, in zwei verschiedenen globalen Variablen resultierende (eine Zugehörigkeit zu der ausführbaren Datei und einem an den DLL gehör ).Um eine globale Variable in Windows tatsächlich zu exportieren, müssen Sie eine Syntax verwenden, die der Funktion Export / Import-Syntax ähnelt, dh:
Wenn Sie dies tun, 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)
.so
exportieren die dynamischen Bibliotheken, die als "gemeinsam genutzte Objekte" mit der Erweiterung bezeichnet werden, alleextern
globalen Variablen (oder Funktionen). In diesem Fall werden die globalen Variablen gemeinsam genutzt, dh wenn sie während der Ladezeit von einer beliebigen Stelle aus mit einer gemeinsam genutzten Objektdatei verknüpft werden. 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 auf der ganzen Linie: Eineextern
globale Variable wird von allen Modulen gemeinsam genutzt, was bedeutet, dass sie nur eine Definition für alle geladenen Module haben sollte.In beiden Fällen können Sie für Windows- oder Unix-ähnliche Systeme die dynamische Bibliothek zur Laufzeit verknüpfen, dh entweder mit
LoadLibrary()
/GetProcAddress()
/FreeLibrary()
oderdlopen()
/dlsym()
/dlclose()
. In diesem Fall müssen Sie manuell einen Zeiger auf jedes der Symbole erhalten, die Sie verwenden möchten, und dies schließt die globalen Variablen ein, die Sie verwenden möchten. Für globale Variablen können Sie Funktionen verwendenGetProcAddress()
oderdlsym()
genauso verwenden wie für Funktionen, 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, dass der von Ihnen zitierte Text (über "unklare" Dinge) genau auf die plattformspezifischen Unterschiede verweist, die ich gerade erklärt habe (dynamische Bibliotheken sind nicht wirklich durch den C ++ - Standard definiert, dies ist plattformspezifisches Gebiet, was bedeutet ist viel weniger zuverlässig / tragbar).
quelle
__attribute__((visibility("default")))
), teilt A / B dieselbe st_var. Wenn die Klasse jedoch mit definiert ist__attribute__((visibility("hidden")))
, haben Modul A und Modul B eine eigene Kopie, die nicht gemeinsam genutzt wird.