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

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

  • 当此应用程序使用带有加载时动态链接的模块A时会发生什么?我假设DLL有一个用于其全局变量和静态变量的部分。操作系统是否加载它们?如果是这样,它们将装载到哪里?
  • 当应用程序使用带有运行时动态链接的模块B时会发生什么?
  • 如果我的应用程序中有两个同时使用A和B的模块,是否按如下所述创建A和B的全局变量的副本(如果它们是不同的过程)?
  • DLL A和B是否可以访问应用程序全局变量?

  • (请同时说明您的原因)

    引用MSDN:



    并从here:



    谢谢。

    最佳答案

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

    无论:

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

  • 因此,这里的关键问题实际上是可见性。

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

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

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

    要在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;
    

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

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

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

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

    关于c++ - 动态链接共享库时,全局变量和静态变量会发生什么?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/62156829/

    10-10 00:50
    查看更多