參考:ios
《程序員的自我修養 ---- 連接,裝載與庫》c++
《C++ dlopen mini HOWTO》http://www.tldp.org/HOWTO/pdf/C++-dlopen.pdf程序員
1,使用 c 生成動態連接庫mylib.so的簡單示例ide
聲明文件mylib.h函數
-spa
#ifndef __MY_LIB_H__ #define __MY_LIB_H__ void foobar(int i); #endif /* __MY_LIB_H__ */
-指針
實現文件mylib.c對象
-接口
#include <stdio.h> #include "mylib.h" void foobar(int i) { printf("Printing from mylib.so %d\n", i); }
-進程
使用 gcc 將 mylib.c 編譯成一個共享對象文件,以下命令:
[steven@sasd c++]$ gcc-fPIC -shared -o mylib.so mylib.c
[steven@sasd c++]$ ll
total 16
-rw-rw-r--. 1 steven steven 109 Apr 13 09:27 mylib.c
-rw-rw-r--. 1 steven steven 90 Apr 13 09:26 mylib.h
-rwxrwxr-x. 1 steven steven 6210 Apr 1309:28 mylib.so
-
注:
-
2這裏的共享對象其實全稱是動態共享對象文件(Dynamic Shared Objects,簡寫爲DSO);
2-fPIC:地址無關代碼(Position-Independent Code),該技術主要用於解決SO中對絕對地址的重定位問題;
-
2,動態連接.so
-
下面在咱們的c++程序中連接並使用此.so文件,在C++中,是使用 linkage directive 方式指出任意非C++函數所用的語言,也就是用extern 「C」來指示對應的聲明是使用C來實現的,這樣對於該聲明中的文件的編譯會採用對應的C編譯器來編譯處理,以下爲使用示例:
-
// use_mylib.cpp #include <iostream> #include <string> using namespace std; #ifdef __cplusplus extern "C" { #endif #include "mylib.h" // for foobar() #ifdef __cplusplus } #endif int main() { foobar(100); return 0; }
-
編譯及運行結果:
-
[steven@sasd c++]$ g++ -o use_mylibuse_mylib.cpp ./mylib.so
[steven@sasd c++]$ ./use_mylib
Printing from mylib.so 100
[steven@sasd c++]$
-
3,運行時動態加載.so
-
動態庫的運行時加載時經過一系列由動態連接器(dynamic linker)提供的API來實現的,這幾個API的實現都是在libdl.so中的,且相關的聲明和常量定義在頭文件<dlfcn.h>中。
-
dlopen():主要用來打開一個.so,並將其加載到進程的地址空間,完成初始化過程,原型以下:
-
void *dlopen(const char *filename, int flag);
-
filename:the dynamic library file named by the null-terminated string;對於路徑設置爲相對路徑或絕對路徑情形下,dlopen()是如何查找該動態庫文件的,這個直接參考man;
-
flag:用於表示函數符號的解析方式,如:RTLD_LAZY表示使用延遲綁定,只有在函數第一次被使用時纔會去綁定,即PLT機制;相對應的爲RTLD_NOW表示當模塊被加載時即完成全部的函數綁定工做,若是有任何未定義的符號引用的綁定工做沒法完成,dlopen都直接返回錯誤;其餘的flag標識能夠直接參考man;
-
返回值是被加載的模塊的句柄,該句柄會被其餘的幾個API使用到,若是加載失敗,則返回NULL。
-
dlsym():經過此函數來找到所須要的符號,定義以下:
-
void *dlsym(void *handle, const char *symbol);
-
第一個參數handle就是dlopen返回的句柄,第二個參數symbol表示要查找的符號的名字,一個以‘\0’ 結尾的字符串;
若是dlsym找到了相應的符號,則返回該符號的值;若是沒有找到,則返回NULL。
對於不一樣類型的符號,返回的指針意義不一樣:若是爲函數,則爲函數地址;若是爲變量,則爲變量的地址;若是爲常量,則應該爲常量的值;
-
dlerror():在調用dlopen,dlsym,dlclose以後,均可以經過dlerror()來判斷上一次調用是否成功,dlerror的返回值爲char*,若是返回NULL,則表示調用成功;不然返回對應的錯誤消息;函數原型聲明以下:
-
char *dlerror(void);
-
dlclose():卸載一個已經加載的模塊。
-
在系統內部會維持一個加載引用計數器,每次調用dlopen()來加載模塊時,相應的計數器加1;每次調用dlclose()卸載某模塊時,相應計數器減1。當計數器等於0時,該模塊纔會真正被卸載掉。
-
下面直接上示例:
-
// ex_dload_so.cpp #include <iostream> using namespace std; #include <dlfcn.h> int main() { // 爲上面生成的.so文件的絕對路徑 const char *MYLIB_PATH = "/home/steven/test/c++/mylib.so"; // 加載指定的 .so 文件 void *handle = dlopen(MYLIB_PATH, RTLD_NOW); if (handle == NULL) { cerr << "Open library " << MYLIB_PATH << " error: " << dlerror() << endl; return -1; } // 查找函數foobar,並返回函數指針 void (*fn_foobar)(int); fn_foobar = (void (*)(int))dlsym(handle, "foobar"); char *error = NULL; if ((error = dlerror()) != NULL) { cerr << "Symbol foobar not found: " << error << endl; dlclose(handle); return -2; } // 調用對應的foobar函數打印輸出 fn_foobar(123); dlclose(handle); return 0; }
-
編譯運行效果:
-
[steven@sasd c++]$ g++-ldl -o ex_dload_so ex_dload_so.cpp
[steven@sasd c++]$ ./ex_dload_so
Printing from mylib.so 123
[steven@sasdc++]$-
4,使用c++建立和使用含有class的.so,採用多態機制實現接口發佈,示例摘自《C++ dlopen mini HOWTO》
-
1)生成動態連接庫triangle.so文件
-
// polygon.h #ifndef __POLYGON_H__ #define __POLYGON_H__ class Polygon { protected: double m_side_len; public: Polygon() : m_side_len(0) {} virtual ~Polygon() {} void set_side_len(double side_len) { m_side_len = side_len; } virtual double area() const = 0; }; typedef Polygon* create_t(); typedef void destroy_t(Polygon*); #endif /* __POLYGON_H__ */
-
// triangle.cpp #include "polygon.h" #include <cmath> class Triangle: public Polygon { public: virtual double area() const { return m_side_len * m_side_len * sqrt(3) / 2; } }; extern "C" Polygon* create() { return new Triangle; } extern "C" void destroy(Polygon *p) { delete p; }
-
編譯生成 triangle.so
-
[steven@sasd c++]$ g++ -fPIC -shared -o triangle.so triangle.cpp
-
2) 使用該動態連接庫
-
// ex_dload_cppso.cpp #include <iostream> #include <string> #include <dlfcn.h> using namespace std; #include "polygon.h" int main() { // load triangle library void *triangle = dlopen("./triangle.so", RTLD_LAZY); if (!triangle) { cerr << "Can not load library: " << dlerror() << endl; return -1; } // reset errors dlerror(); // load the symbols "create" create_t *create_triangle = (create_t*) dlsym(triangle, "create"); const char *dlsym_err = dlerror(); if (dlsym_err) { cerr << "Can not load symbol create: " << dlsym_err << endl; return -2; } // load the symbols "destroy" destroy_t* destroy_triangle = (destroy_t*) dlsym(triangle, "destroy"); dlsym_err = dlerror(); if (dlsym_err) { cerr << "Can not load symbol destroy: " << dlsym_err << endl; return -2; } // create an instance of class <Trangle> Polygon *poly = create_triangle(); // set side length of <Triagle> poly->set_side_len(8); cout << "The area is " << poly->area() << endl; // destroy the class destroy_triangle(poly); // unload the triangle.so library dlclose(triangle); return 0; }
-
編譯運行:
-
[steven@sasd c++]$ g++ -ldl -o ex_dload_cppso ex_dload_cppso.cpp
[steven@sasd c++]$ ./ex_dload_cppso
The area is 55.4256
[steven@sasd c++]$