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

时间:2013-10-15 03:58:05

标签: c++ linker global-variables global dynamic-linking

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

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

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

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

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

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

(请说明您的理由)

引自MSDN

  

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

here

  

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

感谢。

2 个答案:

答案 0 :(得分:148)

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

无论如何:

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

因此,这里的关键问题是可见性

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

复杂的地方是你有extern个全局变量。在这里,Windows和类Unix系统完全不同。

对于Windows(.exe和.dll),extern个全局变量不是导出符号的一部分。换句话说,不同的模块无法识别其他模块中定义的全局变量。这意味着,如果您尝试创建应该使用DLL中定义的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,导出所有extern个全局变量(或函数)。在这种情况下,如果您从任何地方进行加载时间链接到共享对象文件,那么全局变量将被共享,即作为一个链接在一起。基本上,类似Unix的系统旨在使其与静态或动态库链接之间几乎没有区别。同样,ODR全面适用:extern全局变量将在模块之间共享,这意味着它应该在所有加载的模块中只有一个定义。

最后,在这两种情况下,对于Windows或类Unix系统,您可以执行运行时链接动态库,即使用LoadLibrary() / {{1} } / GetProcAddress()FreeLibrary() / dlopen() / dlsym()。在这种情况下,您必须手动获取指向您希望使用的每个符号的指针,其中包括您要使用的全局变量。对于全局变量,您可以使用与函数相同的dlclose()GetProcAddress(),前提是全局变量是导出符号列表的一部分(根据前面段落的规则)。 / p>

当然,作为必要的最终说明:应避免全局变量。我相信你引用的文字(关于“不清楚”的东西)正是指我刚才解释的特定于平台的差异(动态库并没有真正由C ++标准定义,这是特定于平台的领域,这意味着它不太可靠/便携。)

答案 1 :(得分:0)

在UNIX系统中:

要注意的是,如果两个动态库导出相同的全局变量,则链接器不会抱怨。但是在执行期间,可能会因访问冲突而出现段错误。表现出此行为的常见数字是细分错误15

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