Linux下靜態鏈接庫與動態鏈接庫的區別



引言

通常情況下,對函數庫的鏈接是放在編譯時期(compile time)完成的。所有相關的對象文件 (object file)與牽涉到的函數庫(library)被鏈接合成一個可執行文件 (executable file)。程序運行 時,與函數庫再無瓜葛,因爲所有需要的函數已拷貝到自己門下。所以這些函數庫被成爲靜態庫(static libaray),通常文件 名爲“libxxx.a”的形式。

其實,我們也可以把對一些庫函數的鏈接載入推遲到程序運行的時期(runtime)。這就是如雷貫耳的動態鏈接庫(dynamic link library)技術。


一 例子詳解

文件目錄樹如下:

   1. libtest/ 

   2. |-- myjob.c 

   3. |-- myjob.h 

   4. |-- test.c 

靜態庫

A.做成靜態庫  libmyjob.a 

1. $ gcc  -c  myjob.c    -o  myjob.o 

2. $ ar  -c -r -s  libmyjob.a  myjob.o

B.鏈接

   1. $ gcc  test.o  libmyjob.a  -o  test 

C.引用庫情況(無所要信息)

   1. $ ldd test 

   2. linux-gate.so.1 => (0xffffe000) 

   3. libc.so.6 => /lib/libc.so.6 (0xb7e29000) 

   4. /lib/ld-linux.so.2 (0xb7f6e000) 

動態庫

A.做成動態庫  libmyjob.so 

1. $ gcc  -Wall –fPIC    -c  myjob.c    -o  myjob.o   

  2. $ gcc -shared -o libmyjob.so myjob.o   

-shared:  該選項指定生成動態連接庫(讓連接器生成T 類型的導出符號表,有時候也生成弱連接 W 類型的導出符號),不用該標誌外部程序無法連接。相當於一個可執行文件。

-fPIC 表示編譯爲位置獨立的代碼,不用此選項的話編譯後的代碼是位置相關的所以動態載入時是通過代碼拷貝的方式來滿足不同進程的需要,而不能達到真正代碼段共享的目的。

-L. 表示要連接的庫在當前目錄中。

LD_LIBRARY_PATH 這個環境變量指示動態連接器可以裝載動態庫的路徑。

B.鏈接

鏈接方法I ,拷貝到系統庫裏再鏈接,讓 gcc 自己查找:

   1. $ cp libmyjob.so /usr/lib 

   2. $ gcc -o test test.o -lmyjob 

這裏我們可以看到了 -lmyjob  選項, -l[lib_name]  指定庫名,他會主動搜索。 lib[lib_name].so 這個搜索的路徑可以通過  gcc --print-search-dirs 來查找。

鏈接方法II ,手動指定庫路徑

   1. $  gcc -o test test.o -lmyjob -B /path/to/lib

-B  選項就添加 /path/to/lib  gcc 搜索的路徑之中。這樣鏈接沒有問題但是方法 II 中手動鏈接好的程序在 執行 時候仍舊需要指定庫路徑( 鏈接和執行是分開的 )。需要添加系統變量 LD_LIBRARY_PATH :

   1. $ export LD_LIBRARY_PATH=/path/to/lib 

這個時候再來檢測一下test 程序的庫鏈接狀況 ( 方法 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) 

 是不是比靜態鏈接的程序多了一個 libmyjob.so?  這就是靜態與動態的最大區別,靜態情況下,它把庫直接加載到程序裏,而在動態鏈接的時候,它只是保留接口,將動態庫與程序代碼獨立。這樣就可以提高代碼的可複用度,和降低程序的耦合度。

    另外,運行時,要保證主程序能找到動態庫,所以動態庫一般發佈到系統目錄中,要麼就在跟主程序相對很固定的路徑裏,這樣不管主程序在本機何時何地跑,都能找得到動態庫。而靜態庫只作用於鏈接時,運行主程序時靜態庫文件沒存在意義了。


二 靜態庫和動態庫的區別

1. 靜態函數庫 

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

2. 動態函數庫 

這類庫的名字一般是 libxxx.so ;相對於靜態函數庫,動態函數庫在編譯的時候並沒有被編譯進目標代碼中,你的程序執行到相關函數時才調用該函數庫裏的相應函數,因此動態函數庫所產生的可執行文件比較小。由於函數庫沒有被整合進你的程序,而是程序運行時動態的申請並調用,所以程序的運行環境中必須提供相應的庫。 動態函數庫的改變並不影響你的程序,所以動態函數庫的升級比較方便。

 

linux靜態函數庫的創建和使用 

A. 例程 str_out.h str_out.c main.c

/* str_out.h */

#ifndef STR_OUT_H

#define STR_OUT_H

void str_out(const char* str);

#endif

/* str_out.c */

#include "str_out.h"

void str_out(const char* str)

{

    printf("%s / n",str);

}

/* main.c */

int main()

{

    str_out("myjob world");

    return 0;

(第一步 ) gcc -c str_out.c  - o  str_out.o

B.靜態函數庫由 ar 命令創建 

(第二步 ) ar crs libstr_out.a str_out.o

-c : create 的意思 

-r :replace 的意思,表示當插入的模塊名已經在庫中存在,則替換同名的模塊。如果若干模塊中有一個模塊在庫中不存在,ar 顯示一個錯誤消息,並不替換其他同名模塊。


-s: 代表若歸檔文件中包含了對象模式(C++)

C. 使用方法

(第三步 ) gcc  main.c -o out -L. -lstr_out

通過gcc -o out main.c -L. -lstr_out 編譯 main.c 就會把靜態函數庫整合進 out 。 

-L: 指定靜態函數庫的位置供查找,注意L 後面還有 '.' ,表示靜態函數庫在本目錄下查找。 

-l: 則指定了靜態函數庫名,由於靜態函數庫的命名方式是lib***.a ,其中的 lib .a 忽略。 

根據靜態函數庫的特性,此處刪除libstr_out.a out 依然可以運行,因爲靜態庫的內容已經整合進去了。 

動態函數庫的創建和使用 

A. 創建動態庫  

( 第一步 ) gcc -fPIC -Wall -c str_out.c  o str_out.o

(第二步 ) gcc -shared -Wl, -soname,libstr_out.so.1 -o libstr_out.so str_out.o  

該命令生成libstr_out.so  動態函數庫。 

-shared 指定生成動態鏈接庫。 

-static 指定生成靜態鏈接庫。 

-fPIC 表示編譯爲位置獨立的代碼,用於編譯共享庫。目標文件需要創建成位置無關碼,概念上就是在可執行程序裝載它們的時候,它們可以放在可執行程序的內存裏的任何地方。 

-L. 表示要連接的庫在當前目錄中。 

-l 指定鏈接時需要的動態庫。編譯器查找動態連接庫時有隱含的命名規則,即在給出的名字前面加上lib ,後面加上 .so 來確定庫的名稱。 

-Wl,options 把參數(options) 傳遞給鏈接器 ld 。如果 options 中間有逗號 , 就將 options 分成多個選項 , 然後傳遞給鏈接程序。

B.動態庫的使用

這時還不能立即./out ,因爲在動態函數庫使用時,會查找 /usr/lib/lib 目錄下的動態函數庫,而此時我們生成的庫不在裏邊。 

1.最簡單的方法就是把 libstr_out.so 拉到 /usr/lib /lib 中去。

(第三步 ) gcc main.c -o main -lstr_out

2. export LD_LIBRARY_PATH=$(pwd) 

    (第三步 gcc  main.c - o  main   -L.   -lstr_out

3. bashrc profile 文件裏用 LD_LIBRARY_PATH 定義,然後用 source 加載。

4.還可以在 /etc/ld.so.conf 文件里加入我們生成的庫的目錄,然後 /sbin/ldconfig 。 

/etc/ld.so.conf是非常重要的一個目錄,裏面存放的是鏈接器和加載器搜索共享庫時要檢查的目錄,默認是從 /usr/lib  /lib 中讀取的 爲了讓動態鏈接庫爲系統所共享,還需運行動態鏈接庫的管理命令--ldconfig.此執行程序存放在/sbin目錄下.ldconfig命令的用途,主要是在默認搜尋目錄(/lib和/usr/lib)以及動態庫配置文件/etc/ld.so.conf內所列的目錄下,搜索出可共享的動態鏈接庫(格式如前介紹,lib*.so*),進而創建出動態裝入程序(ld.so)所需的連接和緩存文件.緩存文件默認爲 /etc/ld.so.cache,此文件保存已排好序的動態鏈接庫名字列表.ldconfig通常在系統啓動時運行,而當用戶安裝了一個新的動態鏈接庫時,就需要手工運行這個命令.

 

 

另外還有個文件需要了解/etc/ld.so.cache, 裏面保存了常用的動態函數庫,且會先把他們加載到內存中,因爲內存的訪問速度遠遠大於硬盤的訪問速度,這樣可以提高軟件加載動態函數庫的速度了。

    注意:默認情況下使用-l選項將搜索動態鏈接庫(.so),沒有對應.so文件時將使用.a文件進行靜態編譯。 


http://blog.csdn.net/rich_baba/article/details/6138110






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章