Filename:[Linux]連結,靜态庫和動态庫
Version:V1.0
Date:12/01/2009
Author:S.C.Leon
=====================================================================
在Linux中建立靜态庫和動态庫
一、基本概念
1.1什麼是庫
在windows平台和linux平台下都大量存在着庫。
本質上來說庫是一種可執行代碼的二進制形式,可以被作業系統載入記憶體執行。
由于windows和linux的平台不同(主要是編譯器、彙編器和連接配接器的不同),是以二者庫的二進制是不相容的。
本文僅限于介紹linux下的庫。
1.2庫的種類
linux下的庫有兩種:靜态庫和共享庫(動态庫)。
二者的不同點在于代碼被載入的時刻不同。
靜态庫的代碼在編譯過程中已經被載入可執行程式,是以體積較大。
共享庫的代碼是在可執行程式運作時才載入記憶體的,在編譯過程中僅簡單的引用,是以代碼體積較小。
1.3庫存在的意義
庫是别人寫好的現有的,成熟的,可以複用的代碼,你可以使用但要記得遵守許可協定。
現實中每個程式都要依賴很多基礎的底層庫,不可能每個人的代碼都從零開始,是以庫的存在意義非同尋常。
共享庫的好處是,不同的應用程式如果調用相同的庫,那麼在記憶體裡隻需要有一份該共享庫的執行個體。
1.4庫檔案是如何産生的在linux下
靜态庫的字尾是.a,它的産生分兩步
Step 1.由源檔案編譯生成一堆.o,每個.o裡都包含這個編譯單元的符号表
Step 2.ar指令将很多.o轉換成.a,成文靜态庫
動态庫的字尾是.so,它由gcc加特定參數編譯産生。
具體方法參見後文執行個體。
1.5庫檔案是如何命名的,有沒有什麼規範
在linux下,庫檔案一般放在/usr/lib和/lib下,
靜态庫的名字一般為libxxxx.a,其中xxxx是該lib的名稱
動态庫的名字一般為libxxxx.so.major.minor,xxxx是該lib的名稱,major是主版本号,minor是副版本号
1.6如何知道一個可執行程式依賴哪些庫
ldd指令可以檢視一個可執行程式依賴的共享庫,
例如# ldd /bin/lnlibc.so.6
=> /lib/libc.so.6 (0×40021000)/lib/ld-linux.so.2
=> /lib/ld- linux.so.2 (0×40000000)
可以看到ln指令依賴于libc庫和ld-linux庫
1.7可執行程式在執行的時候如何定位共享庫檔案
當系統加載可執行代碼時候,能夠知道其所依賴的庫的名字,但是還需要知道絕對路徑
此時就需要系統動态載入器(dynamic linker/loader)
對于elf格式的可執行程式,是由ld-linux.so*來完成的,它先後搜尋elf檔案的DT_RPATH段—環境變量LD_LIBRARY_PATH—/etc/ld.so.cache檔案清單—/lib/,/usr/lib目錄找到庫檔案後将其載入記憶體
如:export LD_LIBRARY_PATH=’pwd’
将目前檔案目錄添加為共享目錄
1.8在新安裝一個庫之後如何讓系統能夠找到他
如果安裝在/lib或者/usr/lib下,那麼ld預設能夠找到,無需其他操作。
如果安裝在其他目錄,需要将其添加到/etc/ld.so.cache檔案中,步驟如下
1.編輯/etc/ld.so.conf檔案,加入庫檔案所在目錄的路徑
2.運作ldconfig,該指令會重建/etc/ld.so.cache檔案
二、實驗設計
我們通常把一些公用函數制作成函數庫,供其它程式使用。
函數庫分為靜态庫和動态庫兩種。
靜态庫在程式編譯時會被連接配接到目标代碼中,程式運作時将不再需要該靜态庫。
動态庫在程式編譯時并不會被連接配接到目标代碼中,而是在程式運作是才被載入,是以在程式運作時還需要動态庫存在。
本文主要通過舉例來說明在Linux中如何建立靜态庫和動态庫,以及使用它們。
為了便于闡述,我們先做一部分準備工作。
2.1準備好測試代碼hello.h、hello.c和main.c;
hello.h(見程式1)為該函數庫的頭檔案。
hello.c(見程式2)是函數庫的源程式,其中包含公用函數hello,該函數将在螢幕上輸出"Hello XXX!"。
main.c(見程式3)為測試庫檔案的主程式,在主程式中調用了公用函數hello。
程式1: hello.h
#ifndef HELLO_H
#define HELLO_H
voidhello(constchar*name);
#endif //HELLO_H
程式2: hello.c
#include
voidhello(constchar*name)
{
printf("Hello %s!/n", name);
}
程式3: main.c
#include "hello.h"
intmain()
{
hello("everyone");
return0;
}
2.2問題的提出
注意:這個時候,我們編譯好的hello.o是無法通過gcc –o編譯的,這個道理非常簡單,
hello.c是一個沒有main函數的.c程式,是以不夠成一個完整的程式,如果使用gcc –o編譯并連接配接它,GCC将報錯。
無論靜态庫,還是動态庫,都是由.o檔案建立的。是以,我們必須将源程式hello.c通過gcc先編譯成.o檔案。
這個時候我們有三種思路:
1)通過編譯多個源檔案,直接将目标代碼合成一個.o檔案。
2)通過建立靜态連結庫libmyhello.a,使得main函數調用hello函數時可調用靜态連結庫。
3)通過建立動态連結庫libmyhello.so,使得main函數調用hello函數時可調用靜态連結庫。
2.3思路一:編譯多個源檔案
在系統提示符下鍵入以下指令得到hello.o檔案。
# gcc -c hello.c
為什麼不适用gcc –o hello hello.c這個道理我們之前已經說了,使用-c是什麼意思呢?這涉及到gcc編譯選項的常識。
我們通常使用的gcc –o是将.c源檔案編譯成為一個可執行的二進制代碼,這包括調用作為GCC内的一部分真正的C編譯器(ccl),以及調用GNU C編譯器的輸出中實際可執行代碼的外部GNU彙編器和連接配接器工具。
而gcc –c是使用GNU彙編器将源檔案轉化為目标代碼之後就結束,在這種情況下連接配接器并沒有被執行,是以輸出的目标檔案不會包含作為Linux程式在被裝載和執行時所必須的包含資訊,但它可以在以後被連接配接到一個程式。
我們運作ls指令看看是否生存了hello.o檔案。
# ls
hello.c hello.h hello.o main.c
在ls指令結果中,我們看到了hello.o檔案,本步操作完成。
同理編譯main
#gcc –c main.c
将兩個檔案連結成一個.o檔案。
#gcc –o hello hello.o main.o
運作
# ./hello
Hello everyone!
完成^ ^!
2.4思路二:靜态連結庫
下面我們先來看看如何建立靜态庫,以及使用它。
靜态庫檔案名的命名規範是以lib為字首,緊接着跟靜态庫名,擴充名為.a。例如:我們将建立的靜态庫名為myhello,則靜态庫檔案名就是libmyhello.a。在建立和使用靜态庫時,需要注意這點。建立靜态庫用ar指令。
在系統提示符下鍵入以下指令将建立靜态庫檔案libmyhello.a。
# ar cr libmyhello.a hello.o
我們同樣運作ls指令檢視結果:
# ls
hello.c hello.h hello.o libmyhello.a main.c
ls指令結果中有libmyhello.a。
靜态庫制作完了,如何使用它内部的函數呢?隻需要在使用到這些公用函數的源程式中包含這些公用函數的原型聲明,然後在用gcc指令生成目标檔案時指明靜态庫名,gcc将會從靜态庫中将公用函數連接配接到目标檔案中。注意,gcc會在靜态庫名前加上字首lib,然後追加擴充名.a得到的靜态庫檔案名來查找靜态庫檔案。
在程式3:main.c中,我們包含了靜态庫的頭檔案hello.h,然後在主程式main中直接調用公用函數hello。下面先生成目标程式hello,然後運作hello程式看看結果如何。
# gcc -o hello main.c -L. -lmyhello
# ./hello
Hello everyone!
我們删除靜态庫檔案試試公用函數hello是否真的連接配接到目标檔案hello中了。
# rm libmyhello.a
rm: remove regular file `libmyhello.a'? y
# ./hello
Hello everyone!
程式照常運作,靜态庫中的公用函數已經連接配接到目标檔案中了。
2.5思路三、動态連結庫
我們繼續看看如何在Linux中建立動态庫。我們還是從.o檔案開始。
動态庫檔案名命名規範和靜态庫檔案名命名規範類似,也是在動态庫名增加字首lib,但其檔案擴充名為.so。例如:我們将建立的動态庫名為myhello,則動态庫檔案名就是libmyhello.so。用gcc來建立動态庫。
在系統提示符下鍵入以下指令得到動态庫檔案libmyhello.so。
# gcc -shared -fPCI -o libmyhello.so hello.o
“PCI”指令行标記告訴GCC産生的代碼不要包含對函數和變量具體記憶體位置的引用,這是因為現在還無法知道使用該消息代碼的應用程式會将它連接配接到哪一段記憶體位址空間。這樣編譯出的hello.o可以被用于建立共享連結庫。建立共享連結庫隻需要用GCC的”-shared”标記即可。
我們照樣使用ls指令看看動态庫檔案是否生成。
# ls
hello.c hello.h hello.o libmyhello.so main.c
調用動态連結庫編譯目标檔案。
在程式中使用動态庫和使用靜态庫完全一樣,也是在使用到這些公用函數的源程式中包含這些公用函數的原型聲明,然後在用gcc指令生成目标檔案時指明動态庫名進行編譯。我們先運作gcc指令生成目标檔案,再運作它看看結果。
# gcc -o hello main.c -L. -lmyhello
使用”-lmyhello”标記來告訴GCC驅動程式在連接配接階段引用共享函數庫libmyhello.so。”-L.”标記告訴GCC函數庫可能位于目前目錄。否則GNU連接配接器會查找标準系統函數目錄。
# ./hello
./hello: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory
#
錯誤提示,找不到動态庫檔案libmyhello.so。程式在運作時,會在/usr/lib和/lib等目錄中查找需要的動态庫檔案。若找到,則載入動态庫,否則将提示類似上述錯誤而終止程式運作。我們将檔案libmyhello.so複制到目錄/usr/lib中,再試試。
# mv libmyhello.so /usr/lib
# ./hello
Hello everyone!
#
這也進一步說明了動态庫在程式運作時是需要的。
參考文獻:
1、《Linux進階程式設計》Jon Masters等人民郵電出版社
2、《Linux程式設計》Neil Matthew等人民郵電出版社
3、http://blog.csdn.net/thinkerABC/archive/2006/03/11/621817.aspx
4、