柚子快報激活碼778899分享:【Linux】靜態(tài)庫和動態(tài)庫
柚子快報激活碼778899分享:【Linux】靜態(tài)庫和動態(tài)庫
> 作者:?舊言~ > 座右銘:松樹千年終是朽,槿花一日自為榮。
> 目標(biāo):理解靜動態(tài)庫,自己能模擬實(shí)現(xiàn)動靜態(tài)庫。
> 毒雞湯:有些事情,總是不明白,所以我不會堅持。早安!
> 專欄選自:Linux初階
> 望小伙伴們點(diǎn)贊?收藏?加關(guān)注喲??
?
?前言
庫是寫好的現(xiàn)有的,成熟的,可以復(fù)用的代碼?,F(xiàn)實(shí)中每個程序都要依賴很多基礎(chǔ)的底層庫,不可能每個人的代碼都從零開始,因此庫的存在意義非同尋常。本質(zhì)上來說庫是一種可執(zhí)行代碼的二進(jìn)制形式,可以被操作系統(tǒng)載入內(nèi)存執(zhí)行。庫有兩種:
靜態(tài)庫(.a、.lib)動態(tài)庫(.so、.dll)
今天我們來談?wù)? Linux? 下靜態(tài)庫和動態(tài)庫。
?主體
學(xué)習(xí)【Linux】靜態(tài)庫和動態(tài)庫咱們按照下面的圖解:
?
? 認(rèn)識動靜態(tài)庫
Linux的庫一般分為動態(tài)庫和靜態(tài)庫:
靜態(tài)庫(.a):庫文件以.a為后綴,程序在編譯鏈接的時候把庫的代碼鏈接到可執(zhí)行文件中。程序運(yùn)行的時候?qū)⒉辉傩枰o態(tài)庫動態(tài)庫(.so):庫文件以.so為后綴,程序在運(yùn)行的時候才去鏈接動態(tài)庫的代碼,多個程序共享使用庫的代碼。
在Xshell下安裝靜態(tài)庫:
yum install -y glibc-static
yum install -y libstdc++-static
我們可以通過 [?ldd 可執(zhí)行程序文件名?]來查看可執(zhí)行程序所依賴的庫:
?
其中?/lib64/libc.so.6?就是可執(zhí)行程序的庫文件,它其實(shí)是一個軟鏈接。我們可以通過以下命令來查看
ls /lib64/libc.so.6 -l
?
總結(jié):
如何辨別它采用的是哪一種庫呢?
1.我們可以通過后綴來區(qū)分(在Linux中)
在Linux中,以 .so 結(jié)尾的后綴,是動態(tài)庫;以 .a 結(jié)尾的是靜態(tài)庫 ?在Windows中,以 .dll 結(jié)尾的后綴,是動態(tài)庫;以 .lib 結(jié)尾的是靜態(tài)庫 ?
注:
庫文件的名字:libxxx.so 和 libxxx.a庫的真實(shí)名字:去掉lib前綴,去掉 .a 、.so后綴,剩下的就是庫的名稱。
2.我們還可以通過file命令查看
?
? ? ? ? 上圖中分別使用了動靜態(tài)庫對同一個文件進(jìn)行編譯的,通過file命名可以查看到所對應(yīng)的鏈接信息
? 理解什么是庫
概念:
如果不想給對方我們的源代碼,我們可以選擇給用戶提供我們的.o可重定位目標(biāo)二進(jìn)制文件(gcc -c 文件)與.h頭文件。讓用戶用我們提供的.o文件進(jìn)行鏈接即可。在編譯時,只要把源文件編譯成.o文件在將其鏈接便可形成一個可執(zhí)行的程序。
圖解:
?
舉個栗子:
案例:
?
說明:
難道就這么簡單嗎?我們可以給對方提供.o(方法的實(shí)現(xiàn)),同時還有提供.h(里面包含著都有哪些方法),此時對方是能用的。但是如果存在很多.c文件呢?難道我們要把幾千個.c文件全部編譯成.o在加上頭文件全部一個一個提供嗎?那樣太過于麻煩,為了讓用戶更好的使用庫,我們就有把所有的.o文件打成一個包,給對方提供一個庫文件即可!把多個.o合并成一個文件,這個文件就是庫,把包方式的不同就分為了動態(tài)庫和靜態(tài)庫*。
? 庫的制作和使用
概念:
庫是一個二進(jìn)制文件,想要使用庫(給別人使用自己的制作的庫或者使用別人的庫)一定是由三個部分組成:庫文件、頭文件、文檔說明;一般這個庫文件就是函數(shù)的定義,頭文件就是函數(shù)聲明,我們只需要將這些打包好,別人使用我們頭文件所給的接口就行。
編寫如下四個文件:用來制作靜態(tài)庫并打包
源文件包含add.c和sub.c頭文件包含add.h和sub.h
?
??靜態(tài)庫的制作
1.生成二進(jìn)制(.o)文件
?
2.打包
我們將生成好的.o文件進(jìn)行打包:
ar -rc libmymath.a add.o sub.o
ar命令是gnu的歸檔工具,常用于將目標(biāo)文件打包為靜態(tài)庫,下面我們使用ar命令的-r 選項(xiàng)和-c選項(xiàng)進(jìn)行打包。
-r(replace):若靜態(tài)庫文件當(dāng)中的目標(biāo)文件有更新,則用新的目標(biāo)文件替換舊的目標(biāo)文件。-r(rreate):建立靜態(tài)庫文件。
?
我們可以用?ar?命令的?-t?選項(xiàng)和?-v?選項(xiàng)查看靜態(tài)庫當(dāng)中的文件。
-t:列出靜態(tài)庫中的文件。-v:顯示詳細(xì)的信息
?
3.發(fā)布靜態(tài)庫
靜態(tài)庫要發(fā)布出去供別人使用,只要庫文件(所有的.o文件)是不夠的,我們需要將其和頭文件一起發(fā)布出去,別人只要看到頭文件,就大致了解如何使用了
?
??靜態(tài)庫的使用
問題拋出?
上文中,我們已經(jīng)有了靜態(tài)庫output了,別人該如何使用呢? 例如:想要在friend文件下使用這個庫:
方法一 :
?
現(xiàn)在在friend目錄下有一個mytest.c文件和一個靜態(tài)庫文件lib,mytest.c想要使用lib,我們先編寫一下mytest.c代碼:
#include "add.h"
#include "sub.h"
int main()
{
int x = 30;
int y = 20;
int ret1 = my_add(x, y);
int ret2 = my_sub(x, y);
printf("ret1 = %d\n",ret1);
printf("ret2 = %d\n",ret2);
return 0;
}
編譯:
?
說明:
編譯后,報警告:沒有頭文件,可是明明在lib文件下有我們要的有文件以及庫文件,這是為什么呢?
?其實(shí),編譯器在編譯的時候,會在當(dāng)前的目錄的文件中去找,不會去當(dāng)前目錄的文件夾中去找,lib目錄下的頭文件以及庫文件,和mytest.c不是同級目錄,所以編譯會出錯;我們在編譯的時候就需要告訴編譯器,需要的頭文件在哪個目錄下。
gcc mytest.c -I ./lib
?
此時,又有警告了:鏈接錯誤,未定義的兩個函數(shù)?在lib目錄下已經(jīng)定義了兩個函數(shù),并且打包好了?為什么還是報錯呢?
其原因和上面一樣;所以我們還需要告訴編譯器庫文件在lib目錄下:
gcc mytest.c -I ./lib -L ./lib
?
頭文件和庫文件所在位置都告訴編譯器了,怎么還是報錯呢?
其實(shí),頭文件和庫文件都在lib目錄下,在mytest.c文件中,是明確的包含了,add.h和sub.h的,gcc在編譯的時候能夠認(rèn)識,但不認(rèn)識庫文件,如果在lib目錄下有多個庫文件,gcc是不知道你想要使用哪個庫的。所以我們還需要指明庫的名字。
gcc mytest.c -I ./lib -L ./lib -l mymath
?
方法二:
對比我們之前在編譯某個.c文件時,為什么有加上這些選項(xiàng)呢?。這是因?yàn)橹暗膸於际窃谙到y(tǒng)的默認(rèn)路徑下,所以我們可以將我們做好的靜態(tài)庫拷貝到系統(tǒng)的默認(rèn)路徑下,也是可以達(dá)到不需要加這些選項(xiàng)的效果;但是嚴(yán)重不推薦。
總結(jié):
我們在使用靜態(tài)庫進(jìn)行編譯鏈接時,需要指定頭文件的所在路徑,庫文件的所在路徑以及所要掉用的庫名稱
-I:指定頭文件所在路徑。-L:指定庫文件所在路徑。-l:指明需要鏈接庫文件路徑下的哪一個庫
?
? 動態(tài)庫的制作
編寫如下四個文件:其中源文件包含add.c和sub.c,頭文件包含add.h和sub.h;用來制作動態(tài)庫并打包
?
1.生成二進(jìn)制(.o)文件
首先將上面的.c文件生成.o文件?
gcc -fPIC -c add.c -o add.o
gcc -fPIC -c sub.c -o sub.o
-fPIC:作用是告知編譯器 生成位置無關(guān)代碼(編譯產(chǎn)生的代碼沒有絕對位置,只有相對位置);從而可以在任意地方調(diào)用生成的動態(tài)庫。
?
2.打包
?我們將生成好的.o文件進(jìn)行打包:
gcc -shared -o libmymath.so add.o sub.o
-shared:linux在gcc編譯時加上 -shared 參數(shù)時,目的是使源碼編譯成動態(tài)庫 .so 文件;
?
3.發(fā)布動態(tài)庫
將庫文件和所有的頭文件組織起來,放到lib目錄下,這樣就可以發(fā)布動態(tài)庫了
?
? 動態(tài)庫的使用
動態(tài)庫的使用大致和靜態(tài)庫類似,但略有區(qū)別。我們先使用靜態(tài)庫的方法來實(shí)現(xiàn)動態(tài)庫的鏈接。
?
方法一:
?拷貝到系統(tǒng)的默認(rèn)路徑下,一般指/usr/lib?這里不做演示,嚴(yán)重不推薦;
方法二 :
更改 ?LD_LIBRARY_PATH
export LD_LIBRARY_PATH=/home/lyk/lesson8/friend/lib
?LD_LIBRARY_PATH環(huán)境變量用于在程序加載運(yùn)行期間查找動態(tài)鏈接庫時指定除了系統(tǒng)默認(rèn)路徑之外的其他路徑.注意,LD_LIBRARY_PATH中指定的路徑會在系統(tǒng)默認(rèn)路徑之前進(jìn)行查找;
添加好后,我們再次查看,發(fā)現(xiàn)路徑已經(jīng)指定好了 :
再次編譯運(yùn)行:?
? 動靜態(tài)庫的加載
靜態(tài)庫不需要加載,靜態(tài)庫把代碼拷貝到可執(zhí)行程序里,直接決定了當(dāng)加載的時候在內(nèi)存里代碼和數(shù)據(jù)可能存在多份,會比較浪費(fèi)空間,把靜態(tài)庫中拷貝到程序中的代碼區(qū)里:
動態(tài)庫加上fPIC形成位置無關(guān)碼,采用相對編址方案,在程序鏈接時對應(yīng)庫當(dāng)中的偏移量添加到可執(zhí)行程序,運(yùn)行時一旦庫加載進(jìn)來,經(jīng)過地址空間映射,把庫映射到地址空間之后,庫也就具備了起始地址,通過偏移地址和起始地址這樣就可以找到訪問的函數(shù):
系統(tǒng)層面上會維護(hù)動態(tài)庫的起始地址,直接建立頁表與內(nèi)存的映射,也就可以跳轉(zhuǎn)訪問了,所以動態(tài)庫加載一次就可以被多個進(jìn)程共同使用了。而靜態(tài)庫可能有多個程序用了C庫,加載到內(nèi)存時,內(nèi)存里可能會存在100份重復(fù)的代碼。而動態(tài)鏈接不會出現(xiàn)重復(fù)的代碼,減少內(nèi)存。
? 動靜態(tài)庫總結(jié)
靜態(tài)庫的特點(diǎn):
靜態(tài)庫在可執(zhí)行程序鏈接時就加入到可執(zhí)行代碼中,在物理上成為可執(zhí)行程序的一部分;程序運(yùn)行時將不再需要該靜態(tài)庫。相對于動態(tài)庫鏈接生成的程序,靜態(tài)函相當(dāng)于編譯器將代碼補(bǔ)充完整了,因此執(zhí)行程序會大一些,但是運(yùn)行起來相對快些;靜態(tài)庫是犧牲了空間效率,換取了時間效率;
動態(tài)庫的特點(diǎn):
動態(tài)庫在程序編譯時并不會被連接到目標(biāo)代碼中,而是在程序運(yùn)行是才被載入,因此在程序運(yùn)行時還需要動態(tài)庫存在;動態(tài)庫只有在程序執(zhí)行時, 那些需要的函數(shù)代碼才被拷貝到內(nèi)存中。這樣就使可執(zhí)行文件比較小, 節(jié)省磁盤空間;由于運(yùn)行時要去鏈接庫會花費(fèi)一定的時間,執(zhí)行速度相對會慢一些;動態(tài)庫是犧牲了時間效率,換取了空間效率;
??結(jié)束語?
? ? ? ?今天內(nèi)容就到這里啦,時間過得很快,大家沉下心來好好學(xué)習(xí),會有一定的收獲的,大家多多堅持,嘻嘻,成功路上注定孤獨(dú),因?yàn)閳猿值娜瞬欢?。那請大家舉起自己的小手給博主一鍵三連,有你們的支持是我最大的動力???,回見。
??
柚子快報激活碼778899分享:【Linux】靜態(tài)庫和動態(tài)庫
文章來源
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。