在Linux上的共享库中的单例的多个实例 [英] Multiple instances of singleton across shared libraries on Linux
问题描述
我的问题,正如标题所提到的,是显而易见的,我详细描述场景。
在singleton.h文件中有一个名为singleton的单例模式实现的类:
/ *
* singleton.h
*
*创建日期:2011-12-24
*作者:bourneli
* /
#ifndef SINGLETON_H_
#define SINGLETON_H_
class singleton
{
private:
singleton(){num = -1;}
static singleton * pInstance ;
public:
static singleton& instance()
{
if(NULL == pInstance)
{
pInstance = new singleton();
}
return * pInstance;
}
public:
int num
};
singleton * singleton :: pInstance = NULL;
#endif / * SINGLETON_H_ * /
叫做hello.cpp如下:
#include< iostream>
#includesingleton.h
externCvoid hello(){
std :: cout< singleton.num in hello.so:< singleton :: instance()。num<< std :: endl;
++ singleton :: instance()。num;
std :: cout<< singleton.num in hello.so after ++:< singleton :: instance()。num<< std :: endl;
}
您可以看到插件调用singleton并更改singleton。
最后,有一个main函数使用单例和插件如下:
#include< iostream>
#include< dlfcn.h>
#includesingleton.h
int main(){
using std :: cout;
using std :: cerr;
using std :: endl;
singleton :: instance()。num = 100; // call singleton
cout<< singleton.num in main:< singleton :: instance()。num<< endl; // call singleton
//打开库
void * handle = dlopen(./ hello.so,RTLD_LAZY);
if(!handle){
cerr<< 无法打开库:< dlerror()<< '\\\
';
return 1;
}
//加载符号
typedef void(* hello_t)();
// reset errors
dlerror();
hello_t hello =(hello_t)dlsym(handle,hello);
const char * dlsym_error = dlerror();
if(dlsym_error){
cerr<< 无法加载符号'hello':<< dlerror()<< '\\\
';
dlclose(handle);
return 1;
}
hello(); // call plugin function hello
cout<< singleton.num in main:< singleton :: instance()。num<< endl; // call singleton
dlclose(handle);
}
,makefile如下:
example1:main.cpp hello.so
$(CXX)$(CXXFLAGS)-o example1 main.cpp -ldl
hello.so:hello.cpp
$(CXX)$(CXXFLAGS)-shared -o hello.so hello.cpp
clean:
rm -f example1 hello.so
.PHONY:clean
我认为有以下内容:
singleton.num in main:100
singleton.num in hello .so:100
singleton.num in hello.so after ++:101
singleton.num in main:101
但是,实际输出如下:
singleton.num in main:100
singleton.num in hello.so:-1
singleton.num in hello.so after ++:0
singleton.num in main:100
这证明单例类有两个实例。
p>
解决方案首先,在构建共享库时,通常应使用
-fPIC
标志。
在32位Linux上不使用工作,但在64位操作系统上失败,错误类似于:
$在创建共享对象时,不能使用针对`.rodata'的重定位R_X86_64_32 b $ b/ usr / bin / ld:/tmp/ccUUrz9c.o:重新编译-fPIC
其次,在添加
-rdynamic
到主可执行文件的链接行:singleton.num in main:100
singleton.num in hello.so:100
singleton.num in hello.so after ++:101
singleton.num in main:101
为了理解为什么需要
-rdynamic
,你需要知道动态链接器解析符号和关于动态符号表。
首先,让我们看看
hello .so
:$ nm -C -D hello.so | grep singleton
0000000000000b8c W singleton :: instance()
0000000000201068 B singleton :: pInstance
0000000000000b78 W singleton :: singleton()
这告诉我们有两个弱函数定义和一个可见的全局变量
singleton :: pInstance
到动态链接器。
现在让我们看看原始
example1
的静态和动态符号表-rdynamic
):$ nm -C example1 | grep singleton
0000000000400d0f t全局构造函数keyed to singleton :: pInstance
0000000000400d38 W singleton :: instance()
00000000006022e0 B singleton :: pInstance
0000000000400d24 W singleton :: singleton
$ nm -C -D example1 | grep singleton
$
这是正确的:即使
:pInstance
作为全局变量存在于可执行文件中,该符号不存在于动态符号表中,因此对动态链接器不可见。
因为动态链接器不知道
example1
已经包含singleton的定义: :pInstance
,它不会将hello.so
中的那个变量绑定到现有的定义(这是你真正想要的)。
当在链接行中添加
-rdynamic
时:$ nm -C example1-rdynamic | grep singleton
0000000000400fdf t全局构造函数keyed to singleton :: pInstance
0000000000401008 W singleton :: instance()
00000000006022e0 B singleton :: pInstance
0000000000400ff4 W singleton :: singleton
$ nm -C -D example1-rdynamic | grep singleton
0000000000401008 W singleton :: instance()
00000000006022e0 B singleton :: pInstance
0000000000400ff4 W singleton :: singleton()
现在,主可执行文件中
singleton :: pInstance
的定义是 visible 动态链接器,因此在加载hello.so
时将重用该定义:LD_DEBUG = bindings ./example1-rdynamic |& grep pInstance
31972:绑定文件./hello.so [0]到./example1-rdynamic [0]:正常符号`_ZN9singleton9pInstanceE'
My question, as the title mentioned, is obvious, and I describe the scenario in details. There is a class named singleton implemented by singleton pattern as following, in file singleton.h:
/* * singleton.h * * Created on: 2011-12-24 * Author: bourneli */ #ifndef SINGLETON_H_ #define SINGLETON_H_ class singleton { private: singleton() {num = -1;} static singleton* pInstance; public: static singleton& instance() { if (NULL == pInstance) { pInstance = new singleton(); } return *pInstance; } public: int num; }; singleton* singleton::pInstance = NULL; #endif /* SINGLETON_H_ */
then, there is a plugin called hello.cpp as following:
#include <iostream> #include "singleton.h" extern "C" void hello() { std::cout << "singleton.num in hello.so : " << singleton::instance().num << std::endl; ++singleton::instance().num; std::cout << "singleton.num in hello.so after ++ : " << singleton::instance().num << std::endl; }
you can see that the plugin call the singleton and change the attribute num in the singleton.
last, there is a main function use the singleton and the plugin as following:
#include <iostream> #include <dlfcn.h> #include "singleton.h" int main() { using std::cout; using std::cerr; using std::endl; singleton::instance().num = 100; // call singleton cout << "singleton.num in main : " << singleton::instance().num << endl;// call singleton // open the library void* handle = dlopen("./hello.so", RTLD_LAZY); if (!handle) { cerr << "Cannot open library: " << dlerror() << '\n'; return 1; } // load the symbol typedef void (*hello_t)(); // reset errors dlerror(); hello_t hello = (hello_t) dlsym(handle, "hello"); const char *dlsym_error = dlerror(); if (dlsym_error) { cerr << "Cannot load symbol 'hello': " << dlerror() << '\n'; dlclose(handle); return 1; } hello(); // call plugin function hello cout << "singleton.num in main : " << singleton::instance().num << endl;// call singleton dlclose(handle); }
and the makefile is following:
example1: main.cpp hello.so $(CXX) $(CXXFLAGS) -o example1 main.cpp -ldl hello.so: hello.cpp $(CXX) $(CXXFLAGS) -shared -o hello.so hello.cpp clean: rm -f example1 hello.so .PHONY: clean
so, what is the output? I thought there is following:
singleton.num in main : 100 singleton.num in hello.so : 100 singleton.num in hello.so after ++ : 101 singleton.num in main : 101
however, the actual output is following:
singleton.num in main : 100 singleton.num in hello.so : -1 singleton.num in hello.so after ++ : 0 singleton.num in main : 100
It proves that there are two instances of the singleton class.
Why?
解决方案First, you should generally use
-fPIC
flag when building shared libraries.Not using it "works" on 32-bit Linux, but would fail on 64-bit one with an error similar to:
/usr/bin/ld: /tmp/ccUUrz9c.o: relocation R_X86_64_32 against `.rodata' can not be used when making a shared object; recompile with -fPIC
Second, your program will work as you expect after you add
-rdynamic
to the link line for the main executable:singleton.num in main : 100 singleton.num in hello.so : 100 singleton.num in hello.so after ++ : 101 singleton.num in main : 101
In order to understand why
-rdynamic
is required, you need to know about the way dynamic linker resolves symbols, and about the dynamic symbol table.First, let's look at the dynamic symbol table for
hello.so
:$ nm -C -D hello.so | grep singleton 0000000000000b8c W singleton::instance() 0000000000201068 B singleton::pInstance 0000000000000b78 W singleton::singleton()
This tells us that there are two weak function definitions, and one global variable
singleton::pInstance
that are visible to the dynamic linker.Now let's look at the static and dynamic symbol table for the original
example1
(linked without-rdynamic
):$ nm -C example1 | grep singleton 0000000000400d0f t global constructors keyed to singleton::pInstance 0000000000400d38 W singleton::instance() 00000000006022e0 B singleton::pInstance 0000000000400d24 W singleton::singleton() $ nm -C -D example1 | grep singleton $
That's right: even though the
singleton::pInstance
is present in the executable as a global variable, that symbol is not present in the dynamic symbol table, and therefore "invisible" to the dynamic linker.Because the dynamic linker "doesn't know" that
example1
already contains a definition ofsingleton::pInstance
, it doesn't bind that variable insidehello.so
to the existing definition (which is what you really want).When we add
-rdynamic
to the link line:$ nm -C example1-rdynamic | grep singleton 0000000000400fdf t global constructors keyed to singleton::pInstance 0000000000401008 W singleton::instance() 00000000006022e0 B singleton::pInstance 0000000000400ff4 W singleton::singleton() $ nm -C -D example1-rdynamic | grep singleton 0000000000401008 W singleton::instance() 00000000006022e0 B singleton::pInstance 0000000000400ff4 W singleton::singleton()
Now the definition of
singleton::pInstance
inside the main executable is visible to the dynamic linker, and so it will "reuse" that definition when loadinghello.so
:LD_DEBUG=bindings ./example1-rdynamic |& grep pInstance 31972: binding file ./hello.so [0] to ./example1-rdynamic [0]: normal symbol `_ZN9singleton9pInstanceE'
这篇关于在Linux上的共享库中的单例的多个实例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!