运行时加载动态库的一个小问题
简要说明#
大致情况是这样的:
程序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
,发现前一部分是一样的,也许这就是在链接的时候只存留一份的依据吧。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理