linux下動態鏈結庫的顯式呼叫和隱式呼叫

2021-07-24 08:41:52 字數 3118 閱讀 6423

**:

一、顯式呼叫和隱式呼叫的區別

動態庫相比靜態庫的區別是:靜態庫是編譯時就載入到可執行檔案中的,而動態庫是在程式執行時完成載入的,所以使用動態庫的程式的體積要比使用靜態庫程式的體積小,並且使用動態庫的程式在執行時必須依賴所使用的動態庫檔案(.so檔案),而使用靜態庫的程式一旦編譯好,就不再需要依賴的靜態庫檔案了(.a檔案)。

動態庫的呼叫又分為顯示和隱式兩種方式,區別如下:

1、 隱式呼叫需要呼叫者寫的**量少,呼叫起來和使用當前專案下的函式一樣直接;而顯式呼叫則要求程式設計師在呼叫時,指明要載入的動態庫的名稱和要呼叫的函式名稱。

2、隱式呼叫由系統載入完成,對程式設計師透明;顯式呼叫由程式設計師在需要使用時自己載入,不再使用時,自己負責解除安裝。

3、由於顯式呼叫由程式設計師負責載入和解除安裝,好比動態申請記憶體空間,需要時就申請,不用時立即釋放,因此顯式呼叫對記憶體的使用更加合理, 大型專案中應使用顯示呼叫。

4、當動態鏈結庫中只提供函式介面,而該函式沒有封裝到類裡面時,如果使用顯式呼叫的方式,呼叫方不需要包含動態鏈結庫的標頭檔案(需要呼叫的函式名是通過dlsym函式的引數指明的),而使用隱式呼叫時,則呼叫方不可避免要加上動態庫中的標頭檔案,g++編譯時還需要要用引數-i指明包含的標頭檔案的位置。需要注意的是,當動態鏈結庫中的介面函式是作為成員函式封裝在類裡面時,即使使用顯式呼叫的方式,呼叫方也必須包含動態庫中的相應標頭檔案(詳見

五、顯示呼叫動態鏈結中的類成員函式)。

5、顯式呼叫更加靈活,可以模擬多型效果(具體見後文)。

二、extern "c"的作用

c++程式(或庫、目標檔案)中,所有非靜態(non-static)函式在二進位制檔案中都是以「符號(symbol)」形式出現的。這些符號都是唯一的字串,從而把各個函式在程式、庫、目標檔案中區分開來。在c中,符號名正是函式名,兩者完全一樣。而c++允許過載(不同的函式有相同的名字但不同的引數,甚至const過載),並且有很多c所沒有的特性──比如類、成員函式、異常說明──幾乎不可能直接用函式名作符號名。為了解決這個問題,c++採用了所謂的name mangling。它把函式名和一些資訊(如引數數量和大小)雜糅在一起,改造成奇形怪狀,只有編譯器才懂的符號名。例如,被mangle後的foo可能看起來像foo@4%6^,或者,符號名裡頭甚至不包括「foo」。

其中乙個問題是,c++標準並沒有定義名字必須如何被mangle,所以每個編譯器都按自己的方式來進行name mangling。有些編譯器甚至在不同版本間更換mangling演算法(尤其是g++ 2.x和3.x)。前文說過,在顯示呼叫動態庫中的函式時,需要指明呼叫的函式名,即使您搞清楚了您的編譯器到底怎麼進行mangling的,從而知道呼叫的函式名被c++編譯器轉換為了什麼形式,,但可能僅僅限於您手頭的這個編譯器而已,而無法在下一版編譯器下工作。

extern "c"即可以解決這個問題。用 extern "c"宣告的函式將使用函式名作符號名,就像c函式一樣。因此,只有非成員函式才能被宣告為extern "c",並且不能被過載。儘管限制多多,extern "c"函式還是非常有用,因為它們可以象c函式一樣被dlopen動態載入。冠以extern "c"限定符後,並不意味著函式中無法使用c++**了,相反,它仍然是乙個完全的c++函式,可以使用任何c++特性和各種型別的引數。所以extern "c" 只是告訴編譯器編和鏈結的時候都用c的方式的函式名字,函式裡的內容可以為c的**也可以為c++的。

三、顯式呼叫

通過輸入不同的引數,呼叫了不同的共享庫中的fcn函式,是一種多型的表現,許多軟體的不同外掛程式就是這樣實現的。

需要注意的是,要使用顯式呼叫的方式,必須加入標頭檔案dlfcn.h,編譯命令中要加入引數-ldl,否則報錯。

dlfcn.h中提供的api說明如下:

1)        dlopen

函式原型:void *dlopen(const char *libname,int flag);

功能描述:dlopen必須在dlerror,dlsym和dlclose之前呼叫,表示要將庫裝載到記憶體,準備使用。如果要裝載的庫依賴於其它庫,必須首先裝載依賴庫。如果dlopen操作失敗,返回null值;如果庫已經被裝載過,則dlopen會返回同樣的控制代碼。

引數中的libname一般是庫的全路徑,這樣dlopen會直接裝載該檔案;如果只是指定了庫名稱,在dlopen會按照下面的機制去搜尋:

a.根據環境變數ld_library_path查詢

b.根據/etc/ld.so.cache查詢

c.查詢依次在/lib和/usr/lib目錄查詢。

flag引數表示處理未定義函式的方式,可以使用rtld_lazy或rtld_now。rtld_lazy表示暫時不去處理未定義函式,先把庫裝載到記憶體,等用到沒定義的函式再說;rtld_now表示馬上檢查是否存在未定義的函式,若存在,則dlopen以失敗告終。

2)        dlerror

函式原型:char *dlerror(void);

功能描述:dlerror可以獲得最近一次dlopen,dlsym或dlclose操作的錯誤資訊,返回null表示無錯誤。dlerror在返回錯誤資訊的同時,也會清除錯誤資訊。

3)        dlsym

函式原型:void *dlsym(void *handle,const char *symbol);

功能描述:在dlopen之後,庫被裝載到記憶體。dlsym可以獲得指定函式(symbol)在記憶體中的位置(指標)。如果找不到指定函式,則dlsym會返回null值。但判斷函式是否存在最好的方法是使用dlerror函式,

4)        dlclose

函式原型:int dlclose(void *);

功能描述:將已經裝載的庫控制代碼減一,如果控制代碼減至零,則該庫會被解除安裝。如果存在析構函式,則在dlclose之後,析構函式會被呼叫。

四、隱式呼叫

隱式呼叫不需要包含標頭檔案dlfcn.h,只需要包含動態鏈結庫中的標頭檔案,使用動態庫中的函式也不需要像顯示呼叫那麼複雜。

五、顯式呼叫動態鏈結中的類成員函式

顯示呼叫動態鏈結庫的類成員函式,有單獨的寫法,但比較少用。推薦的寫法是為每個要被外部呼叫的類成員函式設計乙個普通的介面函式,在介面函式內部使用類的成員函式。當然這就需要將類設計為單例模式,因為不可能在每個介面函式中都構造乙個類的物件。

Linux下動態鏈結庫和靜態鏈結庫

第一部分 編譯過程 預處理過程,負責標頭檔案展開,巨集替換,條件編譯的選擇,刪除注釋等工作。gcc e表示進行預處理。編譯過程,負載將預處理生成的檔案,經過詞法分析,語法分析,語義分析及優化後生成彙編檔案。gcc s表示進行編譯。彙編,是將彙編 轉換為機器可執行指令的過程。通過使用gcc c或者as...

Linux下動態鏈結庫呼叫

2014 11 01 10 39 3人閱讀收藏 編輯 刪除2013 06 08 20 52 48 分類 整合開發環境相關 舉報 字型大小訂閱 linux下的靜態鏈結庫,做起來比較容易,只要將目標檔案用ar打包就可以,下面寫一下動態鏈結庫的製作和使用方法,完全是根據個人理解和經驗總結,有不對的地方還請...

linux下的動態鏈結庫 DLL

一 公約 1.庫的命名習慣 乙個linux dll 有三個不同名字的檔案組成 soname 檔案 lib 鏈結庫名字 so 版本號 每當鏈結庫介面改變時都遞增版本號。soname 檔案其實只是乙個符號鏈結而已,指向他的real name 檔案。real name 檔案 lib 鏈結庫名字 so 版本...