• 动态共享库(so)开发精悍教程


    动态共享库(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文件

    在代码的目录下运行如下命令:

    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


  • 相关阅读:
    图片上传-下载-删除等图片管理的若干经验总结3-单一业务场景的完整解决方案
    图片上传-下载-删除等图片管理的若干经验总结2
    HDU 1195 Open the Lock
    HDU 1690 Bus System
    HDU 2647 Reward
    HDU 2680 Choose the best route
    HDU 1596 find the safest road
    POJ 1904 King's Quest
    CDOJ 889 Battle for Silver
    CDOJ 888 Absurdistan Roads
  • 原文地址:https://www.cnblogs.com/timssd/p/4567871.html
Copyright © 2020-2023  润新知