Linux共享庫兩種加載方式簡述
動態庫技術通常能減少程序的大小,節省空間,提高效率,具有很高的靈活性,對于升級軟件版本也更加容易。與靜態庫不同,動態庫里面的函數不是執行程序本身 的一部分,而是在程序執行時按需載入,其執行代碼可以同時在多個程序中共享。由于在編譯過程中無法知道動態庫函數的地址,所以需要在運行期間查找,這對程 序的性能會有影響。
共享庫
對于共享庫來講,它只包括2個段:只讀的代碼段 和可修改的數據段。堆和棧段,只有進程才有。如果你在共享庫的函數里,分配了一塊內存,這段內存將被算在調用該函數的進程的堆中。代碼段由于其內容是對每 個進程都是一樣的,所以它在系統中是唯一的,系統只為其分配一塊內存,多個進程之間共享。數據段由于其內容對每個進程是不一樣的,所以在鏈接到進程空間 后,系統會為每個進程創建相應的數據段。也就是說如果一個共享庫被N個進程鏈接,當這N個進程同時運行時,同時共享一個代碼段,每個進程擁有一個數據段,系統中共有N個數據段。PIC即position independent code,使.so文件的代碼段變為真正意義上的共享。如果編譯時不加-fPIC,則加載.so文件的代碼段時,代碼段引用的數據對象需要重定位, 重定位會修改代碼段的內容,這就造成每個使用這個.so文件代碼段的進程在內核里都會生成這個.so文件代碼段的copy。
加載方式
-
靜態加載
在程序編譯的時候加上“-l”選項,指定其所依賴的動態庫,這個庫的名字將記錄在ELF文件的.dynamic節。在程序運行時,loader會預先將程序所依賴的所有動態庫都加載在進程空間中。
優點:動態庫的接口調用簡單,可以直接調用。
缺點:動態庫的生存周期等于進程的生存周期,其加載時機不靈活。 動態加載
在程序中編碼來指定加載動態庫的時機,經常使用的函數dlopen和dlclose。
優點:動態庫加載的時機非常靈活,可以非常細致的定義動態庫的生存周期。
缺點:動態庫的接口調用起來比較麻煩,同時還要關注動態庫的生存周期。
#include <dlfcn.h>
void * dlopen( const char * *pathname*, int *mode* );**
函數描述:
在dlopen的()函數以指定模式打開指定的動態連接庫文件,并返回一個句柄給調用進程。使用dlclose()來卸載打開的庫。
mode:分為這兩種
RTLD_LAZY 暫緩決定,等有需要時再解出符號
RTLD_NOW 立即決定,返回前解除所有未決定的符號。
返回值:
打開錯誤返回NULL
成功,返回庫引用
編譯時候要加入 -ldl (指定dl庫)
在dlopen一個共享庫時:
a、進程會加載該共享庫的代碼段和數據段,同時為這個共享庫計數加1。
b、進程查找該共享庫的dynamic節,查看其所依賴的共享庫。
c、首先檢查所依賴庫是否已經被加載,如果已被加載,則為這個共享庫計數加1。如果未被加載,則加載其代碼段和數據段,然后為這個共享庫計數加1。
d、再查找這些庫所依賴的庫。最終進程會為每個加載的共享庫維護一個依賴的計數。
int dlclose (void *handle);
函數描述:
**dlclose用于關閉指定句柄的動態鏈接庫,只有當此動態鏈接庫的使用計數為0時,才會真正被系統卸載。
在dlclose共享庫時:
a、首先將該共享庫的計數減1,如果該共享庫依賴計數為0,則卸載該共享庫。
b、在dynamic節中,查找其所依賴的共享庫。
c、為每個共享庫的計數減1,如果該共享庫依賴計數為0,則卸載該共享庫。
d、重復上面的步驟。
優點:
a、可以在程序啟動的時候,減少加載庫的數量,這樣可以加快進程的啟動速度和減少加載庫的內存使用。
b、為進程提供了卸載共享庫的機會,這樣就可以回收共享庫代碼段和數據段所占用的內存。
缺點:
對于程序員編碼來講,會產生一定的疑惑。一個static的對象的生存周期是貫穿在進程始終的,實際上不是這樣。在動態庫中的static對象,其生命周期等于該動態庫的生命周期。采用靜態鏈接的方式,動態庫的生命周期等于進程的生命周期;而采用動態加載的方式,則是不同的。
原文地址:http://blog.csdn.net/yeahguyewen/article/details/6275292
Android 7.0 SO庫鏈接策略變更
基于命名空間的動態鏈接—— 隔離 Android 中應用程序和系統的本地庫
android下運行時動態鏈接dlopen()和dlsym()的實現
linux下動態鏈接庫(.so)的顯式調用和隱式調用
Android 7.0 dlopen 函數分析
深入理解 System.loadLibrary
Dynamic Linking
ELF文件格式解析
Linux下程序的加載與運行
共享庫中的位置無關代碼(PIC)
ELF_Tutorial