1.庫的介紹
什么是庫
庫是二進制文件, 是源代碼文件的另一種表現(xiàn)形式, 是加了密的源代碼; 是一些功能相近或者是相似的函數(shù)的集合體.-
使用庫有什么好處
- 提高代碼的可重用性, 而且還可以提高程序的健壯性;
- 可以減少開發(fā)者的代碼開發(fā)量, 縮短開發(fā)周期.
-
庫制作完成后, 如何給用戶使用
- 頭文件---包含了庫函數(shù)的聲明
- 庫文件---包含了庫函數(shù)的代碼實現(xiàn)
注意: 庫不能單獨使用, 只能作為其他執(zhí)行程序的一部分完成某些功能, 也就是說只能被其他程序調(diào)用才能使用.
庫可分靜態(tài)庫(static library)和共享庫(shared library)
2.靜態(tài)庫(static library)
??靜態(tài)庫可以認為是一些目標代碼的集合, 是在可執(zhí)行程序運行前就已經(jīng)加入到執(zhí)行碼中, 成為執(zhí)行程序的一部分. 按照習慣, 一般以.a做為文件后綴名.
- 靜態(tài)庫的命名一般分為三個部分:
- 前綴:lib
- 庫名稱:自定義即可, 如test
- 后綴:.a
所以最終的靜態(tài)庫的名字應(yīng)該為:libtest.a
靜態(tài)庫的制作
下面以fun1.c , fun2.c和head.h三個文件為例講述靜態(tài)庫的制作和使用, 其中head.h文件中有函數(shù)的聲明, fun1.c和fun2.c中有函數(shù)的實現(xiàn).步驟1:將c源文件生成對應(yīng)的.o文件
gcc -c fun1.c fun2.c
或者分別生成.o文件:
gcc -c fun1.c -o fun1.o
gcc -c fun2.c -o fun2.o-
步驟2:使用打包工具ar將準備好的.o文件打包為.a文件
- 在使用ar工具是時候需要添加參數(shù)rcs
- r更新、c創(chuàng)建、s建立索引
-
命令:ar rcs 靜態(tài)庫名 .o文件
ar rcs libtest1.a fun1.o fun2.o

- 靜態(tài)庫的使用
靜態(tài)庫制作完成之后, 需要將.a文件和頭文件一定發(fā)布給用戶.
假設(shè)測試文件為main.c, 靜態(tài)庫文件為libtest1.a, 頭文件為head.h
用到的參數(shù):- -L:指定要連接的庫的所在目錄
- -l:指定鏈接時需要的靜態(tài)庫, 去掉前綴和后綴
- -I: 指定main.c文件用到的頭文件head.h所在的路徑
gcc -o main1 main.c -L./ -ltest1 -I./


- 靜態(tài)庫的優(yōu)缺點
- 優(yōu)點:
- 函數(shù)庫最終被打包到應(yīng)用程序中,實現(xiàn)是函數(shù)本地化,尋址方便、速度快。
(庫函數(shù)調(diào)用效率==自定義函數(shù)使用效率) - 程序在運行時與函數(shù)庫再無瓜葛,移植方便。
- 函數(shù)庫最終被打包到應(yīng)用程序中,實現(xiàn)是函數(shù)本地化,尋址方便、速度快。
- 缺點:
- 消耗系統(tǒng)資源較大, 每個進程使用靜態(tài)庫都要復(fù)制一份, 無端浪費內(nèi)存。
- 優(yōu)點:

- 靜態(tài)庫會給程序的更新、部署和發(fā)布帶來麻煩。如果靜態(tài)庫libxxx.a更新了,所有使用它的應(yīng)用程序都需要重新編譯、發(fā)布給用戶(對于玩家來說,可能是一個很小的改動,卻導(dǎo)致整個程序重新下載)。
3.共享庫(shared library)/動態(tài)庫
??共享庫在程序編譯時并不會被連接到目標代碼中, 而是在程序運行是才被載入. 不同的應(yīng)用程序如果調(diào)用相同的庫, 那么在內(nèi)存里只需要有一份該共享庫的拷貝, 規(guī)避了空間浪費問題.動態(tài)庫在程序運行時才被載入, 也解決了靜態(tài)庫對程序的更新、部署和發(fā)布會帶來麻煩. 用戶只需要更新動態(tài)庫即可, 增量更新. 為什么需要動態(tài)庫, 其實也是靜態(tài)庫的特點導(dǎo)致.
- 按照習慣, 一般以”.so”做為文件后綴名.
- 共享庫的命名一般分為三個部分:
- 前綴:lib
- 庫名稱:自己定義即可, 如test
- 后綴:.so
所以最終的靜態(tài)庫的名字應(yīng)該為:libtest.so

- 共享庫的制作
- 生成目標文件.o, 此時要加編譯選項:-fPIC(fpic)
gcc -fpic -c fun1.c fun2.c
參數(shù):-fpic創(chuàng)建與地址無關(guān)的編譯程序(pic, position independent code), 目的就是為了能夠在多個應(yīng)用程序間共享. - 生成共享庫, 此時要加鏈接器選項: -shared(指定生成動態(tài)鏈接庫)
gcc -shared fun1.o fun2.o -o libtest2.so
- 共享庫的使用
引用動態(tài)庫編譯成可執(zhí)行文件(跟靜態(tài)庫方式一樣):
用到的參數(shù):- -L:指定要連接的庫的所在目錄
- -l:指定鏈接時需要的動態(tài)庫, 去掉前綴和后綴
-
-I: 指定main.c文件用到的頭文件head.h所在的路徑
gcc main.c -I./ -L./ -ltest2 -o main2
然后運行:./main2,發(fā)現(xiàn)竟然報錯了.

分析為什么在執(zhí)行的時候找不到libtest2.so庫
- 當系統(tǒng)加載可執(zhí)行代碼時候, 能夠知道其所依賴的庫的名字, 但是還需要知道所依賴的庫的絕對路徑。此時就需要系統(tǒng)動態(tài)載入器(dynamic linker/loader)。
- ldd命令可以查看可執(zhí)行文件依賴的庫文件, 執(zhí)行l(wèi)dd main2, 可以發(fā)現(xiàn)libtest2.so找不到.

-
對于elf格式的可執(zhí)行程序,是由ld-linux.so*來完成的, 它先后搜索elf文件的 DT_RPATH段 — 環(huán)境變量LD_LIBRARY_PATH — /etc/ld.so.cache文件列表 — /lib/, /usr/lib目錄找到庫文件后將其載入內(nèi)存。
使用file命令可以查看文件的類型: file main2

- 如何讓系統(tǒng)找到共享庫
拷貝自己制作的共享庫到/lib或者/usr/lib
-
臨時設(shè)置LD_LIBRARY_PATH:
- export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:庫路徑
-
永久設(shè)置, 把export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:庫路徑, 設(shè)置到~/.bashrc文件中, 然后在執(zhí)行下列三種辦法之一:
- 執(zhí)行. ~/.bashrc使配置文件生效(第一個.后面有一個空格)
- 執(zhí)行source ~/.bashrc配置文件生效
- 退出當前終端, 然后再次登陸也可以使配置文件生效
永久設(shè)置,把export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:庫路徑,設(shè)置到/etc/profile文件中
-
將其添加到 /etc/ld.so.cache文件中
- 編輯/etc/ld.so.conf文件, 加入庫文件所在目錄的路徑
- 運行sudo ldconfig -v, 該命令會重建/etc/ld.so.cache文件
解決了庫的路徑問題之后, 再次ldd命令可以查看可執(zhí)行文件依賴的庫文件, ldd main2:

- 共享庫的特點
- 動態(tài)庫把對一些庫函數(shù)的鏈接載入推遲到程序運行的時期。
- 可以實現(xiàn)進程之間的資源共享。(因此動態(tài)庫也稱為共享庫)
- 將一些程序升級變得簡單。
- 甚至可以真正做到鏈接載入完全由程序員在程序代碼中控制(顯示調(diào)用)
4.比較靜態(tài)庫和動態(tài)庫的優(yōu)缺點
靜態(tài)庫的優(yōu)點:
1 執(zhí)行速度快, 是因為靜態(tài)庫已經(jīng)編譯到可執(zhí)行文件內(nèi)部了
2 移植方便, 不依賴域其他的庫文件
缺點:
1 耗費內(nèi)存, 是由于每一個靜態(tài)庫的可執(zhí)行程序都會加載一次
2 部署更新麻煩, 因為靜態(tài)庫修改以后所有的調(diào)用到這個靜態(tài)庫的可執(zhí)行文
件都需要重新編譯
動態(tài)庫的優(yōu)點:
1 節(jié)省內(nèi)存
2 部署升級更新方便, 只需替換動態(tài)庫即可, 然后再重啟服務(wù).
缺點:
1 加載速度比靜態(tài)庫慢
2 移植性差, 需要把所有用到的動態(tài)庫都移植.
由于由靜態(tài)庫生成的可執(zhí)行文件是把靜態(tài)庫加載到了其內(nèi)部, 所以靜態(tài)庫生成的可執(zhí)行文件一般會比動態(tài)庫大.
