dynamic链接时共享库中的全局variables和静态variables会发生什么变化?

我想了解当全局variables和静态variables模块dynamic链接到应用程序时会发生什么。 通过模块,我的意思是每个项目在一个解决scheme(我工作了很多与Visual Studio!)。 这些模块是内置到* .lib或* .dll或* .exe本身。

我知道应用程序的二进制文件包含数据段中所有单个翻译单元(目标文件)的全局和静态数据(如果是const,则只读数据段)。

  • 当这个应用程序使用带有加载时间dynamic链接的模块A时会发生什么? 我假设DLL有一个全局和静态的部分。 操作系统是否加载它们? 如果是这样,他们在哪里装载?

  • 当应用程序使用带有运行时dynamic链接的模块B时会发生什么?

  • 如果我的应用程序中有两个模块都使用A和B,那么是如下所述创build的A和B的全局variables的副本(如果它们是不同的进程)?

  • A和B是否可以访问应用程序全局variables?

(请说明你的理由)

从MSDN引用:

在DLL源代码文件中声明为全局的variables被编译器和链接器视为全局variables,但是加载给定DLL的每个进程都会获得该DLL全局variables的副本。 静态variables的范围仅限于声明静态variables的块。 因此,默认情况下,每个进程都有自己的DLL全局和静态variables的实例。

并从这里 :

在dynamic链接模块时,不清楚不同的库是否有自己的全局实例或者全局variables是否共享。

谢谢。

这是Windows和类Unix系统之间的一个非常有名的区别。

无论:

  • 每个进程都有自己的地址空间,这意味着进程之间永远不会共享任何内存(除非使用某些进程间通信库或扩展)。
  • 一个定义规则 (ODR)仍然适用,这意味着在链接时只能有一个全局variables定义(静态链接或dynamic链接)。

所以,这里的关键问题是真正的知名度

在所有情况下, static全局variables(或函数)从模块外部(dll / so或者可执行文件)永远不可见。 C ++标准要求它们具有内部链接,这意味着它们在定义它们的翻译单元(它成为目标文件)之外是不可见的。 那么,这个问题就解决了。

当你有extern全局variables时,它变得复杂。 在这里,Windows和类Unix系统是完全不同的。

对于Windows(.exe和.dll), extern全局variables不是导出符号的一部分。 换句话说,不同的模块并不知道其他模块中定义的全局variables。 这意味着如果您尝试创build一个应该使用DLL中定义的externvariables的可执行文件,则会出现链接错误,因为这是不允许的。 您需要提供一个带有该externvariables定义的对象文件(或静态库),并将其与可执行文件和DLL静态链接,从而生成两个不同的全局variables(一个属于可执行文件,一个属于DLL )。

要在Windows中实际导出全局variables,必须使用类似于函数export / import语法的语法,即:

 #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; 

当你这样做,全局variables被添加到导出的符号列表,并可以像所有其他function一样链接。

在类Unix环境(如Linux)中,dynamic库(称为“共享对象”,扩展名为.so导出所有extern全局variables(或函数)。 在这种情况下,如果你从任何地方加载时间链接到一个共享对象文件,那么全局variables是共享的,即作为一个整体链接在一起。 基本上,类Unix系统的devise使得与静态或dynamic库链接几乎没有区别。 同样,ODR全面应用:一个extern全局variables将被跨模块共享,这意味着它应该只有一个跨所有加载模块的定义。

最后,在这两种情况下,对于Windows或类Unix系统,可以使用LoadLibrary() / GetProcAddress() / FreeLibrary()dlopen() / dlsym() / dlclose() 。 在这种情况下,您必须手动获取您希望使用的每个符号的指针,并且包含您希望使用的全局variables。 对于全局variables,只要全局variables是导出符号列表的一部分(按照前面段落的规则dlsym()就可以像使用函数一样使用GetProcAddress()dlsym() )。

当然,作为一个必要的最后说明: 应该避免全局variables 。 我相信你所引用的文本(关于事物“不清楚”)恰恰是指我刚刚解释的特定于平台的差异(dynamic库不是真正由C ++标准定义的,这是特定于平台的领域,意味着它是不太可靠/便携式)。