一、前言
通常情況下失尖,對(duì)函數(shù)庫(kù)的鏈接是放在編譯時(shí)期(compile time)完成的啊奄。所有相關(guān)的對(duì)象文件 (object file)與牽涉到的函數(shù)庫(kù)(library)被鏈接合成一個(gè)可執(zhí)行文件 (executable file)渐苏。程序 在運(yùn)行 時(shí),與函數(shù)庫(kù)再無(wú)瓜葛菇夸,因?yàn)樗行枰暮瘮?shù)已拷貝到自己門下琼富。所以這些函數(shù)庫(kù)被成為靜態(tài)庫(kù)(static libaray),通常文件 名為“l(fā)ibxxx.a”的形式庄新。其實(shí)鞠眉,我們也可以把對(duì)一些庫(kù)函數(shù)的鏈接載入推遲到程序運(yùn)行的時(shí)期(runtime)。這就是如雷貫耳的動(dòng)態(tài)鏈接庫(kù)(dynamic link library)技術(shù)择诈。
二械蹋、動(dòng)態(tài)鏈接
動(dòng)態(tài)鏈接方法:LoadLibrary()/GetProcessAddress()和FreeLibrary(),使用這種方式的程序并不在一開始就完成動(dòng)態(tài)鏈接羞芍,而是直到真正調(diào)用動(dòng)態(tài)庫(kù)代碼時(shí)哗戈,載入程序才計(jì)算(被調(diào)用的那部分)動(dòng)態(tài)代碼的邏輯地址,然后等到某個(gè)時(shí)候荷科,程序又需要調(diào)用另外某塊動(dòng)態(tài)代碼時(shí)唯咬,載入程序又去計(jì)算這部分代碼的邏輯地址,所以步做,這種方式使程序初始化時(shí)間較短副渴,但運(yùn)行期間的性能比不上靜態(tài)鏈接的程序。
三全度、靜態(tài)鏈接
靜態(tài)鏈接方法:#pragma comment(lib, "test.lib") 煮剧,靜態(tài)鏈接的時(shí)候,載入代碼就會(huì)把程序會(huì)用到的動(dòng)態(tài)代碼或動(dòng)態(tài)代碼的地址確定下來(lái)
靜態(tài)庫(kù)的鏈接可以使用靜態(tài)鏈接将鸵,動(dòng)態(tài)鏈接庫(kù)也可以使用這種方法鏈接導(dǎo)入庫(kù)
四勉盅、靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)的區(qū)別
在軟件開發(fā)的過(guò)程中,大家經(jīng)常會(huì)或多或少的使用別人編寫的或者系統(tǒng)提供的動(dòng)態(tài)庫(kù)或靜態(tài)庫(kù)顶掉,但是究竟是使用靜態(tài)庫(kù)還是動(dòng)態(tài)庫(kù)呢草娜?他們的適用條件是什么呢?
簡(jiǎn)單的說(shuō)痒筒,靜態(tài)庫(kù)和應(yīng)用程序編譯在一起宰闰,在任何情況下都能運(yùn)行,而動(dòng)態(tài)庫(kù)是動(dòng)態(tài)鏈接簿透,顧名思義就是在應(yīng)用程序啟動(dòng)的時(shí)候才會(huì)鏈接移袍,所以,當(dāng)用戶的系統(tǒng)上沒(méi)有該動(dòng)態(tài)庫(kù)時(shí)老充,應(yīng)用程序就會(huì)運(yùn)行失敗葡盗。再看它們的特點(diǎn):
動(dòng)態(tài)庫(kù):
- 類庫(kù)的名字一般是 libxxx.so
- 共享:多個(gè)應(yīng)用程序可以使用同一個(gè)動(dòng)態(tài)庫(kù),啟動(dòng)多個(gè)應(yīng)用程序的時(shí)候啡浊,只需要將動(dòng)態(tài)庫(kù)加載到內(nèi)存一次即可觅够;
- 開發(fā)模塊好:要求設(shè)計(jì)者對(duì)功能劃分的比較好胶背。
- 動(dòng)態(tài)函數(shù)庫(kù)的改變并不影響你的程序,所以動(dòng)態(tài)函數(shù)庫(kù)的升級(jí)比較方便喘先。
靜態(tài)庫(kù):
- 類庫(kù)的名字一般是libxxx.a
- 代碼的裝載速度快钳吟,執(zhí)行速度也比較快,因?yàn)榫幾g時(shí)它只會(huì)把你需要的那部分鏈接進(jìn)去苹祟。
- 應(yīng)用程序相對(duì)比較大砸抛,如果多個(gè)應(yīng)用程序使用的話,會(huì)被裝載多次树枫,浪費(fèi)內(nèi)存直焙。
- 如果靜態(tài)函數(shù)庫(kù)改變了,那么你的程序必須重新編譯砂轻。
如果你的系統(tǒng)上有多個(gè)應(yīng)用程序都使用該庫(kù)的話奔誓,就把它編譯成動(dòng)態(tài)庫(kù),這樣雖然剛啟動(dòng)的時(shí)候加載比較慢搔涝,但是多任務(wù)的時(shí)候會(huì)比較節(jié)省內(nèi)存厨喂;如果你的系統(tǒng)上只有一到兩個(gè)應(yīng)用使用該庫(kù),并且使用的API比較少的話庄呈,就編譯成靜態(tài)庫(kù)吧蜕煌,一般的靜態(tài)庫(kù)還可以進(jìn)行裁剪編譯,這樣應(yīng)用程序可能會(huì)比較大诬留,但是啟動(dòng)的速度會(huì)大大提高斜纪。
五、例子詳解
文件目錄樹如下:
- libtest/
- |-- myjob.c
- |-- myjob.h
- |-- test.c
靜態(tài)庫(kù)
A.做成靜態(tài)庫(kù) libmyjob.a
$ gcc -c myjob.c -o myjob.o
$ ar -c -r -s libmyjob.a myjob.o
B.鏈接
$ gcc test.o libmyjob.a -o test
C.引用庫(kù)情況(無(wú)所要信息)
$ ldd test
linux-gate.so.1 => (0xffffe000)
libc.so.6 => /lib/libc.so.6 (0xb7e29000)
/lib/ld-linux.so.2 (0xb7f6e000)
動(dòng)態(tài)庫(kù)
A.做成動(dòng)態(tài)庫(kù)libmyjob.so
$ gcc -Wall –fPIC -c myjob.c -o myjob.o
$ gcc -shared -o libmyjob.so myjob.o
- -shared: 該選項(xiàng)指定生成動(dòng)態(tài)連接庫(kù)(讓連接器生成T >類型的導(dǎo)出符號(hào)表文兑,有時(shí)候也生成弱連接 W >類型的導(dǎo)出符號(hào))盒刚,不用該標(biāo)志外部程序無(wú)法連接。相當(dāng)于一個(gè)可執(zhí)行文件绿贞。
- -fPIC: 表示編譯為位置獨(dú)立的代碼因块,不用此選項(xiàng)的話編譯后的代碼是位置相關(guān)的所以動(dòng)>>態(tài)載入時(shí)是通過(guò)代碼拷貝的方式來(lái)滿足不同進(jìn)程的需要,而不能達(dá)到真正代碼段共享的目的>籍铁。
- -L.: 表示要連接的庫(kù)在當(dāng)前目錄中涡上。
LD_LIBRARY_PATH: 這個(gè)環(huán)境變量指示動(dòng)態(tài)連接器可以裝載動(dòng)態(tài)庫(kù)的路徑。
B.鏈接
鏈接方法一拒名、拷貝到系統(tǒng)庫(kù)里再鏈接吓懈,讓 gcc 自己查找:
$ cp libmyjob.so /usr/lib
$ gcc -o test test.o -lmyjob
這里我們可以看到了 -lmyjob 選項(xiàng), -l[lib_name] 指定庫(kù)名靡狞,他會(huì)主動(dòng)搜索。 lib[lib_name].so 這個(gè)搜索的路徑可以通過(guò) gcc --print-search-dirs 來(lái)查找隔嫡。
鏈接方法二 甸怕,手動(dòng)指定庫(kù)路徑
$ gcc -o test test.o -lmyjob -B /path/to/lib
-B 選項(xiàng)就添加 /path/to/lib 到 gcc 搜索的路徑之中甘穿。這樣鏈接沒(méi)有問(wèn)題但是方法 II 中手動(dòng)鏈接好的程序在 執(zhí)行 時(shí)候仍舊需要指定庫(kù)路徑( 鏈接和執(zhí)行是分開的 )。需要添加系統(tǒng)變量 LD_LIBRARY_PATH :
1. $ export LD_LIBRARY_PATH=/path/to/lib
這個(gè)時(shí)候再來(lái)檢測(cè)一下test 程序的庫(kù)鏈接狀況 ( 方法 I 情況 )
1. $ ldd test
2. linux-gate.so.1 => (0xffffe000)
3. libmyjob.so => /usr/lib/ libmyjob .so (0xb7f58000)
4. libc.so.6 => /lib/libc.so.6 (0xb7e28000)
5. /lib/ld-linux.so.2 (0xb7f6f000)
是不是比靜態(tài)鏈接的程序多了一個(gè) libmyjob.so? 這就是靜態(tài)與動(dòng)態(tài)的最大區(qū)別梢杭,靜態(tài)情況下温兼,它把庫(kù)直接加載到程序里,而在動(dòng)態(tài)鏈接的時(shí)候武契,它只是保留接口募判,將動(dòng)態(tài)庫(kù)與程序代碼獨(dú)立。這樣就可以提高代碼的可復(fù)用度咒唆,和降低程序的耦合度届垫。
另外,運(yùn)行時(shí)全释,要保證主程序能找到動(dòng)態(tài)庫(kù)装处,所以動(dòng)態(tài)庫(kù)一般發(fā)布到系統(tǒng)目錄中,要么就在跟主程序相對(duì)很固定的路徑里浸船,這樣不管主程序在本機(jī)何時(shí)何地跑妄迁,都能找得到動(dòng)態(tài)庫(kù)。而靜態(tài)庫(kù)只作用于鏈接時(shí)李命,運(yùn)行主程序時(shí)靜態(tài)庫(kù)文件沒(méi)存在意義了登淘。