zoukankan      html  css  js  c++  java
  • 运行时加载动态库的一个小问题

    简要说明

    大致情况是这样的:
    程序program引用动态库libAlibB。其中libAlibB都引用动态库libShared和静态库libStatic。在libSharedlibStatic中都含有静态变量。现在就是看这个静态变量是否存在两份。

                 program    (可以隐式链接,也可以显示动态链接A和B)
                 |    |
    libStatic-> libA  libB <- libStatic
                 |    |		(隐式链接Shared)
                libShared
    

    因为libAlibB都链接了静态库libStatic,所以libStatic的内容会存在两份。在映射到program的进程空间的时候,这部分就会存在映射到不同地址的两份。在windows上的测试结果确实是如此,在linux上的测试结果中,如果是显示动态链接libAlibB也是,但是如果是隐式链接,那么结果却是只有一份。

    因为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
    	%s	",&buf,buf);
    
        static int x = 0;
        if(x == 0) x = rand();
        sprintf(buf,"libStatic的buffer被修改 %d",x);
        printf("%s
    --------------------------
    ",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
    	%s	",&buf,buf);
    
        static int x = 0;
        if(x == 0) x = rand();
        sprintf(buf,"libShared的buffer被修改 %d",x);
        printf("%s
    --------------------------
    ",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
    	%s
    
    ",&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
    	%s
    
    ",&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_Afunc_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在进程空间仅存在一份,虽然它同时存在于libAlibB中。通过strings程序查看libAlibB,发现前一部分是一样的,也许这就是在链接的时候只存留一份的依据吧。

  • 相关阅读:
    阿牛的EOF牛肉串
    盐水的故事
    密码
    Digital Roots
    不容易系列之(3)—— LELE的RPG难题
    不容易系列之一
    超级楼梯
    母牛的故事
    蟠桃记
    Children’s Queue
  • 原文地址:https://www.cnblogs.com/oloroso/p/6273295.html
Copyright © 2011-2022 走看看