• Linux下动态库的使用


    【简介】

    linux环境下的动态库一般名为libxxx.so, 用ldd命令分析某个可执行程序,可以看到该程序依赖哪些动态库,以及路径。 如 ldd ./test

      linux-vdso.so.1 =>  (0x00007fffaab52000)

      libc.so.6 => /lib64/libc.so.6 (0x0000003c4c800000)

      /lib64/ld-linux-x86-64.so.2 (0x0000003c4c000000)

    如果有依赖库找不到,程序会无法正常运行。

    【创建一个动态库】

    util.cpp

    extern "C" int InsertSubStr(char* pszBuf, int nPos, char* pValue) 
    {
         return 1; 
    } 
    

    代码为cpp时,函数可以重载,在symbol里生成的函数名会带上修饰,如_ZwqInsertSubStrBstl. 加上extern "C"之后在symbol里就是原名,当然也就不能有同名函数了。如果函数体使用了该修饰后,头文件定义也必须加上,否则调用者编译时会找不到函数名。

    makefile

    libmyutil.so : util.o
      g++ -shared -o libmyutil.so util.o
    
    util.o : util.cpp
      g++ -fPIC -c util.cpp -o util.o
    

     如果没有编译错误,则会生成libmyutil.so文件。因为使用了-shared编译选项,如果在代码里引用了其他库libabc.so,在编译时不会报错(不像windows下那样会报unresolve external,或者我还没找到设置方法)。如果调用者也没有引用该库libabc.so,则加载或运行时会报错。所以知道引用了哪些库,最好在makefile里加上,如g++ -shared -labc -o libmyutil.so util.o

    QT创建动态库时,会生成libxx.so.1.0.0等几个带版本号的符号链接,可以加上 CONFIG += plugin 这样就不带版本号了。

    【使用动态库】

    像windows一样,也有直接编译链接和动态加载两种方式

    1.直接编译链接

    include头文件的函数定义后,在程序中调用函数,再在makefile中加上-lmyutil即可。用ldd命令分析执行程序,可以看到引用了libmyutil.so QT编译时,需要在pro文件里指定路径 LIB += -L../lib -lmymodule

    2.动态加载

    #include <dlfcn.h>
    
    typedef int (*fnTestFoo)(char* pszBuf, int nPos, char* pValue);
    
      void *hso = dlopen("./libmyutil.so", RTLD_NOW);//RTLD_LAZY);//
      if(!hso)
      {
        printf("dlopen failed:%s
    ", dlerror());
        return;
      }
      fnTestFoo fnTest = (fnTestFoo)dlsym(hso, "InsertSubStr");
      char *perr = dlerror();
      if(perr)
      {
        printf("load symbol failed:%s
    ", perr);
        return;
      }
      char szText[256]="abcdef";
      fnTestFoo(szText, 2, "xyz");
      printf("%s
    ", szText);
      dlclose(hso);
    

    可以看出与windows函数的对应:dlopen=LoadLibrary,dlsym=GetProcAddress. 这几个函数在libdl.so中,编译时在makefile中加上-ldl dlopen

    可以有选项RTLD_NOW/RTLD_LAZY,前者为加载时解析依赖关系,失败则报错停止运行;后者为先不解析,运行到相应的代码时,如果有依赖关系错误再报错。

    动态加载C++类

    如果想要使用动态库中的C++类,直接使用肯定不行,因为编译时会找不到构造函数。其实只要在动态库中输出一个函数,创建类对象

    CTest *GetClass_DL(void)

    {

        return (new CTest());

    }

    这样就可以了,析构函数也一样。

    【依赖关系和路径】

    在windows下可以用depends来查看库的依赖关系,在Linux下有好几个工具可以达到类似效果,如上面提到的文件依赖关系的ldd. 如果要查看函数依赖关系,可以用nm命令,如nm test

    0000000000601170 d _DYNAMIC
                     w _Jv_RegisterClasses
    00000000004008e4 T _Z8callFunciPPcPFiS_zE
                     U dlclose@@GLIBC_2.2.5
                     U dlerror@@GLIBC_2.2.5
                     U dlopen@@GLIBC_2.2.5
                     U dlsym@@GLIBC_2.2.5
    0000000000400da2 T main
    

    其中T(text)前面是在本程序中的地址;U(unresolved)表示引用的外部库函数,以及对应的库名称。另外还有w(weak),A(absolute)不了解。 另外还有objdump命令,更详细的显示程序内的内容。

    动态加载dlopen时,最好是指定全路径,否则当前路径可能发生变化。当编译链接库文件时,有时运行程序会找不到库文件报错:error while loading shared libraries: libxxx.so.1: cannot open shared object file: No such file or directory.很多时候是未指定路径。

    与windows不同,把dll放在exe一起就行了,加载时会先从当前路径查找。linux下的库文件的路径配置在/etc/ld.so.conf 中,内容如下一行

    include ld.so.conf.d/*.conf

    在conf.d目录,可以看到其他软件所需的库文件路径,如mysql-x86_64.conf 里面也只有一行:/usr/lib64/mysql

    所以如果自己的库文件不放在系统的库路径如/usr/lib64下的话,也可以照此方法增加myapp.conf文件,写上/mylib, 放在这里即可。

    sudo ldconfig更新配置(注意较慢),用ldconfig -p|grep libmyxxx可以查看自己的库是否在系统的路径里。当在路径里新增so文件时,配置不会自动更新。

    如果不想更改系统的库文件路径,也可以在运行时修改环境变量LD_LIBRARY_PATH,如 $ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/my_library_path $ ./my_app

  • 相关阅读:
    delphi中屏蔽浏览器控件右键菜单
    书目:一些
    数据库ADONETDataAdapter对象参考
    数据库ADONET排序、搜索和筛选
    易语言数据类型及其长度
    易语言数据类型的初始值
    数据库ADONET使用DataAdapter对象
    ADONET使用DataSet处理脱机数据
    数据库ADONETOleDbParameter对象参考
    在项目中添加新数据集
  • 原文地址:https://www.cnblogs.com/chaos77/p/6874378.html
Copyright © 2020-2023  润新知