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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19373061/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 22:45:41  来源:igfitidea点击:

What happens to global and static variables in a shared library when it is dynamically linked?

c++linkerglobal-variablesglobaldynamic-linking

提问by Raja

I'm trying to understand what happens when modules with globals and static variables are dynamically linked to an application. By modules, I mean each project in a solution (I work a lot with visual studio!). These modules are either built into *.lib or *.dll or the *.exe itself.

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

I understand that the binary of an application contains global and static data of all the individual translation units (object files) in the data segment (and read only data segment if const).

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

  • What happens when this application uses a module A with load-time dynamic linking? I assume the DLL has a section for its globals and statics. Does the operating system load them? If so, where do they get loaded to?

  • And what happens when the application uses a module B with run-time dynamic linking?

  • If I have two modules in my application that both use A and B, are copies of A and B's globals created as mentioned below (if they are different processes)?

  • Do DLLs A and B get access to the applications globals?

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

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

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

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

(Please state your reasons as well)

(也请说明理由)

Quoting from MSDN:

引用自MSDN

Variables that are declared as global in a DLL source code file are treated as global variables by the compiler and linker, but each process that loads a given DLL gets its own copy of that DLL's global variables. The scope of static variables is limited to the block in which the static variables are declared. As a result, each process has its own instance of the DLL global and static variables by default.

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

and from here:

这里开始

When dynamically linking modules, it can be unclear whether different libraries have their own instances of globals or whether the globals are shared.

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

Thanks.

谢谢。

回答by Mikael Persson

This is a pretty famous difference between Windows and Unix-like systems.

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

No matter what:

无论:

  • Each processhas its own address space, meaning that there is never any memory being shared between processes (unless you use some inter-process communication library or extensions).
  • The One Definition Rule(ODR) still applies, meaning that you can only have one definition of the global variable visible at link-time (static or dynamic linking).
  • 每个进程都有自己的地址空间,这意味着进程之间永远不会共享任何内存(除非您使用某些进程间通信库或扩展)。
  • 一个定义规则(ODR)仍然适用,这意味着你只能有全局变量在链接时(静态或动态链接)可见的一个定义。

So, the key issue here is really visibility.

所以,这里的关键问题是可见性

In all cases, staticglobal variables (or functions) are never visible from outside a module (dll/so or executable). The C++ standard requires that these have internal linkage, meaning that they are not visible outside the translation unit (which becomes an object file) in which they are defined. So, that settles that issue.

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

Where it gets complicated is when you have externglobal variables. Here, Windows and Unix-like systems are completely different.

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

In the case of Windows (.exe and .dll), the externglobal variables are not part of the exported symbols. In other words, different modules are in no way aware of global variables defined in other modules. This means that you will get linker errors if you try, for example, to create an executable that is supposed to use an externvariable defined in a DLL, because this is not allowed. You would need to provide an object file (or static library) with a definition of that extern variable and link it statically with boththe executable and the DLL, resulting in two distinct global variables (one belonging to the executable and one belonging to the DLL).

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

To actually export a global variable in Windows, you have to use a syntax similar to the function export/import syntax, i.e.:

要在 Windows 中实际导出全局变量,您必须使用类似于函数导出/导入语法的语法,即:

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

When you do that, the global variable is added to the list of exported symbols and can be linked like all the other functions.

当你这样做时,全局变量被添加到导出的符号列表中,并且可以像所有其他函数一样链接。

In the case of Unix-like environments (like Linux), the dynamic libraries, called "shared objects" with extension .soexport all externglobal variables (or functions). In this case, if you do load-timelinking from anywhere to a shared object file, then the global variables are shared, i.e., linked together as one. Basically, Unix-like systems are designed to make it so that there is virtually no difference between linking with a static or a dynamic library. Again, ODR applies across the board: an externglobal variable will be shared across modules, meaning that it should have only one definition across all the modules loaded.

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

Finally, in both cases, for Windows or Unix-like systems, you can do run-timelinking of the dynamic library, i.e., using either LoadLibrary()/ GetProcAddress()/ FreeLibrary()or dlopen()/ dlsym()/ dlclose(). In that case, you have to manually get a pointer to each of the symbols you wish to use, and that includes the global variables you wish to use. For global variables, you can use GetProcAddress()or dlsym()just the same as you do for functions, provided that the global variables are part of the exported symbol list (by the rules of the previous paragraphs).

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

And of course, as a necessary final note: global variables should be avoided. And I believe that the text you quoted (about things being "unclear") is referring exactly to the platform-specific differences that I just explained (dynamic libraries are not really defined by the C++ standard, this is platform-specific territory, meaning it is much less reliable / portable).

当然,作为必要的最后说明:应该避免使用全局变量。而且我相信您引用的文本(关于“不清楚”的内容)正是指我刚刚解释的特定于平台的差异(动态库并未真正由 C++ 标准定义,这是特定于平台的领域,这意味着它不太可靠/便携)。