J'essaie de comprendre ce qui se passe lorsque des modules contenant des variables globales et statiques sont liés dynamiquement à une application. Par modules, j'entends chaque projet dans une solution (je travaille beaucoup avec Visual Studio!). Ces modules sont soit intégrés à * .lib ou * .dll ou au * .exe lui-même.
Je comprends que le binaire d'une application contient des données globales et statiques de toutes les unités de traduction individuelles (fichiers objets) du segment de données (et ne lit que le segment de données si const).
Que se passe-t-il lorsque cette application utilise un module A avec une liaison dynamique au moment du chargement? Je suppose que le DLL a une section pour ses globaux et sa statique. Est-ce que le système d'exploitation les charge? Si oui, où sont-ils chargés?
Et que se passe-t-il lorsque l'application utilise un module B avec une liaison dynamique au moment de l'exécution?
Si deux modules de ma candidature utilisent A et B, des copies des fonctions globales de A et B sont-elles créées comme indiqué ci-dessous (s’il s’agit de processus différents)?
Les DLL A et B ont-elles accès aux applications globales?
(Merci d'indiquer vos raisons également)
Citant de MSDN :
Les variables déclarées globales dans un fichier de code source DLL sont traitées comme des variables globales par le compilateur et l'éditeur de liens, mais chaque processus qui charge un DLL donné obtient sa propre copie de cette DLL. variables globales. La portée des variables statiques est limitée au bloc dans lequel les variables statiques sont déclarées. Par conséquent, chaque processus a sa propre instance des variables globales et statiques DLL par défaut.
et de ici :
Lors de la liaison dynamique de modules, il peut être difficile de savoir si différentes bibliothèques ont leurs propres instances de globals ou si les globals sont partagés.
Merci.
C'est une différence assez célèbre entre Windows et les systèmes de type Unix.
Peu importe ce que:
La question clé ici est donc vraiment la visibilité .
Dans tous les cas, static
variables globales (ou fonctions) ne sont jamais visibles de l'extérieur d'un module (dll/so ou exécutable). La norme C++ exige que ces derniers disposent d'un lien interne, ce qui signifie qu'ils ne sont pas visibles en dehors de l'unité de traduction (qui devient un fichier objet) dans laquelle ils sont définis. Donc, cela règle la question.
Cela devient compliqué lorsque vous avez extern
variables globales. Ici, les systèmes Windows et Unix sont complètement différents.
Dans le cas de Windows (.exe et .dll), les variables globales extern
ne font pas partie des symboles exportés. En d'autres termes, les différents modules ne sont en aucun cas conscients des variables globales définies dans d'autres modules. Cela signifie que vous obtiendrez des erreurs de l'éditeur de liens si vous essayez, par exemple, de créer un fichier exécutable censé utiliser une variable extern
définie dans une DLL, car cela n'est pas autorisé. Vous devez fournir un fichier objet (ou une bibliothèque statique) avec une définition de cette variable externe et le lier statiquement avec à la fois à l'exécutable et à la DLL, résultant en deux variables globales distinctes (une appartenant à l'exécutable et l'autre à la DLL).
Pour exporter une variable globale sous Windows, vous devez utiliser une syntaxe similaire à la syntaxe de la fonction export/import, à savoir:
#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;
Lorsque vous faites cela, la variable globale est ajoutée à la liste des symboles exportés et peut être liée comme toutes les autres fonctions.
Dans le cas d'environnements de type Unix (comme Linux), les bibliothèques dynamiques, appelées "objets partagés" avec l'extension .so
exportent toutes les variables extern
globales (ou fonctions). Dans ce cas, si vous reliez au moment du chargement de n'importe où à un fichier objet partagé, les variables globales sont partagées, c'est-à-dire reliées ensemble. . Fondamentalement, les systèmes de type Unix sont conçus pour faire en sorte qu’il n’y ait pratiquement aucune différence entre la liaison avec une bibliothèque statique ou dynamique. Là encore, l'ODR s'applique à tous les niveaux: une variable globale extern
sera partagée entre les modules, ce qui signifie qu'elle ne devrait avoir qu'une seule définition parmi tous les modules chargés.
Enfin, dans les deux cas, pour les systèmes Windows ou Unix, vous pouvez effectuer une liaison au moment de l'exécution de la bibliothèque dynamique, c'est-à-dire en utilisant LoadLibrary()
/GetProcAddress()
/FreeLibrary()
ou dlopen()
/dlsym()
/dlclose()
. Dans ce cas, vous devez placer manuellement un pointeur sur chacun des symboles que vous souhaitez utiliser, ce qui inclut les variables globales que vous souhaitez utiliser. Pour les variables globales, vous pouvez utiliser GetProcAddress()
ou dlsym()
de la même manière que pour les fonctions, à condition que les variables globales fassent partie de la liste des symboles exportés (selon les règles des paragraphes précédents).
Et bien sûr, comme note finale nécessaire: les variables globales doivent être évitées. Et je crois que le texte que vous avez cité (à propos des choses "peu claires") fait exactement référence aux différences spécifiques à la plate-forme que je viens d'expliquer (les bibliothèques dynamiques ne sont pas vraiment définies par la norme C++, il s'agit d'un territoire spécifique à la plate-forme, ce qui signifie est beaucoup moins fiable/portable).