在Linux中创建静态库.a和动态库.so
轉(zhuǎn)自:http://www.cnblogs.com/laojie4321/archive/2012/03/28/2421056.html
?
在Linux中創(chuàng)建靜態(tài)庫.a和動態(tài)庫.so
我們通常把一些公用函數(shù)制作成函數(shù)庫,供其它程序使用。
函數(shù)庫分為靜態(tài)庫和動態(tài)庫兩種。
?
1. 靜態(tài)函數(shù)庫
?
??? 這類庫的名字一般是libxxx.a;利用靜態(tài)函數(shù)庫編譯成的文件比較大,因為整個 函數(shù)庫的所有數(shù)據(jù)都會被整合進目標代碼中,他的優(yōu)點就顯而易見了,即編譯后的執(zhí)行程序不需要外部的函數(shù)庫支持,因為所有使用的函數(shù)都已經(jīng)被編譯進去了。當然這也會成為他的缺點,因為如果靜態(tài)函數(shù)庫改變了,那么你的程序必須重新編譯。
?
2. 動態(tài)函數(shù)庫
?
??? 這類庫的名字一般是libxxx.so;相對于靜態(tài)函數(shù)庫,動態(tài)函數(shù)庫在編譯的時候 并沒有被編譯進目標代碼中,你的程序執(zhí)行到相關(guān)函數(shù)時才調(diào)用該函數(shù)庫里的相應(yīng)函數(shù),因此動態(tài)函數(shù)庫所產(chǎn)生的可執(zhí)行文件比較小。由于函數(shù)庫沒有被整合進你的程序,而是程序運行時動態(tài)的申請并調(diào)用,所以程序的運行環(huán)境中必須提供相應(yīng)的庫。動態(tài)函數(shù)庫的改變并不影響你的程序,所以動態(tài)函數(shù)庫的升級比較方便。?
linux系統(tǒng)有幾個重要的目錄存放相應(yīng)的函數(shù)庫,如/lib /usr/lib
?
靜態(tài)庫在程序編譯時會被連接到目標代碼中,程序運行時將不再需要該靜態(tài)庫。
動態(tài)庫在程序編譯時并不會被連接到目標代碼中,而是在程序運行是才被載入,因此在程序運行時還需要動態(tài)庫存在。
本文主要通過舉例來說明在Linux中如何創(chuàng)建靜態(tài)庫和動態(tài)庫,以及使用它們。
在創(chuàng)建函數(shù)庫前,我們先來準備舉例用的源程序,并將函數(shù)庫的源程序編譯成.o文件。
第1步:編輯得到舉例的程序--hello.h、hello.c和main.c;
hello.h(見程序1)為該函數(shù)庫的頭文件。
hello.c(見程序2)是函數(shù)庫的源程序,其中包含公用函數(shù)hello,該函數(shù)將在屏幕上輸出"Hello XXX!"。
main.c(見程序3)為測試庫文件的主程序,在主程序中調(diào)用了公用函數(shù)hello。
?
?程序1: hello.h
?#ifndef HELLO_H
?#define HELLO_H
?
?void hello(const char *name);
?
?#endif //HELLO_H
?
?
?程序2: hello.c
?#include <stdio.h>
?
?void hello(const char *name)
?{
??printf("Hello %s!\n", name);
?}
?
?
??程序3: main.c
?#include "hello.h"
?
?int main()
?{
??hello("everyone");
??return 0;
?}
?
?
第2步:將hello.c編譯成.o文件;
無論靜態(tài)庫,還是動態(tài)庫,都是由.o文件創(chuàng)建的。因此,我們必須將源程序hello.c通過gcc先編譯成.o文件。
在系統(tǒng)提示符下鍵入以下命令得到hello.o文件。
# gcc -c hello.c
#
(注1:本文不介紹各命令使用和其參數(shù)功能,若希望詳細了解它們,請參考其他文檔。)
(注2:首字符"#"是系統(tǒng)提示符,不需要鍵入,下文相同。)
我們運行l(wèi)s命令看看是否生存了hello.o文件。
# ls
hello.c hello.h hello.o main.c
#
(注3:首字符不是"#"為系統(tǒng)運行結(jié)果,下文相同。)
在ls命令結(jié)果中,我們看到了hello.o文件,本步操作完成。
下面我們先來看看如何創(chuàng)建靜態(tài)庫,以及使用它。
?
第3步:由.o文件創(chuàng)建靜態(tài)庫;
靜態(tài)庫文件名的命名規(guī)范是以lib為前綴,緊接著跟靜態(tài)庫名,擴展名為.a。例如:我們將創(chuàng)建的靜態(tài)庫名為myhello,則靜態(tài)庫文件名就是libmyhello.a。在創(chuàng)建和使用靜態(tài)庫時,需要注意這點。創(chuàng)建靜態(tài)庫用ar命令。
在系統(tǒng)提示符下鍵入以下命令將創(chuàng)建靜態(tài)庫文件libmyhello.a。
# ar cr libmyhello.a hello.o
#
我們同樣運行l(wèi)s命令查看結(jié)果:
# ls
hello.c hello.h hello.o libmyhello.a main.c
#
ls命令結(jié)果中有l(wèi)ibmyhello.a。
?
第4步:在程序中使用靜態(tài)庫;
靜態(tài)庫制作完了,如何使用它內(nèi)部的函數(shù)呢?只需要在使用到這些公用函數(shù)的源程序中包含這些公用函數(shù)的原型聲明,然后在用gcc命令生成目標文件時指明靜態(tài)庫名,gcc將會從靜態(tài)庫中將公用函數(shù)連接到目標文件中。注意,gcc會在靜態(tài)庫名前加上前綴lib,然后追加擴展名.a得到的靜態(tài)庫文件名來查找靜態(tài)庫文件。
在程序3:main.c中,我們包含了靜態(tài)庫的頭文件hello.h,然后在主程序main中直接調(diào)用公用函數(shù)hello。下面先生成目標程序hello,然后運行hello程序看看結(jié)果如何。
# gcc -o hello main.c?-L. -lmyhello
# ./hello
Hello everyone!
#
我們刪除靜態(tài)庫文件試試公用函數(shù)hello是否真的連接到目標文件 hello中了。
# rm libmyhello.a
rm: remove regular file `libmyhello.a'? y
# ./hello
Hello everyone!
#
程序照常運行,靜態(tài)庫中的公用函數(shù)已經(jīng)連接到目標文件中了。
我們繼續(xù)看看如何在Linux中創(chuàng)建動態(tài)庫。我們還是從.o文件開始。
?
第5步:由.o文件創(chuàng)建動態(tài)庫文件;
動態(tài)庫文件名命名規(guī)范和靜態(tài)庫文件名命名規(guī)范類似,也是在動態(tài)庫名增加前綴lib,但其文件擴展名為.so。例如:我們將創(chuàng)建的動態(tài)庫名為myhello,則動態(tài)庫文件名就是libmyhello.so。用gcc來創(chuàng)建動態(tài)庫。
在系統(tǒng)提示符下鍵入以下命令得到動態(tài)庫文件libmyhello.so。
# gcc -shared -fPCI -o libmyhello.so hello.o
#
我們照樣使用ls命令看看動態(tài)庫文件是否生成。“PIC”命令行標記告訴GCC產(chǎn)生的代碼不要包含對函數(shù)和變量具體內(nèi)存位置的引用,這是因為現(xiàn)在還無法知道使用該消息代碼的應(yīng)用程序會將它連接到哪一段內(nèi)存地址空間。這樣編譯出的hello.o可以被用于建立共享鏈接庫。建立共享鏈接庫只需要用GCC的”-shared”標記即可。
# ls
hello.c hello.h hello.o libmyhello.so main.c
#
?
第6步:在程序中使用動態(tài)庫;
在程序中使用動態(tài)庫和使用靜態(tài)庫完全一樣,也是在使用到這些公用函數(shù)的源程序中包含這些公用函數(shù)的原型聲明,然后在用gcc命令生成目標文件時指明動態(tài)庫名進行編譯。我們先運行g(shù)cc命令生成目標文件,再運行它看看結(jié)果。
# gcc -o hello main.c?-L. -lmyhello
# ./hello
./hello: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory
#
哦!出錯了。快看看錯誤提示,原來是找不到動態(tài)庫文件libmyhello.so。程序在運行時,會在/usr/lib和/lib等目錄中查找需要的動態(tài)庫文件。若找到,則載入動態(tài)庫,否則將提示類似上述錯誤而終止程序運行。我們將文件libmyhello.so復(fù)制到目錄/usr/lib中,再試試。
(使用”-lmyhello”標記來告訴GCC驅(qū)動程序在連接階段引用共享函數(shù)庫libmyhello.so。”-L.”標記告訴GCC函數(shù)庫可能位于當前目錄。否則GNU連接器會查找標準系統(tǒng)函數(shù)目錄:它先后搜索1.elf文件的?DT_RPATH段—2.環(huán)境變量LD_LIBRARY_PATH—3./etc/ld.so.cache文件列表—4./lib/,/usr/lib目錄找到庫文件后將其載入內(nèi)存,但是我們生成的共享庫在當前文件夾下,并沒有加到上述的4個路徑的任何一個中,因此,執(zhí)行后會出現(xiàn)錯誤)
# mv libmyhello.so /usr/lib
# ./hello
Hello everyone!
#
成功了。這也進一步說明了動態(tài)庫在程序運行時是需要的。
另外:既然連接器會搜尋LD_LIBRARY_PATH所指定的目錄,那么我們可以將這個環(huán)境變量設(shè)置成當前目錄:
先執(zhí)行:
export LD_LIBRARY_PATH=$(pwd)
再執(zhí)行:
./hello
成功!
最后:執(zhí)行:??
ldconfig?? /usr/zhsoft/lib?????
? ??
? 注:?? 當用戶在某個目錄下面創(chuàng)建或拷貝了一個動態(tài)鏈接庫,若想使其被系統(tǒng)共享,可以執(zhí)行一下"ldconfig?? 目錄名"這個命令.此命令的功能在于讓ldconfig將指定目錄下的動態(tài)鏈接庫被系統(tǒng)共享起來,意即:在緩存文件/etc/ld.so.cache中追加進指定目錄下的共享庫.本例讓系統(tǒng)共享了/usr/zhsoft/lib目錄下的動態(tài)鏈接庫.該命令會重建/etc/ld.so.cache文件
成功!
可以查看程序執(zhí)行時調(diào)用動態(tài)庫的過程:
# ldd hello
執(zhí)行 test,可以看到它是如何調(diào)用動態(tài)庫中的函數(shù)的。
[pin@localhost 20090505]$ ldd hello
??????? linux-gate.so.1 => (0x00110000)
??????? libmyhello.so => /usr/lib/libmyhello.so (0x00111000)
??????? libc.so.6 => /lib/libc.so.6 (0x00859000)
??????? /lib/ld-linux.so.2 (0x0083a000)
?
我們回過頭看看,發(fā)現(xiàn)使用靜態(tài)庫和使用動態(tài)庫編譯成目標程序使用的gcc命令完全一樣,那當靜態(tài)庫和動態(tài)庫同名時,gcc命令會使用哪個庫文件呢?抱著對問題必究到底的心情,來試試看。
先刪除 除.c和.h外的 所有文件,恢復(fù)成我們剛剛編輯完舉例程序狀態(tài)。
# rm -f hello hello.o /usr/lib/libmyhello.so
# ls
hello.c hello.h main.c
#
在來創(chuàng)建靜態(tài)庫文件libmyhello.a和動態(tài)庫文件libmyhello.so。
# gcc -c hello.c
# ar cr libmyhello.a hello.o
# gcc -shared -fPCI -o libmyhello.so hello.o
# ls
hello.c hello.h hello.o libmyhello.a libmyhello.so main.c
#
通過上述最后一條ls命令,可以發(fā)現(xiàn)靜態(tài)庫文件libmyhello.a和動態(tài)庫文件libmyhello.so都已經(jīng)生成,并都在當前目錄中。然后,我們運行g(shù)cc命令來使用函數(shù)庫myhello生成目標文件hello,并運行程序 hello。
# gcc -o hello main.c -L. -lmyhello
# ./hello
./hello: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory
#
從程序hello運行的結(jié)果中很容易知道,當靜態(tài)庫和動態(tài)庫同名時, gcc命令將優(yōu)先使用動態(tài)庫。
?
Note:
?
編譯參數(shù)解析
?
最主要的是GCC命令行的一個選項:
-shared 該選項指定生成動態(tài)連接庫(讓連接器生成T類型的導(dǎo)出符號表,有時候也生成弱連接W類型的導(dǎo)出符號),不用該標志外部程序無法連接。相當于一個可執(zhí)行文件
-fPIC:表示編譯為位置獨立的代碼,不用此選項的話編譯后的代碼是位置相關(guān)的所以動態(tài)載入時是通過代碼拷貝的方式來滿足不同進程的需要,而不能達到真正代碼段共享的目的。
-L.:表示要連接的庫在當前目錄中
-ltest:編譯器查找動態(tài)連接庫時有隱含的命名規(guī)則,即在給出的名字前面加上lib,后面加上.so來確定庫的名稱
LD_LIBRARY_PATH:這個環(huán)境變量指示動態(tài)連接器可以裝載動態(tài)庫的路徑。
?當然如果有root權(quán)限的話,可以修改/etc/ld.so.conf文件,然后調(diào)用 /sbin/ldconfig來達到同樣的目的,不過如果沒有root權(quán)限,那么只能采用輸出LD_LIBRARY_PATH的方法了。
?
調(diào)用動態(tài)庫的時候有幾個問題會經(jīng)常碰到,有時,明明已經(jīng)將庫的頭文件所在目錄 通過 “-I” include進來了,庫所在文件通過 “-L”參數(shù)引導(dǎo),并指定了“-l”的庫名,但通過ldd命令察看時,就是死活找不到你指定鏈接的so文件,這時你要作的就是通過修改 LD_LIBRARY_PATH或者/etc/ld.so.conf文件來指定動態(tài)庫的目錄。通常這樣做就可以解決庫無法鏈接的問題了。
?
靜態(tài)庫鏈接時搜索路徑順序:
1. ld會去找GCC命令中的參數(shù)-L
2. 再找gcc的環(huán)境變量LIBRARY_PATH
3. 再找內(nèi)定目錄 /lib /usr/lib /usr/local/lib 這是當初compile gcc時寫在程序內(nèi)的
?
?
?
動態(tài)鏈接時、執(zhí)行時搜索路徑順序:
?
1. ?編譯目標代碼時指定的動態(tài)庫搜索路徑;
2. ?環(huán)境變量LD_LIBRARY_PATH指定的動態(tài)庫搜索路徑;
3. ?配置文件/etc/ld.so.conf中指定的動態(tài)庫搜索路徑;
4. 默認的動態(tài)庫搜索路徑/lib;
5. 默認的動態(tài)庫搜索路徑/usr/lib。
?
?
?
有關(guān)環(huán)境變量:
LIBRARY_PATH環(huán)境變量:指定程序靜態(tài)鏈接庫文件搜索路徑
LD_LIBRARY_PATH環(huán)境變量:指定程序動態(tài)鏈接庫文件搜索路徑
?
?
?
附注:
?
作為Linux程序開發(fā)員,最好對開發(fā)工具和資源的位置有個初步了解。下面簡要介紹一下主要的文件夾和應(yīng)用程序。
?
1.應(yīng)用程序(Applications)
?
應(yīng)用程序通常都有固定的文件夾,系統(tǒng)通用程序放在/usr/bin,日后系統(tǒng)管理員在本地計算機安裝的程序通常放在/usr/local/bin或者/opt文件夾下。除了系統(tǒng)程序外,大部分個人用到的程序都放在/usr/local下,所以保持/usr的整潔十分重要。當升級或者重裝系統(tǒng)的時候,只要把/usr/local的程序備份一下就可以了。
?
一些其他的程序有自己特定的文件夾,比如X Window系統(tǒng),通常安裝在/usr/X11中,或者/usr/X11R6。GNU的編譯器GCC,通常放置在/usr/bin或者/usr/local/bin中,不同的Linux版本可能位置稍有不同。
?
2.頭文件(Head Files)
?
在C語言和其他語言中,頭文件聲明了系統(tǒng)函數(shù)和庫函數(shù),并且定義了一些常量。對于C語言,頭文件基本上散落于/usr/include和它的子文件夾下。其他的編程語言的庫函數(shù)分布在編譯器定義的地方,比如在一些Linux版本中,X Window系統(tǒng)庫函數(shù)分布在/usr/include/X11,GNU C++的庫函數(shù)分布在/usr/include/g++。這些系統(tǒng)庫函數(shù)的位置對于編譯器來說都是“標準位置”,即編譯器能夠自動搜尋這些位置。
?
如果想引用位于標準位置之外的頭文件,我們需要在調(diào)用編譯器的時候加上-I標志,來顯式的說明頭文件所在文件夾。比如,
?
$ gcc -I/usr/openwin/include hello.c
?
會告訴編譯器除了標準位置外,還要去/usr/openwin/include看看有沒有所需的頭文件。詳細情況見編譯器的使用手冊(man gcc)。
?
庫函數(shù)(Library Files)
?
庫函數(shù)就是函數(shù)的倉庫,它們都經(jīng)過編譯,重用性不錯。通常,庫函數(shù)相互合作,來完成特定的任務(wù)。比如操控屏幕的庫函數(shù)(cursers和ncursers庫函數(shù)),數(shù)據(jù)庫讀取庫函數(shù)(dbm庫函數(shù))等。
?
系統(tǒng)調(diào)用的標準庫函數(shù)一般位于/lib以及/usr/lib。C編譯器(精確點說,連接器)需要知道庫函數(shù)的位置。默認情況下,它只搜索標準C庫函數(shù)。
?
庫函數(shù)文件通常開頭字母是lib。后面的部分標示庫函數(shù)的用途(比如C庫函數(shù)用c標識, 數(shù)學(xué)庫函數(shù)用m標示),小數(shù)點后的后綴表明庫函數(shù)的類型:
.a 指靜態(tài)鏈接庫.so 指動態(tài)鏈接庫
去/usr/lib看一下,你會發(fā)現(xiàn),庫函數(shù)都有動態(tài)和靜態(tài)兩個版本。
?
與頭文件一樣,庫函數(shù)通常放在標準位置,但我們也可以通過-L標識符,來添加新的搜索文件夾,-l指定特定的庫函數(shù)文件。比如
?
?$ gcc -o x11fred -L/usr/openwin/lib x11fred.c -lX11
?
上述命令就會在編譯期間,鏈接位于/usr/openwin/lib文件夾下的libX11函數(shù)庫,編譯生成x11fred。
?
靜態(tài)鏈接庫(Static Libraries)
?
最簡單的函數(shù)庫就是一些函數(shù)的簡單集合。調(diào)用庫函數(shù)中的函數(shù)時,需要在調(diào)用函數(shù)中include定義庫函數(shù)的頭文件。我們用-l選項添加標準函數(shù)庫之外的函數(shù)庫。
總結(jié)
以上是生活随笔為你收集整理的在Linux中创建静态库.a和动态库.so的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 项目进度管理和项目成本管理的重点梳理
- 下一篇: Windows Mobile Gprs连