i007.cc

i007.cc

优先队列-降维打击

当动态链接时,共享库中的全局变量和静态变量会发生什么?

我试图理解当全局变量和静态变量动态链接到应用程序时会发生什么。
通过模块,我的意思是每个项目在一个解决方案(我的工作很多与visual studio!)。这些模块内置在* .lib或* .dll或* .exe本身中。

我理解应用程序的二进制文件包含全部和所有

 

    • 当数据段中的个别翻译单元(目标文件)此应用程序使用带有加载时动态链接的模块A?我假设DLL有一个部分为其全局和静态。操作系统是否加载它们?

 

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

      / li>

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


b

(请说明您的原因)

引用 MSDN 

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

 

此处

链接模块,可以不清楚不同的库是否有自己的全局变量实例或者是否共享全局变量。

p>

解决方案

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

无论如何:

    • 每个进程都有自己的地址空间,

 

    • 一个定义规则(ODR)仍然适用,这意味着您可以只有链接时可见的全局变量的一个定义(静态或动态链接)。


在所有情况下,全局变量(或函数)都是 static 从模块外部不可见(dll / so或可执行)。 C ++标准要求这些具有内部链接,这意味着它们在翻译单元(它变成一个目标文件)之外是不可见的。

复杂的是当你有 extern 全局变量时。

在Windows(.exe和.dll)的情况下, extern 全局变量不是导出符号的一部分。换句话说,不同的模块不知道在其他模块中定义的全局变量。这意味着,如果您尝试创建一个应该使用在DLL中定义的 extern 变量的可执行文件,那么您将会遇到链接器错误,因为这是不允许的。您需要提供一个具有该extern变量定义的对象文件(或静态库),并将它与可执行文件和DLL静态链接,从而产生两个不同的全局变量(一个属于可执行文件和一个属于DLL的文件)。

要在Windows中实际导出全局变量,必须使用类似于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; 
  

当这样做时,全局变量被添加到导出符号列表,所有其他函数。

 

在类Unix环境(如Linux)的情况下,动态库称为“共享对象”扩展 .so export all extern 全局变量(或函数)。在这种情况下,如果您从任何位置到共享对象文件进行加载时链接,那么全局变量是共享的,即链接在一起。基本上,类Unix系统被设计成使得它与静态或动态库链接之间几乎没有区别。同样,ODR适用于所有模块: extern 全局变量将跨模块共享,这意味着它应该在所有加载的模块中只有一个定义。

最后,在这两种情况下,对于Windows或类Unix系统,您可以对动态库进行运行时链接,即使用 LoadLibrary() / GetProcAddress() / FreeLibrary() c $ c> dlopen() / dlsym() / dlclose()。在这种情况下,您必须手动获取指向您要使用的每个符号的指针,并且包括要使用的全局变量。对于全局变量,您可以使用 GetProcAddress()或 dlsym()全局变量是导出的符号列表的一部分(按照前面段落的规则)。

当然,作为必要的最后一点注意: global应避免变量。我相信你引用的文本(关于事情“不清楚”)完全是指我刚刚解释的平台特定的差异(动态库没有真正由C ++标准定义,这是平台特定的领域,意味着是不太可靠/便携)。

发表回复