我有 3 样东西:开源应用程序(我们称之为 APP)、闭源共享库(我们称之为 OPENGL)和 OPENGL 的开源插件(我们称之为 PLUGIN)[也是共享库]。
操作系统:Linux。
APP和PLUGIN之间需要共享数据,所以APP与PLUGIN链接,运行时系统会自动加载。
在那个APP调用属于OPENGL的eglInitialize之后,这个函数再次加载PLUGIN。
之后,我在 APP 内存中有两个 PLUGIN 副本。
我知道是因为PLUGIN有全局数据,调试后发现有两份全局数据。
那么质疑我如何解决这种行为?我想要一个 PLUGIN 实例,供 APP 和 OPENGL 使用。而且我无法更改 OPENGL 库。
2 回答
这显然很大程度上取决于图书馆正在做什么,但总的来说应该有一些解决方案。
首先请注意,通常如果多次加载同名的共享库,它将继续使用同一个库。这主要适用于通过标准加载/链接机制进行加载。如果库自行调用dlopen
,它仍然可以获得相同的库,但它取决于dlopen
. 尝试阅读 dlopen 上的文档以了解它的工作原理以及如何操作它。
您还可以尝试在链接器命令中更早地定位 PLUGIN,以便它首先加载,从而可能避免以后的双重加载。如果您必须动态加载插件,这显然无济于事。您还可以检查是否LD_PRELOAD
可以解决链接顺序。
作为最后的手段,您可能不得不求助于使用LD_LIBARY_PATH
并从真实的接口库中放入接口库。这个只会将调用传递给真正的调用,但会拦截重复的负载并将它们分流到前一个负载。
这只是一个需要考虑的大方向。您的实际答案将在很大程度上取决于您的代码以及其他共享库的功能。始终首先研究链接器加载顺序,因为它是最容易检查的,然后是 dlopen 标志,然后再进入其他选项。
我怀疑 OPENGL 正在加载带有RTLD_LOCAL
标志的 PLUGIN。这通常是您在加载插件时想要的,这样多个插件就不会发生冲突。
我们在 Java 下加载代码时也遇到过类似的问题:我们会加载十几个不同的模块,但它们无法相互通信。我们的解决方案可能对您有用:我们为插件编写了一个包装器,并告诉 Java 包装器就是插件。dlopen
然后,该插件使用with加载其他每个共享对象RTLD_GLOBAL
。这在插件之间有效。但是,我不确定它是否会允许插件返回主插件(但我认为应该)。而 IIRC,在链接 main 时,您需要特殊选项才能使其符号可用。我认为 Linux 将 main 中的符号视为已加载 main RTLD_LOCAL
。(也许
--export-dynamic
?自从我不得不这样做以来已经有一段时间了,我记不清了。)