2019 march 17
靜態鏈結庫, 動態鏈結庫
由於我對windows的程式設計環境比較熟悉,所以首先對windows下的程式設計環境進行乙個簡單的介紹。在windows中進行c++程式設計時,我們會經常呼叫一些函式庫,因此不可避免地會經常接觸到obj、lib和dll這些檔案,當然也會接觸到各種include目錄。
obj檔案由編譯器(compiler)編譯生成,其內容為本地**(***** code,對應於特定cpu的機器**),其中包括外部符號,也就是對其他**函式(或者庫函式)的引用(所以如果出現每個函式未出現在其他**中或者引用庫函式中,在鏈結階段就會出現「未解析的外部符號」的錯誤)。
在windows中,cpp檔案是源**檔案,lib、dll和exe檔案屬於最終的生成檔案。obj檔案是一種介於源**和最終生成檔案的中間**。通常乙個程式(例如exe檔案)由多個源**組成,或者依賴其他函式庫(lib、dll等檔案),單個obj檔案不具有整個程式執行所需要的所有內容。形成乙個完整的可執行檔案,需要通過鏈結器(linker),將不同源**生成的obj檔案和所呼叫的庫函式鏈結到一起。因此,obj檔案是一種中間**而非最終的可執行檔案。
接著介紹lib檔案的作用。lib檔案通常被稱為靜態鏈結庫(static library)。純粹的靜態鏈結庫包含了函式符號和函式實現**。在編譯的鏈結階段,靜態鏈結庫中的內容被包括到了編譯得到的可執行檔案中。因此,在只鏈結了靜態鏈結庫的情況下,可以認為所得到的編譯結果不依賴於外部動態鏈結庫。
我個人認為「靜態鏈結庫」這個稱呼不夠貼切,可以從上面dll檔案的作用中看出,編譯的鏈結過程中,動態鏈結庫也會使用到lib。在這種情況下,我們把lib檔案稱為靜態鏈結庫,從字面上給人的感覺就是和動態鏈結庫是互斥的。所以,我認為將lib檔案稱為引入庫檔案(import library)更加合適。不過,從另外一方面來說,引入庫檔案在鏈結時將所要呼叫的函式在dll檔案中的入口鏈結到了對應的生成檔案中,也可以認為是一種靜態鏈結。
靜態鏈結在**編譯的時候,進行靜態載入;而動態鏈結在程式執行的時候,進行動態載入。lib在編譯階段需要用到,而dll檔案在程式執行時需要用到。如果只需要完成源**編譯,那麼在編譯階段新增lib檔案就夠了;如果要讓編譯得到的程式執行起來,那麼就分為2種情況:
如果乙個函式庫使用動態鏈結庫的形式進行發布(release),當然這個函式庫是用來給其他開發者呼叫的,那麼這個函式庫需要包含lib檔案和dll檔案。lib檔案包含了函式符號,也就是告訴程式到**找引用函式的實現**,而dll檔案包含了**的實現。因此,對於開發者來說lib檔案和dll檔案二者都必不可少。編譯得到的程式中只包含了所呼叫函式的函式符號,但是並未包含所呼叫函式的實現**,因此程式執行的時候仍然需要附帶對應的dll檔案。
如果乙個函式庫採用了靜態鏈結的方式發布,那麼只需要提供lib檔案就可以了。因為lib檔案中已經包含了函式符號和函式實現,也就是說已經包含了程式所有所需要的內容。在程式編譯階段,lib檔案的內容已經被鏈結到了程式中,這樣程式在執行的時候不需要再額外提供lib檔案了。
靜態鏈結是將程式執行所需要的**都整合到程式中,因此對外部環境的依賴較小,不需要新增額外的依賴檔案(例如dll檔案)。這是靜態鏈結最大的優點,也是靜態鏈結的問題所在。靜態鏈結將用到的**都新增到程式中,就會導致編譯得到的程式,無論是在記憶體上還是在硬碟上,占用空間比較龐大。
在上一節windows程式設計環境中,已經介紹完了靜態鏈結庫和動態鏈結庫的基本概念。這一節將在上一節的基礎上,對照windows下的程式設計環境進一步介紹linux下的程式設計環境。首先,介紹linux下目標檔案、靜態鏈結庫和動態鏈結庫與windows上的對應關係:
由於windows下,開發者通常採用visual studio等整合開發環境(ide)。對於開發者來說,程式的編譯鏈結通常是一步完成的。因此,不容易接觸到,ide在編譯時每一步都進行了什麼操作。而linux下使用gcc(gnu compiler collection)編譯能夠很好的體會編譯的過程,下面通過gcc編譯的例子來介紹linux中程式的編譯的具體過程。
對於如下c語言程式,使用gcc進行編譯。
#include
intmain
(void
)
以上程式可以通過gcc一步到位完成編譯,命令如下:
$ gcc test.c -o
test
預處理
$ gcc -e test.c -o test.i 或 gcc -e test.c
生成的test.i檔案存放著test.c經過預處理之後的**,也就是將stdio.**件中的內容插入到了test.c檔案中了。
編譯為彙編**
$ gcc -s test.i -o test.s.
預處理之後,直接對生成的test.i檔案進行編譯,生成彙編**test.s。
彙編
$ gcc -c test.s -o test.o
將彙編**test.s進行編譯生成目標檔案test.o。
鏈結
$ gcc test.o -o
test
下面以mysql connectors的c庫為例,介紹使用gcc鏈結庫函式的過程。函式庫包括乙個include檔案,路徑為/usr/dev/mysql/include;乙個lib檔案,路徑為/usr/dev/mysql/lib,裡面包含二進位制so檔案libmysqlclient.so。
編譯得到目標檔案
$ gcc –c –i /usr/dev/mysql/include test.c –o test.o
鏈結生成可執行檔案
$ gcc –l /usr/dev/mysql/lib –lmysqlclient test.o –o test
也可以強制鏈結時使用靜態鏈結庫
$ gcc –l /usr/dev/mysql/lib –static –lmysqlclient test.o –o test
預設情況下, gcc在鏈結時優先使用動態鏈結庫,只有當動態鏈結庫不存在時才考慮使用靜態鏈結庫,如果需要的話可以在編譯時加上-static選項,強制使用靜態鏈結庫。
在/usr/dev/mysql/lib目錄下有鏈結時所需要的庫檔案libmysqlclient.so和libmysqlclient.a,為了讓gcc在鏈結時只用到靜態鏈結庫,可以使用以上命令。
ld會去找gcc命令中的引數-l;
再找gcc的環境變數library_path;
再找內定目錄 /lib、/usr/lib和/usr/local/lib,這是當初compile gcc時寫在程式內的。
編譯目標**時指定的動態庫搜尋路徑
環境變數ld_library_path指定的動態庫搜尋路徑
配置檔案/etc/ld.so.conf中指定的動態庫搜尋路徑
預設的動態庫搜尋路徑/lib
預設的動態庫搜尋路徑/usr/lib
靜態鏈結庫和動態鏈結庫
其實再vc中,我們所用得所有api函式都封裝再下列三個dll檔案中 kernel32.dll 用於管理記憶體,程序和執行緒得各個函式 user32.dll 用於執行使用者介面任務,如視窗的建立和訊息的傳遞的各個函式 gdi32.dll 用於顯示文字和畫圖的各個函式 動態鏈結庫 我們再使用動態庫的時候...
靜態鏈結庫和動態鏈結庫
靜態鏈結庫 win32 static library 呼叫libtest.lib 動態dll win32 dynamic link library 跟librest的生成是一樣的 動態呼叫 成的.lib 和.dll 檔案拷入dllcall 工程所在的路徑,dllcall 執行下列 dll 中匯出函式...
靜態鏈結庫和動態鏈結庫
以前的時候寫過這方面的部落格,當時寫的時候都覺得已經懂了。今天閒著沒事幹,和咚哥他們去大華校招筆試遇到了這個問題。我突然間發現sdk沒寫三個月,把dll都忘記了。回來看了下,複習下。以前寫過程式的裝載和鏈結的部落格,那是os上面比較理論的。實際用函式庫的時候主要分為,靜態庫和動態庫,這裡就簡簡單單地...