Linux下動態庫和靜態庫的製作及使用

在實際的開發過程中,編寫程序往往都需要依賴很多基礎的底層庫,比方說平時用的較多的標準C庫,數學庫等等;我們會頻繁的使用這些庫里的函數,這些函數大多數都是前人為我們寫好的,所以值得慶幸的是我們的工作不必從零開始,我們要做的只是在恰當的位置調用合適的庫函數去實現相應的功能,充分利用前人的勞動成果,就是「站在巨人的肩膀上」。本文主要簡述Linux下庫的製作以及使用方法。

一、什麼是庫

庫從本質上來說是一種可執行代碼的二進位格式,可以被載入內存中執行。根據鏈接時期的不同,庫又有:靜態庫和共享庫(動態庫)二者的不同點在於代碼被載入的時刻不同。

靜態庫的代碼在編譯過程中已經被載入可執行程序,因此體積較大。

共享庫的代碼是在可執行程序運行時才載入內存的,在編譯過程中僅簡單的引用,因此代碼體積較小。

Advertisements

二、初識靜態庫與動態庫

1.靜態函數庫

這類庫的名字一般是libxxx.a,xxx為庫的名字。利用靜態函數庫編譯成的文件比較大,因為整個函數庫的所有數據都會被整合進目標代碼中,他的優點就顯而易見了,即編譯后的執行程序不需要外部的函數庫支持,因為所有使用的函數都已經被編譯進去了。當然這也會成為他的缺點,因為如果靜態函數庫改變了,那麼你的程序必須重新編譯。

2.動態函數庫

這類庫的名字一般是libxxx.M.N.so,同樣的xxx為庫的名字,M是庫的主版本號,N是庫的副版本號。當然也可以不要版本號,但名字必須有。相對於靜態函數庫,動態函數庫在編譯的時候並沒有被編譯進目標代碼中,你的程序執行到相關函數時才調用該函數庫里的相應函數,因此動態函數庫所產生的可執行文件比較小。由於函數庫沒有被整合進你的程序,而是程序運行時動態的申請並調用,所以程序的運行環境中必須提供相應的庫。動態函數庫的改變並不影響你的程序,所以動態函數庫的升級比較方便。linux系統有幾個重要的目錄存放相應的函數庫,如/lib /usr/lib。

Advertisements

三、靜態庫與動態庫的比較

靜態庫其實從某種意義上來說只不過它操作的對象是目標代碼而不是源碼而已。因為靜態庫被鏈接后庫就直接嵌入可執行文件中了,這樣就帶來了兩個問題。

(1)首先就是系統空間被浪費了。這是顯而易見的,想象一下,如果多個程序鏈接了同一個庫,則每一個生成的可執行文件就都會有一個庫的副本,必然會浪費系統空間。

(2)再者,一旦發現了庫中有bug,挽救起來就比較麻煩了。必須一一把鏈接該庫的程序找出來,然後重新編譯。

而動態庫的出現正彌補了靜態庫的以上弊端。因為動態庫是在程序運行時被鏈接的,所以磁碟上只須保留一份副本,因此節約了磁碟空間。如果發現了bug或要升級也很簡單,只要用新的庫把原來的替換掉就行了。

但是靜態庫也有自己的優點:編譯后的執行程序不需要外部的函數庫支持,因為所有使用的函數都已經被編譯進去了。

四、如何判斷一個程序有沒有鏈接動態庫

(1)file命令

file程序是用來判斷文件類型的,啥文件一看都清楚明了。

(2)ldd命令

看動態庫,如果目標程序沒有鏈接動態庫,則列印「not a dynamic executable」 (不是動態可執行文件)

五、靜態庫的製作

(1) 為pr1和pr2生成object文件

gcc -O -c pr1.c pr2.c

(2) ls

(3) 鏈接靜態庫

為了在編譯程序中正確找到庫文件,靜態庫必須按照 lib[name].a 的規則命名,如下例中[name]=pr.

ar參數意義:

c: create的意思

r:在庫中插入模塊(替換)。當插入的模塊名已經在庫中存在,則替換同名的模塊。

s:寫入一個目標文件索引到庫中,或者更新一個存在的目標文件索引。

v:該選項用來顯示執行操作選項的附加信息。

t:顯示庫的模塊表清單。一般只顯示模塊名。

ar -crsv libpr.a pr1.o pr2.o

ar -t libpr.a //顯示靜態庫所依賴的文件

(4) 編譯鏈接選項

-L 及-l 參數放在後面.其中,-L 載入庫文件路徑,-l 指明庫文件名字.

gcc -o main main.c -L./ -lpr //生成main

-I後面接頭文件 (大寫的i)

-L後面接庫文件路徑路徑

-l後面接庫文件名,除了「lib」和「.a」部分,全名為libpr.a

(5)執行目標程序

./main

六、動態庫的製作

注意,和動態庫相關的路徑搜索問題可以認為分鏈接時的搜索 和 運行時載入的搜索。鏈接時的搜索就是」-L」,比較簡單直接,我們重點講的是運行時的載入搜索。

(1)生成動態庫 xxx.so

gcc -fPIC -Wall -c pr1.c

PIC告訴編譯器產生與位置無關代碼(Position-Independent Code), 則產生的代碼中,沒有絕對地址,全部使用相對地址,故而代碼可以被載入器載入到內存的任意位置,都可以正確的執行。這正是共享庫所要求的,共享庫被載入時,在內存的位置不是固定的。

gcc -shared -o libpr.so pr1.o

or use one line:

gcc -O -fPIC -shared -o libpr.so pr1.c

(2)編譯時調用動態庫

gcc -o test main.c –L. -lpr

採用該方法執行會報告./test: error while loading shared libraries: libpr.so: cannot open shared object file: No such file or directory

原因:因為在動態函數庫使用時,會查找/usr/lib、/lib目錄下的動態函數庫,而此時我們生成的庫不在裡邊。

這個時候有好幾種方法可以讓他成功運行:

(1)最直接最簡單的方法就是把so拉到/usr/lib或/lib中去,但這好像有點污染環境吧。需要root許可權,在別人的電腦上會很麻煩;會把系統目錄弄得混亂。

(2)新建並編輯/etc/ld.so.conf.d/my.conf文件,加入庫所在目錄的路徑,執行ldconfig命令更新ld.so.cache文件但是需要root許可權。

(3)export LD_LIBRARY_PATH=/tmp

不過這樣export 只對當前shell有效,當另開一個shell時候,又要重新設置。可以把export LD_LIBRARY_PATH=/tmp 語句寫到 ~/.bashrc中,這樣就對當前用戶有效了,寫到/etc/bashrc中就對所有用戶有效了。

echo $LD_LIBRARY_PATH

不過LD_LIBRARY_PATH的設定作用是全局的,過多的使用可能會影響到其他應用程序的運行,所以多用在調試。

小結:

總而言之,靜態庫是以空間換時間,動態庫是以時間換空間。無論你是在Linux平台還是Windows平台下做開發,庫的使用都大同小異。熟練的使各種庫,會給我們帶來許多便利,減少工作的負擔加快工程的進度,從此升職,加薪不是夢,希望對你有所幫助。

-------------------------------------------------------------

關注微信 華清創客在線,與10萬個程序員做朋友。

每天下午17:30乾貨分享,我們不見不散~

Advertisements

你可能會喜歡