Linux下怎么用GCC編譯動態(tài)庫

這篇文章主要講解了“Linux下怎么用GCC編譯動態(tài)庫”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“Linux下怎么用GCC編譯動態(tài)庫”吧!

作為一家“創(chuàng)意+整合+營銷”的成都網(wǎng)站建設(shè)機構(gòu),我們在業(yè)內(nèi)良好的客戶口碑。成都創(chuàng)新互聯(lián)公司提供從前期的網(wǎng)站品牌分析策劃、網(wǎng)站設(shè)計、網(wǎng)站設(shè)計制作、網(wǎng)站建設(shè)、創(chuàng)意表現(xiàn)、網(wǎng)頁制作、系統(tǒng)開發(fā)以及后續(xù)網(wǎng)站營銷運營等一系列服務(wù),幫助企業(yè)打造創(chuàng)新的互聯(lián)網(wǎng)品牌經(jīng)營模式與有效的網(wǎng)絡(luò)營銷方法,創(chuàng)造更大的價值。

本文主要解決以下幾個問題
1 為什么要使用庫?
2 庫的分類
3 創(chuàng)建自己的庫
或許大家對自己初學(xué) Linux時的情形仍記憶尤新吧。如果沒有一個能較好的解決依賴關(guān)系的包管理器,在Linux下安裝軟件將是一件及其痛苦的工作。你裝a包時,可能會提示你要先裝b包,當(dāng)你費盡心力找到b包時,可能又會提示你要先安裝c包。我就曾被這樣的事搞的焦頭爛額,至今一提起rpm仍心有余悸,頭皮發(fā)麻。說是一朝被蛇咬,十年怕井繩怕也不為過。
Linux下之所以有這許多的依賴關(guān)系,其中一個開發(fā)原則真是功不可沒。這個原則就是:盡量不重復(fù)做別人已經(jīng)做過的事。換句話說就是盡量充分利用別人的勞動成果。
這就涉及到如何有效的進行代碼復(fù)用。
1 為什么要使用庫?
關(guān)于代碼復(fù)用的途徑,一般有兩種。
粘貼復(fù)制
這是最沒有技術(shù)含量的一種方案。如果代碼小,則工作量還可以忍受,如果代碼很龐大,則此法不可取。即便有人原意這樣做,但誰又能保證所有的代碼都可得到呢?
而庫的出現(xiàn)很好的解決了這個問題。
庫,是一種封裝機制,簡單說把所有的源代碼編譯成目標(biāo)代碼后打成的包。
那么用戶怎么能知道這個庫提供什么樣的接口呢?難道要用nm等工具逐個掃描?
不用擔(dān)心,庫的開發(fā)者早以把一切都做好了。除了包含目標(biāo)代碼的庫外,www.Linuxidc.com一般還會提供一系列的頭文件,頭文件中就包含了庫的接口。為了讓方便用戶,再加上一個使用說明就差不多完美了。
2 庫的分類
2.1 庫的分類
根據(jù)鏈接時期的不同,庫又有靜態(tài)庫和動態(tài)庫之分。
靜態(tài)庫是在鏈接階段被鏈接的(好像是廢話,但事實就是這樣),所以生成的可執(zhí)行文件就不受庫的影響了,即使庫被刪除了,程序依然可以成功運行。
有別于靜態(tài)庫,動態(tài)庫的鏈接是在程序執(zhí)行的時候被鏈接的。所以,即使程序編譯完,庫仍須保留在系統(tǒng)上,以供程序運行時調(diào)用。(TODO:鏈接動態(tài)庫時鏈接階段到底做了什么)
2.2 靜態(tài)庫和動態(tài)庫的比較
鏈接靜態(tài)庫其實從某種意義上來說也是一種粘貼復(fù)制,只不過它操作的對象是目標(biāo)代碼而不是源碼而已。因為靜態(tài)庫被鏈接后庫就直接嵌入可執(zhí)行文件中了,這樣就帶來了兩個問題。
首先就是系統(tǒng)空間被浪費了。這是顯而易見的,想象一下,如果多個程序鏈接了同一個庫,則每一個生成的可執(zhí)行文件就都會有一個庫的副本,必然會浪費系統(tǒng)空間。
再者,人非圣賢,即使是精心調(diào)試的庫,也難免會有錯。一旦發(fā)現(xiàn)了庫中有bug,挽救起來就比較麻煩了。必須一一把鏈接該庫的程序找出來,然后重新編譯。
而動態(tài)庫的出現(xiàn)正彌補了靜態(tài)庫的以上弊端。因為動態(tài)庫是在程序運行時被鏈接的,所以磁盤上只須保留一份副本,因此節(jié)約了磁盤空間。如果發(fā)現(xiàn)了bug或要升級也很簡單,只要用新的庫把原來的替換掉就行了。
那么,是不是靜態(tài)庫就一無是處了呢?
答曰:非也非也。不是有句話么:存在即是合理。靜態(tài)庫既然沒有湮沒在滔滔的歷史長河中,就必然有它的用武之地。想象一下這樣的情況:如果你用libpcap庫編了一個程序,要給被人運行,而他的系統(tǒng)上沒有裝pcap庫,該怎么解決呢?最簡單的辦法就是編譯該程序時把所有要鏈接的庫都鏈接它們的靜態(tài)庫,這樣,就可以在別人的系統(tǒng)上直接運行該程序了。
所謂有得必有失,正因為動態(tài)庫在程序運行時被鏈接,故程序的運行速度和鏈接靜態(tài)庫的版本相比必然會打折扣。然而瑕不掩瑜,動態(tài)庫的不足相對于它帶來的好處在現(xiàn)今硬件下簡直是微不足道的,所以鏈接程序在鏈接時一般是優(yōu)先鏈接動態(tài)庫的,除非用-static參數(shù)指定鏈接靜態(tài)庫。
2.3 如何判斷一個程序有沒有鏈接動態(tài)庫?
答案是用file實用程序。
file程序是用來判斷文件類型的,在file命令下,所有文件都會原形畢露的。
順便說一個技巧。有時在 windows下用瀏覽器下載tar.gz或tar.bz2文件,后綴名會變成奇怪的tar.tar,到Linux有些新手就不知怎么解壓了。但 Linux下的文件類型并不受文件后綴名的影響,所以我們可以先用命令file xxx.tar.tar看一下文件類型,然后用tar加適當(dāng)?shù)膮?shù)解壓。
另外,還可以借助程序ldd實用程序來判斷。
ldd是用來打印目標(biāo)程序(由命令行參數(shù)指定)所鏈接的所有動態(tài)庫的信息的,如果目標(biāo)程序沒有鏈接動態(tài)庫,則打印“not a dynamic executable”,ldd的用法請參考manpage。
3 創(chuàng)建自己的庫
3.1 創(chuàng)建動態(tài)庫
創(chuàng)建文件hello.c,內(nèi)容如下:
#include
void hello(void)
{
printf("Hello World\n");
}
用命令gcc -shared hello.c -o libhello.so編譯為動態(tài)庫??梢钥吹?,當(dāng)前目錄下多了一個文件libhello.so。
[leo@leo test]$ file libhello.so
libhello.so: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), not stripped
看到了吧,文件類型是shared object了。
再編輯一個測試文件test.c,內(nèi)容如下:
int
main()
{
hello();
return 0;
}
這下可以編譯了:)
[leo@leo test]$ gcc test.c
/tmp/ccm7w6Mn.o: In function `main':
test.c:(.text+0x1d): undefined reference to `hello'
collect2: ld returned 1 exit status
鏈接時gcc找不到hello函數(shù),編譯失敗:(。原因是hello在我們自己創(chuàng)建的庫中,如果gcc能找到那才教見鬼呢!ok,再接再厲。
[leo@leo test]$ gcc test.c -lhello
/usr/lib/gcc/i686-pc-Linux-gnu/4.0.0/../../../../i686-pc-Linux-gnu/bin/ld: cannot find -lhello
collect2: ld returned 1 exit status
[leo@leo test]$ gcc test.c -lhello -L.
[leo@leo test]$
第一次編譯直接編譯,gcc默認(rèn)會鏈接標(biāo)準(zhǔn)c庫,但符號名hello解析不出來,故連接階段通不過了。
現(xiàn)在用gcc test.c -lhello -L.已經(jīng)編譯成功了,默認(rèn)輸出為a.out?,F(xiàn)在來試著運行一下:
[leo@leo test]$ ./a.out
./a.out: error while loading shared libraries: libhello.so: cannot open shared object file: No such file or directory
咦,怎么回事?原來雖然鏈接時鏈接器(dynamic linker)找到了動態(tài)庫libhello.so,但動態(tài)加載器(dynamic loader, 一般是/lib/ld-Linux.so.2)卻沒找到。再來看看ldd的輸出:
[leo@leo test]$ ldd a.out
Linux-gate.so.1 => (0xffffe000)
libhello.so => not found
libc.so.6 => /lib/libc.so.6 (0x40034000)
/lib/ld-Linux.so.2 (0x40000000)
果然如此,看到?jīng)]有,libhello.so => not found。
Linux為我們提供了兩種解決方法:
1.可以把當(dāng)前路徑加入 /etc/ld.so.conf中然后運行l(wèi)dconfig,或者以當(dāng)前路徑為參數(shù)運行l(wèi)dconfig(要有root權(quán)限才行)。
2.把當(dāng)前路徑加入環(huán)境變量LD_LIBRARY_PATH中
當(dāng)然,如果你覺得不會引起混亂的話,可以直接把該庫拷入/lib,/usr/lib/等位置(無可避免,這樣做也要有權(quán)限),這樣鏈接器和加載器就都可以準(zhǔn)確的找到該庫了。
我們采用第二種方法:
[leo@leo test]$ export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
[leo@leo test]$ ldd a.out
Linux-gate.so.1 => (0xffffe000)
libhello.so => ./libhello.so (0x4001f000)
libc.so.6 => /lib/libc.so.6 (0x40036000)
/lib/ld-Linux.so.2 (0x40000000)
哈哈,這下ld-Linux.so.2就可以找到libhello.so這個庫了。
現(xiàn)在可以直接運行了:
[leo@leo test]$ ./a.out
Hello World
3.2 創(chuàng)建靜態(tài)庫
仍使用剛才的hello.c和test.c。
第一步,生成目標(biāo)文件。
[leo@leo test]$ gcc -c hello.c
[leo@leo test]$ ls hello.o -l
-rw-r--r-- 1 leo users 840 5月 6 12:48 hello.o
第二步,把目標(biāo)文件歸檔。
[leo@leo test]$ ar r libhello.a hello.o
ar: creating libhello.a
OK,libhello.a就是我們所創(chuàng)建的靜態(tài)庫了,簡單吧:)
[leo@leo test]$ file libhello.a
libhello.a: current ar archive
下面一行命令就是教你如何在程序中鏈接靜態(tài)庫的:
[leo@leo test]$ gcc test.c -lhello -L. -static -o hello.static
我們來用file命令比較一下用動態(tài)庫和靜態(tài)庫鏈接的程序的區(qū)別:
[leo@leo test]$ gcc test.c -lhello -L. -o hello.dynamic
正如前面所說,鏈接器默認(rèn)會鏈接動態(tài)庫(這里是libhello.so),所以只要把上個命令中的 -static參數(shù)去掉就可以了。
用file實用程序驗證一下是否按我們的要求生成了可執(zhí)行文件:
[leo@leo test]$ file hello.static hello.dynamic
hello.static: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.6.6, statically linked, not stripped
hello.dynamic: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.6.6, dynamically linked (uses shared libs), not stripped
不妨順便練習(xí)一下ldd的用法:
[leo@leo test]$ ldd hello.static hello.dynamic
hello.static:
not a dynamic executable
hello.dynamic:
Linux-gate.so.1 => (0xffffe000)
libhello.so => ./libhello.so (0x4001f000)
libc.so.6 => /lib/libc.so.6 (0x40034000)
/lib/ld-Linux.so.2 (0x40000000)
OK,看來沒有問題,那就比較一下大小先:
[leo@leo test]$ ls -l hello.[ds]*
-rwxr-xr-x 1 leo users 5911 5月 6 12:54 hello.dynamic
-rwxr-xr-x 1 leo users 628182 5月 6 12:54 hello.static
看到區(qū)別了吧,鏈接靜態(tài)庫的目標(biāo)程序和鏈接動態(tài)庫的程序比起來簡直就是一個龐然大物!
這么小的程序,很難看出執(zhí)行時間的差別,不過為了完整起見,還是看一下time的輸出吧:
[leo@leo test]$ time ./hello.static
Hello World
real 0m0.001s
user 0m0.000s
sys 0m0.001s
[leo@leo test]$ time ./hello.dynamic
Hello World
real 0m0.001s
user 0m0.000s
sys 0m0.001s
如果程序比較大的話,應(yīng)該效果會很明顯的。

感謝各位的閱讀,以上就是“Linux下怎么用GCC編譯動態(tài)庫”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對Linux下怎么用GCC編譯動態(tài)庫這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!

當(dāng)前文章:Linux下怎么用GCC編譯動態(tài)庫
網(wǎng)站網(wǎng)址:http://muchs.cn/article28/phodcp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供全網(wǎng)營銷推廣、網(wǎng)站制作、品牌網(wǎng)站制作、Google、App設(shè)計網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

外貿(mào)網(wǎng)站制作