运行时加载动态库的一个小问题
简要说明
大致情况是这样的:
程序program
引用动态库libA
和libB
。其中libA
和libB
都引用动态库libShared
和静态库libStatic
。在libShared
和libStatic
中都含有静态变量。现在就是看这个静态变量是否存在两份。
program (可以隐式链接,也可以显示动态链接A和B)
| |
libStatic-> libA libB <- libStatic
| | (隐式链接Shared)
libShared
因为libA
和libB
都链接了静态库libStatic
,所以libStatic
的内容会存在两份。在映射到program
的进程空间的时候,这部分就会存在映射到不同地址的两份。在windows上的测试结果确实是如此,在linux上的测试结果中,如果是显示动态链接libA
和libB
也是,但是如果是隐式链接,那么结果却是只有一份。
因为libShared是动态库,在libA和libB中都不存在,所以在第一个映射到program
的进程空间之后,第二次引用的时候就不会再去映射一次了。
这里的一个结论就是,如果一个静态库被多处引用,那么这个静态库在最终程序的进程空间可能会存在多份。如果这个静态库中含有静态变量或全局变量,那么这个变量也会存在多份。如果这个变量是一个类对象,那么变量初始化的时候,每一份都会调用构造函数。因为VS编译的时候默认是不导出符号的,而gcc默认是导出的,也可能是这个原因。但libA和libB都是动态库,这两者中应该都是含有引用的libStatic部分的全部内容的。
所以,在静态库中最好不要去存放全局变量,也不要在这里创建单例对象等。如果对程序文件大小有要求,最好使用动态库。
实验代码
实验代码包括六个部分,libA
/libB
/libShared
/libStaic
/test
/test2
。其中test
测试的是显示动态链接,test2
测试的是隐式链接。
源代码下载LoadLibraryTest.7z
可以使用QtCreator
工具打开。
libStatic
libStatic.hpp
#ifndef LIBSTATIC_HPP
#define LIBSTATIC_HPP
void func_Static();
#endif // LIBSTATIC_HPP
libStatic.cpp
#include "LibStatic.hpp"
#include <stdio.h>
#include <stdlib.h>
void func_Static()
{
static char buf[1024] =
"这个是libStatic里的buffer。<---->";
printf("func_Static 地址 %p|",&func_Static);
printf("buf 地址 %p\n\t%s\t",&buf,buf);
static int x = 0;
if(x == 0) x = rand();
sprintf(buf,"libStatic的buffer被修改 %d",x);
printf("%s\n--------------------------\n",buf);
}
libShared
libshared_global.hpp
#ifndef LIBSHARED_GLOBAL_HPP
#define LIBSHARED_GLOBAL_HPP
#ifdef _WIN32
#if defined(LIBSHARED_LIBRARY)
# define LIBSHAREDSHARED_EXPORT extern "C" __declspec(dllexport)
#else
# define LIBSHAREDSHARED_EXPORT extern "C" __declspec(dllimport)
#endif
#else
# define LIBSHAREDSHARED_EXPORT extern "C"
#endif
#endif // LIBSHARED_GLOBAL_HPP
libShared.hpp
#ifndef LIBSHARED_HPP
#define LIBSHARED_HPP
#include "libshared_global.hpp"
LIBSHAREDSHARED_EXPORT void func_Shared();
#endif // LIBSHARED_HPP
libShared.cpp
#include "LibShared.hpp"
#include <stdio.h>
#include <stdlib.h>
void func_Shared()
{
static char buf[1024] =
"这个是libShared里的buffer。<---->";
printf("func_Shared 地址 %p|",&func_Shared);
printf("buf 地址 %p\n\t%s\t",&buf,buf);
static int x = 0;
if(x == 0) x = rand();
sprintf(buf,"libShared的buffer被修改 %d",x);
printf("%s\n--------------------------\n",buf);
}
libA
libA_global.hpp
#ifndef LIBA_GLOBAL_HPP
#define LIBA_GLOBAL_HPP
#ifdef _WIN32
#if defined(LIBA_LIBRARY)
# define LIBASHARED_EXPORT extern "C" __declspec(dllexport)
#else
# define LIBASHARED_EXPORT extern "C" __declspec(dllimport)
#endif
#else
# define LIBASHARED_EXPORT extern "C"
#endif
#endif // LIBA_GLOBAL_HPP
libA.hpp
#ifndef LIBA_HPP
#define LIBA_HPP
#include "liba_global.hpp"
LIBASHARED_EXPORT int func_A();
#endif // LIBA_HPP
libA.cpp
#include "LibA.hpp"
#include "LibShared.hpp"
#include "LibStatic.hpp"
#include <stdio.h>
int func_A()
{
static char buf[] =
"这个是libA里的buffer。---------";
printf("func_A 地址 %p|",&func_A);
printf("buf 地址 %p\n\t%s\n\n",&buf,buf);
func_Shared();
func_Static();
return 0;
}
libB
libB_global.hpp
#ifndef LIBB_GLOBAL_HPP
#define LIBB_GLOBAL_HPP
#ifdef _WIN32
#if defined(LIBB_LIBRARY)
# define LIBBSHARED_EXPORT extern "C" __declspec(dllexport)
#else
# define LIBBSHARED_EXPORT extern "C" __declspec(dllimport)
#endif
#else
# define LIBBSHARED_EXPORT extern "C"
#endif
#endif // LIBB_GLOBAL_HPP
libB.hpp
#ifndef LIBB_HPP
#define LIBB_HPP
#include "libb_global.hpp"
LIBBSHARED_EXPORT int func_B();
#endif // LIBB_HPP
libB.cpp
#include "LibB.hpp"
#include "LibShared.hpp"
#include "LibStatic.hpp"
#include <stdio.h>
int func_B()
{
static char buf[] =
"这个是libB里的buffer。---------";
printf("func_B 地址 %p|",&func_B);
printf("buf 地址 %p\n\t%s\n\n",&buf,buf);
func_Shared();
func_Static();
return 0;
}
test
main.cpp
#include <iostream>
#ifdef _WIN32
#include <Windows.h>
#else
#include <dlfcn.h>
#endif
using namespace std;
typedef int(func)();
#ifndef _WIN32
#define LoadLibraryA(dllpath) \
dlopen(dllpath,RTLD_LAZY)
// RTLD_LAZY 暂缓解出,需要时解
#define FreeLibrary(hdll) \
dlclose(hdll)
#define GetProcAddress dlsym
#define HMODULE void*
#endif
class dylib{
public:
dylib(const char* dllpath,const char* funcname)
{
hdll = LoadLibraryA(dllpath);
if(hdll){
f = (func*)GetProcAddress(hdll,funcname);
if(f == NULL){
cout<<funcname<<" 函数查找失败"<<endl;
#ifdef _WIN32
cout<<"错误码:"<<GetLastError()<<endl;
#else
cout<<"错误信息:"<<dlerror()<<endl;
#endif
}
}else{
cout<<dllpath<<" 加载失败"<<endl;
f = NULL;
}
}
~dylib()
{
if(hdll){
FreeLibrary(hdll);
}
}
int operator()()
{
return (f == NULL)?-1:f();
}
private:
HMODULE hdll;
func* f;
};
int main(int argc, char *argv[])
{
#ifdef _WIN32
dylib dA("libA.dll","func_A");
dylib dB("libB.dll","func_B");
#else
dylib dA("./liblibA.so","func_A");
dylib dB("./liblibB.so","func_B");
#endif
dA();
dB();
dA();
dB();
return 0;
}
test2
main.cpp
#include <iostream>
#include "LibA.hpp"
#include "LibB.hpp"
using namespace std;
int main(int argc, char *argv[])
{
func_A();
func_B();
func_A();
func_B();
return 0;
}
实验结果
Windows上VS2015编译x64版本运行结果
test 运行结果
为了便于观察,这里多加了换行
func_A 地址 00007FFF756910AA|buf 地址 00007FFF75699000
这个是libA里的buffer。---------
func_Shared 地址 00007FFF73E610FA|buf 地址 00007FFF73E69000
这个是libShared里的buffer。<----> libShared的buffer被修改 41
--------------------------
func_Static 地址 00007FFF7569109B|buf 地址 00007FFF75699030
这个是libStatic里的buffer。<----> libStatic的buffer被修改 18467
--------------------------
func_B 地址 00007FFF71B810AA|buf 地址 00007FFF71B89000
这个是libB里的buffer。---------
func_Shared 地址 00007FFF73E610FA|buf 地址 00007FFF73E69000
libShared的buffer被修改 41 libShared的buffer被修改 41
--------------------------
func_Static 地址 00007FFF71B8109B|buf 地址 00007FFF71B89030
这个是libStatic里的buffer。<----> libStatic的buffer被修改 6334
--------------------------
func_A 地址 00007FFF756910AA|buf 地址 00007FFF75699000
这个是libA里的buffer。---------
func_Shared 地址 00007FFF73E610FA|buf 地址 00007FFF73E69000
libShared的buffer被修改 41 libShared的buffer被修改 41
--------------------------
func_Static 地址 00007FFF7569109B|buf 地址 00007FFF75699030
libStatic的buffer被修改 18467 libStatic的buffer被修改 18467
--------------------------
func_B 地址 00007FFF71B810AA|buf 地址 00007FFF71B89000
这个是libB里的buffer。---------
func_Shared 地址 00007FFF73E610FA|buf 地址 00007FFF73E69000
libShared的buffer被修改 41 libShared的buffer被修改 41
--------------------------
func_Static 地址 00007FFF71B8109B|buf 地址 00007FFF71B89030
libStatic的buffer被修改 6334 libStatic的buffer被修改 6334
--------------------------
这里可以看到func_A
和func_B
调用的func_Shared
是同一个,但是func_Static
却不是同一个。
test2运行结果
func_A 地址 00007FFF756910AA|buf 地址 00007FFF75699000
这个是libA里的buffer。---------
func_Shared 地址 00007FFF73E610FA|buf 地址 00007FFF73E69000
这个是libShared里的buffer。<----> libShared的buffer被修改 41
--------------------------
func_Static 地址 00007FFF7569109B|buf 地址 00007FFF75699030
这个是libStatic里的buffer。<----> libStatic的buffer被修改 18467
--------------------------
func_B 地址 00007FFF758810AA|buf 地址 00007FFF75889000
这个是libB里的buffer。---------
func_Shared 地址 00007FFF73E610FA|buf 地址 00007FFF73E69000
libShared的buffer被修改 41 libShared的buffer被修改 41
--------------------------
func_Static 地址 00007FFF7588109B|buf 地址 00007FFF75889030
这个是libStatic里的buffer。<----> libStatic的buffer被修改 6334
--------------------------
func_A 地址 00007FFF756910AA|buf 地址 00007FFF75699000
这个是libA里的buffer。---------
func_Shared 地址 00007FFF73E610FA|buf 地址 00007FFF73E69000
libShared的buffer被修改 41 libShared的buffer被修改 41
--------------------------
func_Static 地址 00007FFF7569109B|buf 地址 00007FFF75699030
libStatic的buffer被修改 18467 libStatic的buffer被修改 18467
--------------------------
func_B 地址 00007FFF758810AA|buf 地址 00007FFF75889000
这个是libB里的buffer。---------
func_Shared 地址 00007FFF73E610FA|buf 地址 00007FFF73E69000
libShared的buffer被修改 41 libShared的buffer被修改 41
--------------------------
func_Static 地址 00007FFF7588109B|buf 地址 00007FFF75889030
libStatic的buffer被修改 6334 libStatic的buffer被修改 6334
与test
测试结果一致。
linux上运行测试结果
test运行测试结果
func_A 地址 0x7ffa3cdc47e0|buf 地址 0x7ffa3cfc5060
这个是libA里的buffer。---------
func_Shared 地址 0x7ffa3cbc2750|buf 地址 0x7ffa3cdc3060
这个是libShared里的buffer。<----> libShared的buffer被修改 1804289383
--------------------------
func_Static 地址 0x7ffa3cdc4830|buf 地址 0x7ffa3cfc50a0
这个是libStatic里的buffer。<----> libStatic的buffer被修改 846930886
--------------------------
func_B 地址 0x7ffa3c9c07e0|buf 地址 0x7ffa3cbc1060
这个是libB里的buffer。---------
func_Shared 地址 0x7ffa3cbc2750|buf 地址 0x7ffa3cdc3060
libShared的buffer被修改 1804289383 libShared的buffer被修改 1804289383
--------------------------
func_Static 地址 0x7ffa3c9c0830|buf 地址 0x7ffa3cbc10a0
这个是libStatic里的buffer。<----> libStatic的buffer被修改 1681692777
--------------------------
func_A 地址 0x7ffa3cdc47e0|buf 地址 0x7ffa3cfc5060
这个是libA里的buffer。---------
func_Shared 地址 0x7ffa3cbc2750|buf 地址 0x7ffa3cdc3060
libShared的buffer被修改 1804289383 libShared的buffer被修改 1804289383
--------------------------
func_Static 地址 0x7ffa3cdc4830|buf 地址 0x7ffa3cfc50a0
libStatic的buffer被修改 846930886 libStatic的buffer被修改 846930886
--------------------------
func_B 地址 0x7ffa3c9c07e0|buf 地址 0x7ffa3cbc1060
这个是libB里的buffer。---------
func_Shared 地址 0x7ffa3cbc2750|buf 地址 0x7ffa3cdc3060
libShared的buffer被修改 1804289383 libShared的buffer被修改 1804289383
--------------------------
func_Static 地址 0x7ffa3c9c0830|buf 地址 0x7ffa3cbc10a0
libStatic的buffer被修改 1681692777 libStatic的buffer被修改 1681692777
--------------------------
这里结果也是一样的,func_Static
存在两份。
test2运行结果
func_A 地址 0x7fbeb43087e0|buf 地址 0x7fbeb4509060
这个是libA里的buffer。---------
func_Shared 地址 0x7fbeb39e0750|buf 地址 0x7fbeb3be1060
这个是libShared里的buffer。<----> libShared的buffer被修改 1804289383
--------------------------
func_Static 地址 0x7fbeb450a830|buf 地址 0x7fbeb470b0a0
这个是libStatic里的buffer。<----> libStatic的buffer被修改 846930886
--------------------------
func_B 地址 0x7fbeb450a7e0|buf 地址 0x7fbeb470b060
这个是libB里的buffer。---------
func_Shared 地址 0x7fbeb39e0750|buf 地址 0x7fbeb3be1060
libShared的buffer被修改 1804289383 libShared的buffer被修改 1804289383
--------------------------
func_Static 地址 0x7fbeb450a830|buf 地址 0x7fbeb470b0a0
libStatic的buffer被修改 846930886 libStatic的buffer被修改 846930886
--------------------------
func_A 地址 0x7fbeb43087e0|buf 地址 0x7fbeb4509060
这个是libA里的buffer。---------
func_Shared 地址 0x7fbeb39e0750|buf 地址 0x7fbeb3be1060
libShared的buffer被修改 1804289383 libShared的buffer被修改 1804289383
--------------------------
func_Static 地址 0x7fbeb450a830|buf 地址 0x7fbeb470b0a0
libStatic的buffer被修改 846930886 libStatic的buffer被修改 846930886
--------------------------
func_B 地址 0x7fbeb450a7e0|buf 地址 0x7fbeb470b060
这个是libB里的buffer。---------
func_Shared 地址 0x7fbeb39e0750|buf 地址 0x7fbeb3be1060
libShared的buffer被修改 1804289383 libShared的buffer被修改 1804289383
--------------------------
func_Static 地址 0x7fbeb450a830|buf 地址 0x7fbeb470b0a0
libStatic的buffer被修改 846930886 libStatic的buffer被修改 846930886
--------------------------
这里运行结果与windows上不一样,func_Static
只存在一份。这里说明libStatic
在进程空间仅存在一份,虽然它同时存在于libA
和libB
中。通过strings
程序查看libA
和libB
,发现前一部分是一样的,也许这就是在链接的时候只存留一份的依据吧。