C++ 静态库中的共享全局变量

Posted

技术标签:

【中文标题】C++ 静态库中的共享全局变量【英文标题】:Shared global variable in C++ static library 【发布时间】:2010-07-06 14:09:05 【问题描述】:

我有一个 MS C++ 项目(我们称之为项目 A),我目前正在将其编译为静态库 (.lib)。它定义了一个全局变量 foo。我有另外两个单独编译的项目(分别称为 B 和 C),每个项目都链接共享静态库 A。B 和 C 都是最终在同一个进程中加载​​的 dll。我想在同一进程中在 B 和 C 之间共享来自 A 的 foo 的单个实例:单例。我不确定如何在这里使用项目 A 完成单例模式,因为它是分别静态编译成 B 和 C 的。如果我在 B 和 C 中都将 foo 声明为 extern,我最终会在 B 和 C 中得到不同的实例。使用带有静态 getInstance 方法的标准、简单的单例类模式会导致两个静态 foo 实例化。

当项目 A 被静态编译成 B 和 C 时,有什么办法可以做到这一点?还是我必须让 A 成为 DLL?

【问题讨论】:

“如果我在 B 和 C 中都将 foo 声明为 extern,我最终会在 B 和 C 中得到不同的实例。” - 这是真的吗? 这似乎是真的 - 这是我在调试时单步执行程序时观察到的行为。我看到 foo 的构造函数被调用了两次,并且外部变量 g_Foo 在两个模块 B 和 C 中的地址不同。应该不是这种情况吗? How to implement process-global variable in C++?的可能重复 @Zach - 我不这么认为。在这种情况下,变量需要是跨进程的。仅仅因为它在 DLL 中是静态全局的,并不意味着不同的进程映像具有相同的实例。每个图像都有自己的实例。 @Ragster,嗯,我认为我当时的问题并不清楚(或者我可能不完全理解你在说什么)。对于给定的进程,我真的只需要一个实例(它不需要跨进程可见)。或者我不太明白你所说的过程图像是什么意思......谢谢! 【参考方案1】:

是的,您必须将 A 设为共享 DLL,否则将其定义为 B 和 C 中的外部并静态链接所有三个。

【讨论】:

您能否详细说明“静态链接所有三个”的含义?我目前正在将 A 静态链接到 B 和 C 并在 B 和 C 中声明 foo extern,但我最终在运行时得到了 foo 的多个定义。我认为您是说我需要将 A 和 B 静态链接到 C(或 A 和 C 静态链接到 B),或者将 A、B 和 C 静态链接到另一个库。那准确吗?谢谢! 最后一个,除了用“最终的可执行文件”替换“另一个库”。不要将任何库链接在一起。相反,将 A、B 和 C 静态链接到您的最终可执行文件。在 B 或 C 中使用的来自 A 的任何符号都应在 B 和 C 中声明为 extern,但未定义。如果你将 A、B 和 C 链接到另一个共享库,你只需要确保这个库封装了来自 A 的单例,因为如果 A 静态链接到可执行文件并且可执行文件直接访问 A 的静态数据,它将得到与 lib 不同的副本。【参考方案2】:

不 - 它们不共享。

来自 Richter 的“Windows via C/C++”(p583):

当一个进程映射一个 DLL 映像文件时 到它的地址空间空间中, 系统创建全局实例 和静态数据变量。

因此,如果您需要在多个可执行文件之间共享资源,则需要创建某种共享内核对象。我建议创建一个命名文件映射,然后您可以使用该映射从单独的进程中读取和写入(当然要使用适当的 Mutex 排除。)

【讨论】:

您的观察总体上是正确的,但您监督了 OP 的一项重要声明:“B 和 C 都是 dll,最终在同一进程中加载​​ .” 因此,它可以通过 DLL 共享。这里的问题是静态链接。 顺便说一句,跨进程的共享可以很容易地实现,如下所述:***.com/a/7705793/3723423【参考方案3】:

我确实遇到了这个问题:

我有 A.EXE 与 B.LIB 链接。 我有 C.DLL 也与 B.LIB 链接。

如您所见,这两个程序是与 B.LIB 链接的

现在,当需要时,A.exe 会加载 C.DLL。 加载后,A.EXE 和 C.DLL 各有独立的 B.LIB 代码和数据

问题可能有一个 B.LIB “fantom” 与 C.DLL 链接以使用 A.exe 中已经加载的内容?

我认为没有。这是Window的限制。如果我没记错的话,在Linux中你可以。这是 GCC 的 -fPic 选项所考虑的。但不确定。

与 A.EXE 和 C.DLL 共享存储在 B.LIB 中的全局/静态数据的解决方案是使用共享类/接口。您的 A.EXE 向 C.DLL 发送接口,其中包含指向 A.EXE 中存储的 B.LIB 的数据/接口指针。

换句话说,如果您的 C.DLL 需要设置/共享 B.LIB 中的静态变量的状态,您的 A.EXE 必须使用接口初始化 C.DLL 以允许与 A 中的 B.LIB 进行数据交换。EXE文件。

在所有情况下,由于 A.EXE 和 C.DLL 中的 B.LIB 重复代码,存储在 C.DLL 中的 B.LIB 继续膨胀全局程序

为了减少全局膨胀,你必须将你的 A.LIB 分成更多的 D.DLL、E.DLL,它们由 A.EXE 加载并通过接口传输到你的 C.DLL

要将臃肿的代码减少到零,必须使用完全独立的接口方法。

【讨论】:

以上是关于C++ 静态库中的共享全局变量的主要内容,如果未能解决你的问题,请参考以下文章

在 C 代码库中查找全局/静态变量的工具

如何在运行时确定共享库中全局变量的地址范围?

C++中的全局变量普通局部变量和静态局部变量的区别

Solaris 共享库和全局变量

不同的静态全局变量共享相同的内存地址

C++中的静态全局变量