區別:1、動態庫的后綴為“.so”,靜態庫的后綴為“.a”。2、如果靜態函數庫改變了,那么程序必須重新編譯;而動態函數庫的改變并不影響程序。3、相對于靜態庫,動態庫在編譯的時候并沒有被編譯進目標代碼中,用戶的程序執行到相關函數時才調用該函數庫里的相應函數,因此動態函數庫所產生的可執行文件比較小。
本教程操作環境:linux7.3系統、Dell G3電腦。
一、庫的基礎概念:
在windows平臺和linux平臺下都大量存在著庫。本質上來說庫是一種可執行代碼的二進制形式,可以被操作系統載入內存執行。由于windows和linux的本質不同,因此二者庫的二進制是不兼容的。通俗的說就是把這些常用函數的目標文件打包在一起,提供相應函數的接口,便于程序員使用。在使用函數時,只需要包對應的頭文件即可。按照庫的使用方式又可分為動態庫和靜態庫,在不同平臺下對應后綴也有所不同。
WINDOWS下:.dll 后綴為動態庫,.lib 后綴為靜態庫;
LINUX下:.so后綴為動態庫,.a后綴為靜態庫。
二、靜態庫與靜態鏈接
靜態庫:
靜態庫可以簡單的看成一組目標文件的集合,即很多目標文件經過壓縮打包后形成的文件。比如在我們日常編程中,如果需要使用printf函數,就需要包stdio.h的庫文件,使用strlen時,又需要包String.h的庫文件,可是如果直接把對應函數源碼編譯后形成的.o文件直接提供給我們,將會對我們的管理和使用上造成極大不便,于是可以使用“ar”壓縮程序將這些目標文件壓縮在一起,形成libx.a靜態庫文件。
注:靜態庫命名格式:lib + “庫名稱”+ .a(后綴) 例:libadd.a就是一個叫add的靜態庫
靜態鏈接:
對于靜態庫,程序在編譯鏈接時,將庫的代碼鏈接到可執行文件中,程序運行時不再需要靜態庫。在使用過程中只需要將庫和我們的程序編譯后的文件鏈接在一起就可形成一個可執行文件。
通過一個例子來了解下如何將我們自己寫的頭文件和代碼同時進行編譯鏈接,最終生成可執行文件:
/main.c/ #include?<stdio.h> #include?"add.h" int?main() { int?ret?=?add(3,?4); printf("3?+?4?=?%dn",ret); return?0; } /add.c/ #include?"add.h" int?add(?int?x,?int?y) { return?x?+?y; } /add.h/ #pragma?once #include?<stdio.h> int?add(?int?x,?int?y); /Makefile/ main?:?main.c?libadd.a gcc?main.c?-L?.?-ladd?-o?main //-L為指定路徑?.為當前目錄下?-l+庫名字,編譯器可在指定目錄下自己尋找名為add的庫文件 libadd.a?:? gcc?-c?add.c?-o?add.o //ar?-rc將多個編譯后的文件打包為一個靜態庫文件 ar?-rc?libadd.a?add.o .PHONY:clean clean: rm?main?libadd.a</stdio.h></stdio.h>
make后輸出截圖:
缺點:
1、內存和磁盤空間浪費:靜態鏈接方式對于計算機內存和磁盤的空間浪費十分嚴重。
假如一個c語言的靜態庫大小為1MB,系統中有100個需要使用到該庫文件,采用靜態鏈接的話,就要浪費進100M的內存,若數量再大,那浪費的也就更多。例如下圖:程序1和程序2都需要用到Lib.o,采用靜態鏈接的話,那么物理內存中就會存放兩份對應此文件的拷貝。
2、更新麻煩:
比如一個程序20個模塊,每個模塊只有1MB,那么每次更新任何一個模塊,用戶都得重新下載20M的程序。
三、動態庫與動態鏈接
動態庫:
程序在運行時才去鏈接動態庫的代碼,多個程序共享庫的代碼。一個與動態庫鏈接的可執行文件僅僅包含它用到的函數入口地址的一個表,而不是外部函數所在目標文件的整個機器碼。
注:動態庫命名格式:lib + “庫名稱”+ .so(后綴) 例:libadd.so就是一個叫add的動態庫
動態鏈接:
由于靜態鏈接具有浪費內存和模塊更新困難等問題,提出了動態鏈接。基本實現思想是把程序按照模塊拆分成各個相對獨立部分,在程序運行時才將他們鏈接在一起形成一個完整的程序,而不是像靜態鏈接那樣把所有的程序模塊都鏈接成一個單獨的可執行文件。所以動態鏈接是將鏈接過程推遲到了運行時才進行。
同樣,假如有程序1,程序2,和Lib.o三個文件,程序1和程序2在執行時都需要用到Lib.o文件,當運行程序1時,系統首先加載程序1,當發現需要Lib.o文件時,也同樣加載到內存,再去加載程序2當發現也同樣需要用到Lib.o文件時,則不需要重新加載Lib.o,只需要將程序2和Lib.o文件鏈接起來即可,內存中始終只存在一份Lib.o文件。
動態庫和動態鏈接的例子依然使用上面的代碼,輸出結果也相同,唯一需要改變的就是Makefile文件。
/Makefile/ main : main.c libadd.so gcc main.c -L . -ladd -o main libadd.so : gcc -fPIC -shared add.c -o libadd.so //-shared表示輸出結果是共享庫類型的 -fPIC表示使用地址無關代碼奇數來生產輸出文件 .PHONY:clean clean: rm main libadd.so
-
當我們生成可執行文件后,可使用ldd命令查看該可執行文件所依靠的動態庫。
-
前面提到windows和Linux下庫文件的后綴不同,更根本的原因在于二者文件格式都不同??梢酝ㄟ^file一個動態庫查看Linux下動態庫的文件類型其實是ELF格式。ELF動態鏈接文件被稱為動態共享對象(DSO,Dynamic Shared Objects),簡稱共享對象;在windows下,動態鏈接文件被稱為動態鏈接庫(Dynamic Linking Library),也就是.dll文件后綴的全稱。
優點:
-
①毋庸置疑的就是節省內存;
-
②減少物理頁面的換入換出;
-
③在升級某個模塊時,理論上只需要將對應舊的目標文件覆蓋掉即可。新版本的目標文件會被自動裝載到內存中并且鏈接起來;
-
④程序在運行時可以動態的選擇加載各種程序模塊,實現程序的擴展。
四、靜態庫和動態庫的區別
1.?靜態庫?
這類庫的名字一般是?libxxx.a?;利用靜態函數庫編譯成的文件比較大,因為整個?函數庫的所有數據都會被整合進目標代碼中,他的優點就顯而易見了,即編譯后的執行程序不需要外部的函數庫支持,因為所有使用的函數都已經被編譯進去了。當然這也會成為他的缺點,因為?如果靜態函數庫改變了,那么你的程序必須重新編譯?。?
2.?動態庫?
這類庫的名字一般是?libxxx.so?;相對于靜態函數庫,動態函數庫在編譯的時候并沒有被編譯進目標代碼中,你的程序執行到相關函數時才調用該函數庫里的相應函數,因此動態函數庫所產生的可執行文件比較小。由于函數庫沒有被整合進你的程序,而是程序運行時動態的申請并調用,所以程序的運行環境中必須提供相應的庫。?動態函數庫的改變并不影響你的程序,所以動態函數庫的升級比較方便。
相關推薦:《Linux視頻教程》