Linux靜態庫與動態庫

一、簡介

實際開發工程中,一般會有很多函數只是聲明,而找不到實現的代碼,因為那些實現代碼已經編譯成庫了。在Linux系統中.o、.a、*.so文件都是Linux下的程序函數庫,即編譯好的可供其他程序使用的代碼和數據,一般來講:

  • ==.o==:是目標文件,相當于windows中的.obj文件(動態加載函數庫);
  • ==.so==:為共享庫,是shared object,用于動態鏈接的,和dill差不多(共享函數庫);
  • ==.a==:為靜態庫,是好多個.o合在一起,用于靜態鏈接(靜態函數庫);
  • ==.la==:為libtool自動生成的一些共享庫,vi編輯查看,主要記錄了一些配置信息;

這樣做有以下優點:程序模塊化,容易重新編譯,方便升級。

庫文件在鏈接(靜態庫和共享庫)和運行(僅限于使用共享庫的程序)時被使用,其搜索路勁是系統中進行設置的,一般Linux把/lib和/usr/lib兩個目錄作為默認的庫搜索路徑,所以使用這兩個目錄中的庫時,不需要進行設置搜索路徑即可直接使用。

假設當前目錄下有這些源文件:main.c func.c func.h,其中main.c要調用func.c中的函數。以下分別是Linux下將func.c生成靜態庫和動態庫的方法,以及如何使用生成的靜態庫和動態庫。

二、靜態鏈接庫

2.1 靜態庫的特點

靜態鏈接庫可以將代碼進行封裝,具有如下特點:

  1. 靜態函數庫實際上是簡單的普通目標文件(*.o)的集合;
  2. 靜態函數庫在可執行程序鏈接時就加入到可執行代碼中,在物理上成為可執行程序的一部分;
  3. 靜態函數庫鏈接生成的程序,在哪里都可以運行,無需該靜態函數庫的支持;
  4. 相對于動態函數庫鏈接生成的程序,靜態函數庫鏈接生成的可執行程序文件較大;

2.2 靜態庫的優點

相比源代碼來說,靜態鏈接庫有如下特點:

  1. 可以重用以前的程序模塊;
  2. 開發者可以對源代碼保密;
  3. 允許程序員不用重新編譯代碼而直接把程序link起來,節省了重新編譯代碼的時間(該優勢目前已不明顯);
  4. 理論上將,使用elf格式的靜態庫函數生成的代碼可以比使用共享函數庫生成的程序運行速度快。

2.3 靜態庫的制作

【命令格式】:

在linux環境中,使用ar命令創建靜態庫文件(創建嵌入式靜態庫,可使用arm-linux-ar命令),該命令格式如下:

ar [-]{dmpqrtx}[abcfilNoPsSuvV] [membername] [count] archive files…
arm-linux-ar [-]{dmpqrtx}[abcfilNoPsSuvV] [membername] [count] archive files…
  • ==archive==:定義庫的名稱;
  • ==files== :是庫文件中包含的目標文件的清單,用空格分隔每個文件;

【命令選項】:

  • -a :把新的目標文件(*.o)添加到靜態庫文件中現有文件之后;
  • -b :把新的目標文件(*.o)添加到靜態庫文件中現有文件之前;
  • -d :從指定的靜態庫文件中刪除文件;
  • -m:把文件移動到指定的靜態庫文件中;
  • -p :把靜態庫文件中指定的文件輸出到標準輸出;
  • -q :快速地把文件追加到靜態庫文件中;
  • -r :把文件插入到靜態庫文件中;
  • -t :顯示靜態庫文件中文件的列表;
  • -x :從靜態庫文件中提取文件;
  • -v :使用詳細模式

如下面創建了一個libapue.a庫文件,然后可以用t選項顯示包含在庫中的文件:

$ ar –r libapue.a error.o errorlog.o lockreg.o

++創建庫文件之后,可以創建這個靜態庫文件的索引來幫助提高和庫連接的其他程序的編譯速度。使用ranlib程序創建庫的索引,索引存放在庫文件的內部。++

$ ranlib libapue.a

++用nm程序可以顯示存檔文件的索引,它也可以顯示目標文件的符號:++

$ nm libapue.a | more
$ nm error.o | more

2.4 靜態庫的使用

靜態庫的使用方法,可以采用如下三種中的一種(-static可以不用):

$ gcc test.c –o test libapue.a
$ gcc test.c –o –L. -lfunc –static
$ gcc test.c –o –L./ -lfunc -static

【使用說明】

  1. 使用靜態庫生成的可執行文件放在目標板中可以直接運行;
  2. 使用靜態庫時,需用-L指定靜態庫的位置,如上面指定為當前目錄:-L./;
  3. 使用靜態庫時,需用-l指定靜態庫的庫名,如上面的-lfunc。

【完整使用實例】:

$ gcc func.c –c –o func.o               //生成目標文件
$ ar crv libfunc.a func.o                //生成靜態庫
$ gcc main.c –static –L./ –lfunc –o main   //使用靜態庫
$ ./main                            //執行文件

三、動態鏈接庫

3.1 動態庫的簡介

動態庫是在可執行程序啟動時加載到執行程序中,可以被多個執行程序共享使用,使用動態庫編譯生成的程序相對較小,但運行時需要庫文件支持;

動態庫的鏈接時路徑運行時路徑:現代鏈接器在處理動態庫時將鏈接時路徑(Link-time path)和運行時路徑(Run-time path)分開,用戶可通過-L指定鏈接時,庫文件的路徑;通過-R(或-rpath)指定程序運行時,庫文件的路徑,大大提高了庫應用的靈活性。比如我們做嵌入式移植時#arm-linux-gcc $(CFLAGS) –o target –L/work/lib/zlib/ -llibz-1.2.3 (work/lib/zlib下是交叉編譯好的zlib庫),將target編譯好后我們只要把zlib庫拷貝到開發板的系統默認路徑下即可。或者通過- rpath(或-R )、LD_LIBRARY_PATH指定查找路徑。

3.2 動態庫的命名

每個動態庫(*.so)文件都有個特殊的名字,叫做“soname”,它必須以“lib”作為前綴,然后是函數庫名,然后是“.so”,最后是版本號信息;不過有個特例,就是非常底層的C庫函數都不是以lib開頭命名的;每個動態庫(*.so)文件都有一個真正的名字,叫做“real name”,它是包含真正庫函數代碼的文件;真名有一個主版本號和一個發行版本號外,還有一個名字是編譯器編譯的時候需要的函數庫的名字,這個就是簡單的soname名字,它不包含任何版本號信息:

  • soname:必須的格式:lib+函數庫名+.so+版本號信息,如libreadline.so.3
  • real name:也就是真正的名字,有主版本號和發行版本號;
  • 編譯名字:編譯器編譯的時候需要的函數庫的名字就是不包含版本號信息的soname,如上面的libreadlie.so.3去掉后面的“.3”即可。

管理共享函數庫的關鍵是區分好這些名字,++當可執行程序需要在自己的程序中列出這些他們需要的共享庫函數的時候,它只要用soname就可以了;反過來,當要創建一個新的共享函數庫的時候,只要指定一個特定的文件名,其中包含很細節的版本信息;當安裝一個新版本的函數庫的時候,只要先將這些函數庫文件拷貝到一些特定的目錄中,運行ldconfig這個命令即可++(ldconfig檢查已存在的庫文件,然后創建soname的符號鏈接到真正的函數庫,同時設置/etc/ld.so.cache這個緩沖文件)。

3.3 動態庫的制作

由于動態鏈接庫的共享特性,它們不會被拷貝到可執行文件中。在編譯的時候,編譯器只會做一些函數名之類的檢查,在程序運行的時候,被調用的動態鏈接庫函數被安置在內存的某個地方,所有調用它的程序將指向這個代碼段。因此,這些代碼必須使用相對地址,而不是絕對地址。那么在編譯的時候,我們需要使用地址無關選項(Position Independent Code:PIC)“-fpic”來告訴編譯器,這些對象文件是用來做動態鏈接庫的。創建共享庫的方法:

$ gcc -fpic error.c -c -o error.o
$ gcc -fpic errorlog.c -c -o errorlog.o
$ gcc -fpic lockreg.c -c -o lockreg.o
$ gcc -shared -o libapue.so error.o errorlog.o lockreg.o

也可以使用如下方法:

$ gcc -shared -fpic error.c errorlog.c lockreg.c -o libapue.so
  • -shared選項是告訴編譯器這是要建立動態鏈接庫;這與靜態鏈接庫的建立很不一樣,這里使用的gcc命令而不是ar,同時庫的后綴名為*.so;
  • -fpic選項告訴編譯器該代碼為位置無關碼,不用此選項的話編譯后的代碼是位置相關的,所以動態載入時是通過代碼拷貝的方式來滿足不同進程的需要,而不能達到真正代碼段共享的目的;

編譯共享庫的方法(假設庫文件在當前目錄),可采用如下三種中的一種:

$ gcc test.c -o test -L. -lapue
$ gcc test.c -o test -L./ -lapue
$ gcc test.c -o test libapue.so

這樣就編譯出了不包含函數代碼的可執行文件了,但是當你運行生成的可執行文件時,動態加載器無法動態加載libapue.so文件。那如何才能讓動態加載器發現庫文件呢?有如下兩種方法:

  • 在環境變量LD_LIBRARY_PATH中指明庫的搜索路徑;
export LD_LIBRARY_PATH=”$(LD_LIBRARY_PATH):.”  #添加當前路勁
  • 配置/etc/ld.so.conf文件(推薦且簡單的方法):一般應用程序的庫文件不與系統庫文件放在同一個目錄下,一般把應用程序的共享庫文件放在/usr/local/lib下,新建一個屬于自己的目錄apue,然后把自己的libapue.so復制過去就行了;同時在/etc/ld.so.conf中新增一行:
/usr/local/lib/apue

以后在編譯程序時加上編譯選項:-L/usr/local/lib/apue –lapue;針對具體的可執行文件,可以通過ldd命令查看該可執行文件依賴什么共享庫:

ldd test

3.4 動態庫的使用

【使用說明】

  1. 使用動態庫生成的可執行文件,需將生成的動態庫也拷貝到目標板的連接文件目錄(/lib或/usr/lib)中,才可以順利執行。如果將生成的libfunc.so.1.0.0拷貝到系統lib目錄(如/usr/lib),則倒數第二步就不需要了。
  2. 使用動態庫鏈接生成可執行文件時,需要用-L指定動態庫的位置,如上面指定為當前目錄:-L./。

【使用實例】

$ gcc –fPIC func.c -c –o func.o          
$ gcc –shared –o libfunc.so.1.0.0 func.o
$ ln –s libfunc.so.1.0.0 libfunc.so
$ gcc main.c -o main -lfunc -L./
$ export LD_LIBRARY_PATH=$(pwd):$LD_LIBRARY_PATH
$ ./main

四、程序編譯時和運行時庫文件路徑的指定方法

如何指定程序編譯和運行時使用的靜態庫和動態庫呢?在連接時,可以通過-L選項來指定編譯時所用到的靜態庫和動態庫文件路徑。在程序運行時,可以通過配置/etc/ld.so.conf文件或者使用環境變量LD_LIBRARY_PATH來配置。

4.1 配置/etc/ld.so.conf文件

一般應用程序的庫文件不與系統庫文件放在同一目錄下,一般把應用程序的共享庫文件放在/usr/local/lib下,新建一個屬于自己的目錄apue,然后把剛才生成的libapue.so復制過去,同時在/etc/ld.so.conf中添加庫文件的路勁,一行一個,如:

/usr/local/lib/apue
/usr/X11R6/lib
/opt/lib

也可以在/etc/ld.so.conf.d目錄下創建一個.conf文件,將搜索路徑一行一個的加入該文件中。以后在編譯程序時,加上如下編譯選項,就可以使用共享庫了。

-L/usr/local/lib/apue –lapue

但需要注意的是:更改/etc/ld.so.conf的設置方法對于程序連接時的庫(包括共享庫和靜態庫)的定位已經足夠了,但是對于使用了共享庫的程序的執行還是不夠的。這是因為為了加快程序執行時對共享庫的定位速度,避免使用搜索路勁查找共享庫的低效率,動態加載器是直接讀取庫列表文件/etc/ld.so.cache來進行搜索的。Ld.so.cache是一個非文本的數據文件,不能直接編輯,它是根據/etc/ld.so.conf中設置的路勁由/sbin/ldconfig命令將這些搜索路徑下的共享庫文件集中在一起而生成的(ldconfig需要root權限才能執行)。

因此,為了保證程序執行時對庫的定位,在/etc/ld.so.conf中進行了庫文件搜索路徑設置后,還需要運行/sbin/ldconfig命令更新/etc/ld.so.cache文件。

4.2 修改環境變量LD_LIBRARY_PATH

方法一需要有root權限才能設置,而且當系統重新啟動后,所有的基于GTK2的程序在運行時都將使用新安裝的GTK+庫。不幸的是,由于GTK+版本的改變,這有時會給應用程序帶來兼容性的問題,造成程序運行不正常。為避免出現這種情況,可采用在環境變量LD_LIBRARY_PATH中指明庫的搜索路徑的方法,它不需要root權限,設置也簡單。

設置方法如下:

$ export LD_LIBRARY_PATH=/opt/gtk/lib:$LD_LIBRARY_PATH

查看LD_LIBRARY_PATH的內容的方法為:

$ echo $LD_LIBRARY_PATH

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

4.3 鏈接時通過-L選型顯示指定路徑

在程序鏈接時,對于庫文件(靜態庫和動態庫)的搜索路徑,可以通過-L參數顯示指定庫文件路徑。因為-L設置的路徑將被優先搜索,所以在鏈接的時候通常會以這種方式直接指定要鏈接的庫的路徑。如:

$ gcc main.c -o main -lfunc -L./
$ gcc test.c –o test –L./ -lapue

五、常見問題及解決方法

調用動態庫的時候,有時明明已經將庫的頭文件在目錄通過“-I”include進來了,庫所在文件通過“-L”參數引導,并指定了“-l”的庫名,但是通過ldd命令查看是,就是死活找不到指定鏈接的so庫文件,這時可通過修改LD_LIBRARY_PATH或者/etc/ld.so.conf文件來指定動態庫的目錄。通常這樣做就可以解決庫無法鏈接的問題了。

makefile里面怎么正確的編譯和連接生成的.so庫文件,然后又是在其他程序的makefile里面如何編譯和鏈接才能調用這個庫文件和函數?

  1. 通過export命令設置環境變量將庫的路徑添加到庫目錄中,這種方法不太方便:
export LD_LIBRARY_PATH=$(LD_LIBRARY_PATH:$(pwd));
  1. LD_LIBRARY_PATH可以在/etc/profile,~/.profile或者./bash_profile里設置,或者.bashrc里,改完后運行source /etc/profile或./etc/profile;
  2. 將新的路徑添加進/etc/ld.so.conf,然后執行/sbin/ldconfig。
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,702評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,615評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,606評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,044評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,826評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,227評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,307評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,447評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,992評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,807評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,001評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,550評論 5 361
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,243評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,667評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,930評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,709評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,996評論 2 374

推薦閱讀更多精彩內容