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
#ifndef STR_OUT_H
#define STR_OUT_H
void str_out(const char* str);
#endif
#include "str_out.h"
void str_out(const char* str)
{
printf("%s / n",str);
}
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 -o libstr_out.so str_out.o
該指令生成libstr_out.so 動态函數庫。
-shared: 指定生成動态連結庫。
-static: 指定生成靜态連結庫。
-fPIC: 表示編譯為位置獨立的代碼,用于編譯共享庫。目标檔案需要建立成位置無關碼,概念上就是在可執行程式裝載它們的時候,它們可以放在可執行程式的記憶體裡的任何地方。
-L.: 表示要連接配接的庫在目前目錄中。
-l: 指定連結時需要的動态庫。編譯器查找動态連接配接庫時有隐含的命名規則,即在給出的名字前面加上lib ,後面加上 .so 來确定庫的名稱。
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檔案進行靜态編譯