閱讀119 返回首頁    go 阿裏雲 go 技術社區[雲棲]


ubuntu下動態鏈接庫的編譯和使用實例

以下實例的環境是amd64 + ubuntu10.10 + g++ 4.4.5測試成功,在其他配置的機器上可能有一點區別。
    動態庫的使用方式中有兩種,第一種是類似於靜態庫的使用,另一種我稱之為真正的動態加載動態庫,主要是因為這種方式在程序運行的過程中加載鏈接庫,使用之後在卸載鏈接庫。
    先介紹第一種。
    在目錄/home/owner/test/下創建我們的實驗程序:
        //dll_fun.c
        #include <stdio.h>
        void dll_function(const char* szString)
        {
                printf("%s\n", szString);
        }
    編譯生成動態鏈接庫
        gcc -c -fPIC dll_fun.c //這裏一定要加上-fPIC選項,不然下一步編譯失敗
        gcc -shared -fPIC -o libdllfun.so dll_fun.o //生成動態鏈接庫libdllfun.so
    創建調用動態庫方法:
        //main.c
        void dll_function(const char* szString);
        int main()
        {
               dll_function("This is the words of the dll function!!!!!!");
               return 0; 
        }
    編譯main.c生成可執行文件
        gcc -o main main.c -L. -ldllfun //這裏提供了剛才生成的dllfun庫
    如果此時執行./main的話,會出現如下錯誤:
        cannot open shared object file: No such file or directory
    這是因為係統未找到動態庫libdllfun.so。
    Linux動態鏈接庫的默認搜索路徑是/lib和/usr/lib,因此動態庫被創建後,一般都複製到這兩個目錄下麵,當程序執行時需要某動態庫,並且改動態庫還沒有加載到內存中,則係統會自動到這兩個默認的搜索路徑中去查找相應的動態庫文件,然後加載改文件到內存中,這樣程序就可以使用該動態庫中的函數以及該動態庫中的其他資源了。在linux中,動態庫的搜索路徑除了默認的搜索路徑外,還可以通過其他三種方法來指定,這裏隻介紹其中的一種:通過環境變量LD_LIBRARY_PATH指定動態庫搜索路徑。
    當通過該環境變量指定多個動態鏈接庫搜索路徑時,路徑之間用冒號":"分隔。     
    使用下麵命令來配置環境
      mkdir /home/owner/test/lib//將這個目錄設置為動態庫的存放目錄
       mkdir/home/owner/test/libdllfun.so /home/owner/test/lib/libdllfun.so
       export LD_LIBRARY_PATH=/home/owner/test/lib
   此時設置這個環境變量之後的所有命令命令中,該環境變量都有效。
       ./main
   可得如下結果:
       This is the words of the dll function!!!!!!
 第二種加載動態庫實例:
       //dll_fun.c
       #include<stdio.h>
        void dll_function(const char* szString)
       {
             printf("%s\n", szString);
       }
    編譯該文件:
      gcc -Wall -fPIC -c dll_fun.c
      gcc -shared -W1,-soname,libdllfun.so.1 -o libdllfun.so.1.0 *.o
      sudo mv libdllfun.so.1.0 /usr/lib
      sudo ln -sf /usr/lib/libdllfun.so.1.0 /usr/lib/libdllfun.so
      sudo ln -sf /usr/lib/libdllfun.so.1.0 /usr/lib/libdllfun.so.1
    參數詳解:
      -Wall:包含warning信息
      -fPIC:編譯動態庫所必須的,輸出不依賴位置的代碼
       -shared:編譯動態庫必須選項
       -W1:向連接器傳遞一些參數,在這裏傳遞的參數有“-soname”,"libdllfun.so.1"
       -o:動態庫的名字,在這個例子裏最終生成動態庫libdllfun.so.1.0
    兩個符號鏈接的含義:
        第一個:允許應用代碼用-dllfun的語法進行編譯
        第二個:允許應用程序在運行時調用動態庫
    下麵是簡單的動態調用so的例子:
        增加dll_fun.h:
        //dll_fun.h
        #ifndef _DLL_FUN_H_
        #define _DLL_FUN_H_
        #ifdef __cplusplus
        extern "C"{
        #endif
        void dll_function(const char* szString);
        #ifdef __cplusplus
        }
        #endif
        #endif
    這裏我們仍然使用之前生成的libdllfun.so.1.0,增加一個新的應用程序cprog.c
        //cprog.c
        #include <stdio.h>
        #include <dlfcn.h> //dlopen, dlsym, dlerror, dlclose的頭文件
        #include <stdlib.h>
        #include "dll_fun.h"
        //此例為動態加載動態庫
        int main()
        {
               typedef void (*DLL_FUNCTION)(const char*):
               void* hHandle = NULL;
               DLL_FUNCTION fpFun = NULL;
               hHandle = dlopen("libdllfun.so", RTLD_LAZY);
               if(hHandle == NULL)
                        print("%s\n", dlerror());
               else
               {
                        fpFun = (DLL_FUNCTION)dlsym(hHandle, "dll_function");
                        char* szErrInfo = dlerror();
                        if(szErrInfo == NULL)
                                fpFun("This is the words of the dll function!!!!!!");
                        else
                                printf("Error: load dynamic library failed!\n");
                        dlclose(hHandle);
                }
                return 0;
        }
    用如下命令編譯運行:
        gcc -Wall cprog.c -ldllfun -ldl -o cprog 
        ./cprog
    方法簡介:
        dlopen("libdllfun.so", RTLD_LAZY):加載動態庫,如果加載失敗返回NULL,第二個參數可能有:
            RTLD_LAZY:lazy模式,直到程序運行到該行才嚐試加載
            RTLD_NOW:馬上加載
            RTLD_GLOBAL:Make symbol libraries visible
        dlsym(hHandle, "dll_function"):返回函數地址,如果查找函數失敗則返回NULL
    在這種方式下不知道怎麼修改動態鏈接庫搜索路徑,網上的一種方法是采用修改/etc/ld.so.conf,我試了一下,好像效果不是很好,而且要修改這個文件,感覺總不是太好,而且一樣的麻煩,希望有那個好心人能夠提供一個比較好的方法,讓我也學習一下,謝謝了:)。
    相關知識:
        命令ldd appname 可以查看應用程序所依賴的動態庫,運行如下命令:
            ldd cprog
        可得如下結果:
            linux-vdso.so.1 => (0x00007fff831ff000)
            libdllfun.so => /usr/lib/libdllfun.so (0x00007fa1798df000)
            libdl.so.2 => /lib/libdl.so.2 (0x00007fa1796db000)
            libc.so.6 => /lib/libc.so.6 (0x00007fa179357000)
            /lib64/ld-linux-x86-64.so.2 (0x00007fa179afc000)
        使用命令nm查看輸出函數:
            nm libdllfun.so
    編譯命令簡介:
        假設C文件是cprog.c,C++調用文件是cppprog.cpp,則編譯腳本分別是:
        C語言:
            gcc -Wall -I/path/to/include/headers -L/path/to/libraries cprog.c -ldllfun -ldl -o cprog
        C++語言:
            g++ -Wall -I/path/to/include/headers -L/path/to/libraries cppprog.cpp -ldllfun -ldl -o cppprog
        參數詳解:
            -I:指定頭文件目錄
            -L:指定庫目錄
            -ldllfun:調用動態庫libdllfun.so.1.0,如果在打包so時沒有創建第一個符號鏈接,那麼這個參數會導致編譯失敗
            -ldl:編譯動態庫必須,不然鏈接不到dlopen等方法
    C++開發帶class的動態庫
    以下幾個文件
        //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;
        }
        //classuser.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");/div>
                destroy = (void (*)(MyClass*))dlsym(handle, destroy_object");/div>
                MyClass* myClass = (MyClass*)create();
                myClass->DoSomething();
                destroy( myClass );
        }
    編譯和運行
        g++ -fPIC -shared myclass.cpp -o myclass.so
        g++ classuser.cpp -ldl -o classuser
        ./classuser

最後更新:2017-04-03 16:48:40

  上一篇:go 編譯、裁剪、安裝、刪除 Ubuntu內核和模塊管理
  下一篇:go FFMpeg.H264解碼win開發環境搭建