Was passiert mit globalen und statischen Variablen in einer Shared Library, wenn sie dynamisch gelinkt wird?

Lesezeit: 9 Minuten

Was passiert mit globalen und statischen Variablen in einer Shared
Raja

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 eingebaut.

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 Verknüpfung zur Ladezeit verwendet? Ich gehe davon aus, dass die DLL einen Abschnitt für ihre Globals und Statics 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 DLLs A und B Zugriff auf die Anwendungsglobals?

(Bitte geben Sie auch Ihre Gründe an)

Zitat von 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 seine eigene Kopie der globalen Variablen dieser DLL. Der Gültigkeitsbereich statischer Variablen ist auf den Block beschränkt, in dem die statischen Variablen deklariert sind. Daher hat jeder Prozess standardmäßig seine eigene Instanz der globalen und statischen DLL-Variablen.

und von Hier:

Beim dynamischen Linken von Modulen kann es unklar sein, ob verschiedene Bibliotheken ihre eigenen Instanzen von Globals haben oder ob die Globals gemeinsam genutzt werden.

Danke.

  • Durch Module meinst du wahrscheinlich Bibliotheken. Es gibt einen Vorschlag zur Ergänzung Module dem C++-Standard mit einer genaueren Definition dessen, was ein Modul sein würde, und einer anderen Semantik als normale Bibliotheken ab sofort.

    – David Rodríguez – Dribeas

    15. Oktober 2013 um 4:14 Uhr

  • Ah, hätte das klarstellen sollen. Ich betrachte verschiedene Projekte in einer Lösung (ich arbeite viel mit Visual Studio) als Module. Diese Module sind in *.lib oder *.dll ‘s eingebaut.

    – Raja

    15. Oktober 2013 um 4:26 Uhr

  • @DavidRodríguez-dribeas Der Begriff „Modul“ ist der korrekte technische Begriff für eigenständige (vollständig verknüpfte) ausführbare Dateien, einschließlich: ausführbare Programme, Dynamic Link Libraries (.dll) oder gemeinsam genutzte Objekte (.so). Es ist hier vollkommen angebracht, und die Bedeutung ist richtig und gut verstanden. Bis es ein Standardfeature mit dem Namen “Module” gibt, bleibt die Definition dafür die traditionelle, wie ich erklärt habe.

    – Michael Persson

    15. Oktober 2013 um 4:28 Uhr

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

Egal was:

  • Jeder Prozess hat seinen eigenen Adressraum, was bedeutet, dass niemals Speicher zwischen Prozessen geteilt wird (es sei denn, Sie verwenden eine Bibliothek oder Erweiterungen für die Kommunikation zwischen Prozessen).
  • Die Eine Definitionsregel (ODR) gilt weiterhin, was bedeutet, dass Sie nur eine Definition der globalen Variablen zur Linkzeit sichtbar haben können (statisches oder dynamisches Linken).

Das Hauptproblem hier ist also wirklich Sichtweite.

Auf alle Fälle, static globale Variablen (oder Funktionen) sind niemals von außerhalb eines Moduls (dll/so oder ausführbar) sichtbar. Der C++-Standard erfordert, 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 also erledigt.

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

Im Fall von Windows (.exe und .dll) ist die extern globale Variablen sind nicht Teil der exportierten Symbole. Mit anderen Worten, unterschiedliche Module kennen in keiner Weise globale Variablen, die in anderen Modulen definiert sind. Das bedeutet, dass Sie Linker-Fehler erhalten, wenn Sie beispielsweise versuchen, eine ausführbare Datei zu erstellen, die eine verwenden soll extern Variable, die in einer DLL definiert ist, da dies nicht zulässig ist. Sie müssten eine Objektdatei (oder statische Bibliothek) mit einer Definition dieser externen Variablen bereitstellen und sie statisch mit verknüpfen beide die ausführbare Datei und die DLL, was zu zwei unterschiedlichen globalen Variablen führt (eine gehört zur ausführbaren Datei und eine gehört zur DLL).

Um eine globale Variable tatsächlich in Windows zu exportieren, müssen Sie eine Syntax verwenden, die der Export/Import-Syntax der Funktion ähnelt, dh:

#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 der Liste der exportierten Symbole hinzugefügt und kann wie alle anderen Funktionen verknüpft werden.

Im Fall von Unix-ähnlichen Umgebungen (wie Linux) die dynamischen Bibliotheken, genannt “Shared Objects” mit der Erweiterung .so alles exportieren extern globale Variablen (oder Funktionen). In diesem Fall, wenn Sie dies tun Ladezeit Verknüpfen von irgendwo zu einer gemeinsam genutzten Objektdatei, dann werden die globalen Variablen gemeinsam genutzt, dh miteinander als eine verknüpft. Grundsätzlich sind Unix-ähnliche Systeme so konzipiert, dass es praktisch keinen Unterschied zwischen dem Linken mit einer statischen oder einer dynamischen Bibliothek gibt. Auch hier gilt ODR allgemein: an extern Die globale Variable wird von allen Modulen gemeinsam genutzt, was bedeutet, dass sie nur eine Definition für alle geladenen Module haben sollte.

Schließlich können Sie in beiden Fällen für Windows- oder Unix-ähnliche Systeme tun Laufzeit Verknüpfen der dynamischen Bibliothek, dh entweder verwenden LoadLibrary() / GetProcAddress() / FreeLibrary() oder dlopen() / dlsym() / dlclose(). In diesem Fall müssen Sie manuell einen Zeiger auf jedes der Symbole erhalten, die Sie verwenden möchten, und dazu gehören auch die globalen Variablen, die Sie verwenden möchten. Für globale Variablen können Sie verwenden GetProcAddress() oder dlsym() genauso wie Sie es für Funktionen tun, vorausgesetzt, dass die globalen Variablen Teil der exportierten Symbolliste sind (nach den Regeln der vorherigen Absätze).

Und natürlich als notwendige Schlussbemerkung: globale Variablen sollten vermieden werden. Und ich glaube, dass sich der von Ihnen zitierte Text (über Dinge, die “unklar” sind) genau auf die plattformspezifischen Unterschiede bezieht, die ich gerade erklärt habe (dynamische Bibliotheken werden nicht wirklich durch den C++-Standard definiert, dies ist plattformspezifisches Gebiet, das heißt es ist viel weniger zuverlässig / portabel).

  • Tolle Antwort, danke! Ich habe ein Follow-up: Da die DLL ein in sich geschlossenes Stück Code und Daten ist, hat sie einen Datensegmentabschnitt ähnlich wie ausführbare Dateien? Ich versuche zu verstehen, wo und wie diese Daten geladen werden, wenn die gemeinsam genutzte Bibliothek verwendet wird.

    – Raja

    15. Oktober 2013 um 18:24 Uhr


  • @Raja Ja, die DLL hat ein Datensegment. In Bezug auf die Dateien selbst sind ausführbare Dateien und DLLs praktisch identisch, der einzige wirkliche Unterschied ist ein Flag, das in der ausführbaren Datei gesetzt wird, um anzuzeigen, dass sie eine „Haupt“-Funktion enthält. Wenn ein Prozess eine DLL lädt, wird sein Datensegment irgendwo in den Adressraum des Prozesses kopiert, und der statische Initialisierungscode (der nicht-triviale globale Variablen initialisieren würde) wird ebenfalls im Adressraum des Prozesses ausgeführt. Das Laden ist dasselbe wie für die ausführbare Datei, außer dass der Adressraum des Prozesses erweitert wird, anstatt einen neuen zu erstellen.

    – Michael Persson

    15. Oktober 2013 um 22:40 Uhr

  • Wie wäre es mit den statischen Variablen, die in einer Inline-Funktion einer Klasse definiert sind? Definieren Sie zB “class A{ void foo() { static int st_var = 0; } }” in der Header-Datei und fügen Sie sie in Modul A und Modul B ein. Wird A/B dieselbe st_var gemeinsam nutzen oder hat jeder seine eigene Kopie?

    – Camino

    23. Juli 2018 um 21:06 Uhr

  • @camino Wenn die Klasse exportiert wird (also definiert mit __attribute__((visibility("default")))), dann teilen sich A/B dieselbe st_var. Aber wenn die Klasse mit definiert ist __attribute__((visibility("hidden")))dann haben Modul A und Modul B eine eigene Kopie, die nicht geteilt wird.

    – Wei Guo

    19. März 2019 um 7:46 Uhr


  • @camino __declspec(dllexport)

    – ruipacheco

    26. September 2019 um 13:46 Uhr

Was passiert mit globalen und statischen Variablen in einer Shared
Deckard 5 Pegasus

Die von Mikael Persson hinterlassene Antwort enthält, obwohl sehr gründlich, einen schwerwiegenden Fehler (oder zumindest irreführend) in Bezug auf die globalen Variablen, der geklärt werden muss. Die ursprüngliche Frage lautete, ob es separate Kopien der globalen Variablen gab oder ob globale Variablen von den Prozessen gemeinsam genutzt wurden.

Die wahre Antwort lautet wie folgt: Es gibt separate (mehrere) Kopien der globalen Variablen für jeden Prozess, und sie werden nicht zwischen Prozessen geteilt. Daher ist die Aussage, dass die One Definition Rule (ODR) gilt, auch sehr irreführend, sie gilt nicht in dem Sinne, dass es sich NICHT um die gleichen Globals handelt, die von jedem Prozess verwendet werden, also ist es in Wirklichkeit nicht “One Definition”. zwischen Prozessen.

Auch wenn globale Variablen für den Prozess nicht “sichtbar” sind, sind sie für den Prozess immer leicht “zugänglich”, da jede Funktion leicht einen Wert einer globalen Variablen an den Prozess oder einen Prozess zurückgeben könnte könnte einen Wert einer globalen Variablen durch einen Funktionsaufruf setzen. Somit ist auch diese Antwort irreführend.

In Wirklichkeit “ja” haben die Prozesse vollen “Zugriff” auf die Globals, zumindest durch die Funktionsaufrufe an die Bibliothek. Aber um es noch einmal zu wiederholen, jeder Prozess hat seine eigene Kopie der Globals, also werden es nicht die gleichen Globals sein, die ein anderer Prozess verwendet.

Daher ist die gesamte Antwort in Bezug auf den externen Export von Globals wirklich kein Thema und unnötig und nicht einmal im Zusammenhang mit der ursprünglichen Frage. Da auf die Globals nicht extern zugegriffen werden muss, kann auf die Globals immer indirekt über Funktionsaufrufe an die Bibliothek zugegriffen werden.

Der einzige Teil, der zwischen den Prozessen geteilt wird, ist natürlich der eigentliche “Code”. Der Code wird nur an einer Stelle im physischen Speicher (RAM) geladen, aber dieselbe physische Speicherstelle wird natürlich in die “lokalen” virtuellen Speicherstellen jedes Prozesses abgebildet.

Im Gegensatz dazu verfügt eine statische Bibliothek über eine Kopie des Codes für jeden Prozess, der bereits in die ausführbare Datei (ELF, PE usw.) eingebacken ist, und hat natürlich wie dynamische Bibliotheken separate Globals für jeden Prozess.

  • Danke! Ich war so verwirrt, was ODR und Namenssichtbarkeit mit irgendetwas zu tun hatten.

    – Johannes Cramerus

    16. Februar um 16:25 Uhr

In Unix-Systemen:

Es ist zu beachten, dass sich der Linker nicht beschwert, wenn zwei dynamische Bibliotheken dieselben globalen Variablen exportieren. aber während der Ausführung kann es je nach Zugriffsverletzungen zu einem Segfault kommen. Eine übliche Zahl, die dieses Verhalten zeigt, wäre Segmentierungsfehler 15

segfault at xxxxxx ip xxxxxx sp xxxxxxx error 15 in a.out

985740cookie-checkWas passiert mit globalen und statischen Variablen in einer Shared Library, wenn sie dynamisch gelinkt wird?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy