C++ 为什么dlopen加载的主可执行文件和共享库共享名称空间静态变量的一个副本?
据我所知,命名空间范围静态变量在每个编译单元中应该有一个副本。如果我有这样的头文件:C++ 为什么dlopen加载的主可执行文件和共享库共享名称空间静态变量的一个副本?,c++,static,linker,shared-libraries,dlopen,C++,Static,Linker,Shared Libraries,Dlopen,据我所知,命名空间范围静态变量在每个编译单元中应该有一个副本。如果我有这样的头文件: class BadLad { public: B
class BadLad {
public:
BadLad();
~BadLad();
};
static std::unique_ptr<int> sCount;
static BadLad sBadLad;
#include <dlfcn.h>
int main() {
void* dll1 = dlopen("./libplugin.so", RTLD_LAZY);
dlclose(dll1);
void* dll2 = dlopen("./libplugin.so", RTLD_LAZY);
dlclose(dll2);
return 0;
}
在执行主程序时,我可以看到scont
变量首先被创建,并在调用main之前设置为1,这是预期的。但是,在调用第一个dlopen
之后,scont
增加到2,然后在调用dlclose
时减少到1。第二个dlopen/dlclose也会发生同样的情况
所以我的问题是,为什么只有一份Scont?为什么链接器不将副本分开(我认为这是大多数人所期望的)?如果我直接将libPlugin.so链接到main而不是dlopen,则其行为相同
我用clang-4(clang-900.0.39.2)在macOS上运行这个
编辑:请参阅中的完整源代码。(迭代2)
你的情况很有趣,也很不幸。让我们一步一步地分析它
libBadLad.so
。因此,在程序启动时加载此共享库。静态对象的构造函数在main
之前执行libplugin.so
。然后加载该共享库,并执行静态对象的构造函数libBadLad.so
这个libplugin.so
链接的对象呢?由于进程已包含libBadLad.so
,因此不会第二次加载此共享库<代码>libplugin。所以完全可以不链接它libplugin.so的静态对象。它们有两种,scont
和sBadLad
。两者都是按顺序构建的
sBadLad
具有用户定义的非内联构造函数。它没有在libplugin.so
中定义,因此它是针对已加载的libBadLad.so
进行解析的,后者定义了此符号BadLad::BadLad
fromlibBadLad.so
被调用scont
。这将解析为libBadLad.so
中的scont
,而不是libplugin.so
中的scont
,因为函数本身位于libBadLad.so
中。这已初始化,并指向值为1的int
libplugin中的scont
。因此
安静地坐着,被初始化为nullptr
李>
// foo.cpp
#include "badlad.h"
// bar.cpp
#include "badlad.h"
int main () {}
构建和测试:
# > g++ -o test foo.cpp bar.cpp badlad.cpp
./test
BadLad, reset count to, 1
BadLad, 2
BadLad, 3
~BadLad, 2
Segmentation fault
为什么分割错误?这是我们旧的静态初始化命令失败。这个故事的寓意是什么?静态变量是有害的。您可以在相关部分中详细查看。我认为基本机制是一样的。欢迎来到dll地狱。基本上,每个dll都有静态/全局变量,就像它的代码一样。@post说确实有两个副本,但主副本不知怎么地掩盖了共享库中的副本?但是为什么链接器更喜欢这样做,这不违背对静态变量的一般理解吗?我相信这是线程安全的事情,因为共享库可以托管在不同的线程上下文中,并且静态变量实例化可以保证线程安全。不过,链接器对线程上下文一无所知。我对此不确定,否则我会把它作为一个答案贴出来。是POSIX标准BTW。我想到的一个想法是,将
静态
变量定义放在那些TU中的匿名名称空间中,这可能与您希望为单独的翻译单元提供单独副本的方式相同。这些应该是真正的私人副本在那里。你完全正确!如果我让BadLad构造函数内联,Scont将永远不会超过1。
# > g++ -o test foo.cpp bar.cpp badlad.cpp
./test
BadLad, reset count to, 1
BadLad, 2
BadLad, 3
~BadLad, 2
Segmentation fault