zoukankan      html  css  js  c++  java
  • Linux下的动态共享库(so)开发调用

    翻译并根据实际情况进行了小小修改,仅关注Linux下动态共享库(Dynamic shared library .so)的开发.

    1 简单的so实例

    源文件

    //test1.c
    int test1(){
        
    return 1;
    }

    //test2.c
    int test2(){
        
    return2;
    }

    //mytest.c
    #include <stdio.h>
    int test1();
    int test2();
    int main(){
        printf(
    "result of test1:= %d ",test1());
        printf(
    "result of test2:= %d ",test2());
    }

    打包成so文件

    在代码的目录下运行如下命令: (如果你不是Ubuntu系统,请将命令的sudo都去掉)

    gcc -Wall -fPIC -c *.c
    gcc -shared -Wl
    ,-soname,libctest.so.1 -o libctest.so.1.0   *.o
    sudo mv libctest.so
    .1.0 /usr/lib
    sudo ln -sf /usr/lib/libctest.so
    .1.0 /usr/lib/libctest.so
    sudo ln -sf /usr/lib/libctest.so
    .1.0 /usr/lib/libctest.so.1

    参数详解:
    • -Wall: 包含warning信息
    • -fPIC: 编译动态库必须,输出不依赖位置的代码(原文 :Compiler directive to output position independent code)
    • -shared: 编译动态库必需选项
    • -W1: 向链接器(Linker)传递一些参数.在这里传递的参数有 "-soname libctest.so.1"
    • -o: 动态库的名字. 在这个例子里最终生成动态库 libctest.so.1.0
    两个符号链接的含义:
    • 第一个:允许应用代码用 -lctest 的语法进行编译.
    • 第二个:允许应用程序在运行时调用动态库.

    2 so路径设置

    为了使应用程序能够在运行时加载动态库,可以通过3种方式指定动态库的路径(以下例子均假定/opt/lib是动态库所在位置):

    用ldconfig指定路径

    运行

    sudo ldconfig -n /opt/lib

    /opt/lib 是动态库所在路径.  这种方式简单快捷,便于程序员开发.缺点是重启后即失效.

    修改/etc/ld.so.conf文件

    打开/etc/ld.so.confg 文件,并将/opt/lib 添加进去.

    (注: 在Ubuntu系统中, 所有so.conf文件都在/etc/ld.so.conf.d目录. 你可以仿照该目录下的.conf文件写一个libctest.conf并将/opt/lib填入)

    用环境变量LD_LIBRARY_PATH指定路径

    环境变量的名字一般是LD_LIBRARY_PATH, 但是不同的系统可能有不同名字. 例如

    Linux/Solaris: LD_LIBRARY_PATH, SGI: LD_LIBRARYN32_PATH, AIX: LIBPATH, Mac OS X: DYLD_LIBRARY_PATH, HP-UX: SHLIB_PATH) (注: 此说法未经验证)

    修改~/.bashrc , 增加以下脚本:


    if 
    [ -d /opt/lib ];
    then
       LD_LIBRARY_PATH
    =/opt/lib:$LD_LIBRARY_PATH
    fi



    export LD_LIBRARY_PATH

    在第一章的简单例子中, /usr/lib 是Ubuntu默认的动态库目录,所以我们不须指定动态库目录也能运行应用程序.

    3 简单的动态调用so例子


    C调用例子

    保留第一章的test1.c和test2.c文件,并增加ctest.h头文件如下:

    #ifndef CTEST_H
    #define CTEST_H

    #ifdef __cplusplus
    extern "C" {
    #endif

    int test1();
    int test2();

    #ifdef __cplusplus
    }
    #endif

    #endif
          

    我们继续使用第一章生成的libctest.so,仅需增加一个新的应用程序 prog.c:

    //prog.c

    #include <stdio.h>
    #include 
    <dlfcn.h>
    #include 
    "ctest.h"

    int main(int argc, char **argv) 
    {
       
    void *lib_handle;
       
    int (*fn)();
       
    char *error;

       lib_handle 
    = dlopen("libctest.so", RTLD_LAZY);
       
    if (!lib_handle) 
       {
          fprintf(stderr, 
    "%s ", dlerror());
          
    return 1;
       }

       fn 
    = dlsym(lib_handle, "test1");
       
    if ((error = dlerror()) != NULL)  
       {
          fprintf(stderr, 
    "%s ", error);
          
    return 1;
       }

       
    int y=fn();
       printf(
    "y=%d ",y);

       dlclose(lib_handle);
       
       
    return 0;
    }
           


    然后用如下命令运行(由于没有使用其他库,所以忽略-L等参数):

    gcc -Wall prog.c -lctest -o prog -ldl
    .
    /progdl

    方法简介

    dlopen("libctest.so", RTLD_LAZY): 加载动态库,如果加载失败返回NULL. 第二个参数可以是:
    • RTLD_LAZY: lazy模式. 直到源码运行到改行才尝试加载.
    • RTLD_NOW: 马上加载.
    • RTLD_GLOBAL: 不解(原文: Make symbol libraries visible.)
    dlsym(lib_handle, "test1"): 返回函数地址. 如果查找函数失败则返回NULL.

    和微软的动态加载dll技术对比如下:
    • ::LoadLibrary() - dlopen()
    • ::GetProcAddress() - dlsym()
    • ::FreeLibrary() - dlclose()

    C++调用例子


    增加一个prog2.cpp
    #include <dlfcn.h>
    #include 
    <iostream>
    #include 
    "ctest.h"


    using namespace std;
    int main(){
        
    void *lib_handle;
        
    //MyClass* (*create)();
        
    //ReturnType (* func_name)();
        int (* func_handle)();  
        
    string nameOfLibToLoad("libctest.so");
        lib_handle 
    = dlopen(nameOfLibToLoad.c_str(), RTLD_LAZY);
        
    if (!lib_handle) {
            cerr 
    << "Cannot load library: " << dlerror() << endl;
        }
        
    // reset errors
        dlerror();
        
    // load the symbols (handle to function "test")
        
    //create = (MyClass* (*)())dlsym(handle, "create_object");
        
    //destroy = (void (*)(MyClass*))dlsym(handle, "destroy_object");
        func_handle =(int(*)())dlsym(lib_handle, "test1");

        
    const char* dlsym_error = dlerror();
        
    if (dlsym_error) {
            cerr 
    << "Cannot load symbol test1: " << dlsym_error << endl;
        }
        
        cout
    <<"result:= "<<func_handle()<<endl;
        
        dlclose(lib_handle);
        
        
    return 0;
    }




    然后用如下命令运行:

    g++ -Wall prog2.cpp -lctest -o prog2 -ldl
    .
    /prog2

    编译命令简介

    假设C文件是prog.c, C++调用文件是prog2.cpp,那么编译脚本分别是:

    C语言:

     gcc -Wall -I/path/to/include-files -L/path/to/libraries prog.c -lctest -o prog

    C++语言:

     g++ -Wall -I/path/to/include-files -L/path/to/libraries prog2.cpp -lctest -ldl -o prog2

    参数详解:
    • -I: 指定头文件目录.
    • -L: 指定库目录.
    • -lctest: 调用动态库libctest.so.1.0. 如果在打包so时没有创建第一个符号链接,那么这个参数会导致编译不成功.
    • -ldl: C++编译必须

    相关知识


    命令ldd appname 可以查看应用程序所依赖的动态库,运行如下命令:

    ldd prog

    在我的机器输出:

        linux-gate.so.1 =>  (0xb80d4000)
        libctest.so
    .1 => /usr/lib/libctest.so.1 (0xb80be000)
        libc.so
    .6 => /lib/tls/i686/cmov/libc.so.6 (0xb7f5b000)
        /lib/ld-linux.so
    .2 (0xb80d5000)

    4 C++开发带class的so

    //myclass.h
    #ifndef __MYCLASS_H__
    #define __MYCLASS_H__

    class MyClass
    {
    public:
      MyClass();

      
    /* use virtual otherwise linker will try to perform static linkage */
      
    virtual void DoSomething();

    private:
      
    int x;
    };

    #endif

    //myclass.cpp
    #include "myclass.h"
    #include 
    <iostream>

    using namespace std;

    extern "C" MyClass* create_object()
    {
      
    return new MyClass;
    }

    extern "C" void destroy_object( MyClass* object )
    {
      delete 
    object;
    }

    MyClass::MyClass()
    {
      x 
    = 20;
    }

    void MyClass::DoSomething()
    {
      cout
    <<x<<endl;
    }

    //class_user.cpp
    #include <dlfcn.h>
    #include 
    <iostream>
    #include 
    "myclass.h"

    using namespace std;

    int main(int argc, char **argv)
    {
      
    /* on Linux, use "./myclass.so" */
      
    void* handle = dlopen("./myclass.so", RTLD_LAZY);

      MyClass
    * (*create)();
      
    void (*destroy)(MyClass*);

      create 
    = (MyClass* (*)())dlsym(handle, "create_object");
      destroy 
    = (void (*)(MyClass*))dlsym(handle, "destroy_object");

      MyClass
    * myClass = (MyClass*)create();
      myClass
    ->DoSomething();
      destroy( myClass );
    }


    编译和运行:

     g++ -fPIC -shared myclass.cpp -o myclass.so
     g++ class_user.cpp -ldl -o class_user
     ./class_user

  • 相关阅读:
    sqlzoo练习答案--SUM and COUNT
    响应式的嵌入内容和图片
    缓存server设计与实现(五)
    编译器DIY——读文件
    [Leetcode]-Pascal&#39;s Triangle
    zoj 1562 反素数 附上个人对反素数性质的证明
    [POJ 1236][IOI 1996]Network of Schools
    POJ 3691 DNA repair [AC自动机 DP]
    POJ 1625 Censored! [AC自动机 高精度]
    51Nod 1225 余数之和 [整除分块]
  • 原文地址:https://www.cnblogs.com/hzcya1995/p/13318899.html
Copyright © 2011-2022 走看看