Linux下动态库(.so)和静态库(.a)
linux下有兩種庫(kù):動(dòng)態(tài)庫(kù)和靜態(tài)庫(kù)(共享庫(kù))
二者的不同點(diǎn)在于代碼被載入的時(shí)刻不同。
靜態(tài)庫(kù)的代碼在編譯過(guò)程中已經(jīng)被載入可執(zhí)行程序,因此體積比較大。
動(dòng)態(tài)庫(kù)(共享庫(kù))的代碼在可執(zhí)行程序運(yùn)行時(shí)才載入內(nèi)存,在編譯過(guò)程中僅簡(jiǎn)單的引用,因此代碼體積比較小。
不同的應(yīng)用程序如果調(diào)用相同的庫(kù),那么在內(nèi)存中只需要有一份該動(dòng)態(tài)庫(kù)(共享庫(kù))的實(shí)例。
靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)的最大區(qū)別,靜態(tài)情況下,把庫(kù)直接加載到程序中,而動(dòng)態(tài)庫(kù)鏈接的時(shí)候,它只是保留接口,將動(dòng)態(tài)庫(kù)與程序代碼獨(dú)立,這樣就可以提高代碼的可復(fù)用度,和降低程序的耦合度。
靜態(tài)庫(kù)在程序編譯時(shí)會(huì)被連接到目標(biāo)代碼中,程序運(yùn)行時(shí)將不再需要該靜態(tài)庫(kù)。
動(dòng)態(tài)庫(kù)在程序編譯時(shí)并不會(huì)被連接到目標(biāo)代碼中,而是在程序運(yùn)行是才被載入,因此在程序運(yùn)行時(shí)還需要?jiǎng)討B(tài)庫(kù)存在
?
一? 靜態(tài)庫(kù)
這類庫(kù)的名字一般是libxxx.a;利用靜態(tài)函數(shù)庫(kù)編譯成的文件比較大,因?yàn)檎麄€(gè) 函數(shù)庫(kù)的所有數(shù)據(jù)都會(huì)被整合進(jìn)目標(biāo)代碼中,他的優(yōu)點(diǎn)就顯而易見(jiàn)了,即編譯后的執(zhí)行程序不需要外部的函數(shù)庫(kù)支持,因?yàn)樗惺褂玫暮瘮?shù)都已經(jīng)被編譯進(jìn)去了。當(dāng)然這也會(huì)成為他的缺點(diǎn),因?yàn)槿绻o態(tài)函數(shù)庫(kù)改變了,那么你的程序必須重新編譯。
靜態(tài)庫(kù)的代碼在編譯時(shí)鏈接到應(yīng)用程序中,因此編譯時(shí)庫(kù)文件必須存在,并且需要通過(guò)“-L”參數(shù)傳遞給編譯器,應(yīng)用程序在開(kāi)始執(zhí)行時(shí),庫(kù)函數(shù)代碼將隨程序一起調(diào)入進(jìn)程內(nèi)存段直到進(jìn)程結(jié)束,其執(zhí)行過(guò)程不需要原靜態(tài)庫(kù)存在。
在UNIX中,使用ar命令創(chuàng)建或者操作靜態(tài)庫(kù)
ar???? archivefile objfile
archivefile:archivefile是靜態(tài)庫(kù)的名稱
objfile:objfile是已.o為擴(kuò)展名的中間目標(biāo)文件名,可以多個(gè)并列
參數(shù)??????? 意義
-r??????????? 將objfile文件插入靜態(tài)庫(kù)尾或者替換靜態(tài)庫(kù)中同名文件
-x??????????? 從靜態(tài)庫(kù)文件中抽取文件objfile
-t???????????? 打印靜態(tài)庫(kù)的成員文件列表
-d??????????? 從靜態(tài)庫(kù)中刪除文件objfile
-s?????????? 重置靜態(tài)庫(kù)文件索引
-v????????????創(chuàng)建文件冗余信息
-c??????????? 創(chuàng)建靜態(tài)庫(kù)文件
?example:
?
/****************** hello.h **************/void hello(void); /****************** hello.cpp **************/#include<iostream> #include"hello.h" using namespace std; void hello(void) {cout <<"Hello "<<endl; } /****************** main.cpp **************/#include"hello.h"int main(int argc,char *argv[]) {hello();return 0; }
?1.編譯成靜態(tài)庫(kù)
無(wú)論靜態(tài)庫(kù),還是動(dòng)態(tài)庫(kù),都是由.o文件創(chuàng)建的。因此,我們必須將源程序hello.c通過(guò)gcc先編譯成.o文件。
hc@linux-v07j:~/weiming/tt> g++ -o hello.o -c hello.cpp
hc@linux-v07j:~/weiming/tt> ar cqs libHello.a hello.o
hc@linux-v07j:~/weiming/tt> ls
hello.cpp? hello.h? hello.o? libHello.a? main.cpp
?2.鏈接
hc@linux-v07j:~/weiming/tt> g++ main.cpp libHello.a -o Out1? (g++ -o aOut main.cpp ./libHello.a 或者 g++ -o aOut main.cpp? -L./ -lHello)
hc@linux-v07j:~/weiming/tt> ls
hello.cpp? hello.h? hello.o? libHello.a? main.cpp? Out1
?
hc@linux-v07j:~/weiming/tt> ldd Out1
??????? linux-gate.so.1 =>? (0xffffe000)
??????? libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0xb7e36000)
??????? libm.so.6 => /lib/libm.so.6 (0xb7e11000)
??????? libgcc_s.so.1 => /lib/libgcc_s.so.1 (0xb7e06000)
??????? libc.so.6 => /lib/libc.so.6 (0xb7ce3000)
??????? /lib/ld-linux.so.2 (0xb7f1b000)
?
?
二: 動(dòng)態(tài)庫(kù)
這類庫(kù)的名字一般是libxxx.so;相對(duì)于靜態(tài)函數(shù)庫(kù),動(dòng)態(tài)函數(shù)庫(kù)在編譯的時(shí)候 并沒(méi)有被編譯進(jìn)目標(biāo)代碼中,你的程序執(zhí)行到相關(guān)函數(shù)時(shí)才調(diào)用該函數(shù)庫(kù)里的相應(yīng)函數(shù),因此動(dòng)態(tài)函數(shù)庫(kù)所產(chǎn)生的可執(zhí)行文件比較小。由于函數(shù)庫(kù)沒(méi)有被整合進(jìn)你的程序,而是程序運(yùn)行時(shí)動(dòng)態(tài)的申請(qǐng)并調(diào)用,所以程序的運(yùn)行環(huán)境中必須提供相應(yīng)的庫(kù)。動(dòng)態(tài)函數(shù)庫(kù)的改變并不影響你的程序,所以動(dòng)態(tài)函數(shù)庫(kù)的升級(jí)比較方便
不同的UNIX系統(tǒng),鏈接動(dòng)態(tài)庫(kù)方法,實(shí)現(xiàn)細(xì)節(jié)不一樣
編譯PIC型.o中間文件的方法一般是采用C語(yǔ)言編譯器的-KPIC或者-fpic選項(xiàng),有的UNIX版本C語(yǔ)言編譯器默認(rèn)帶上了PIC標(biāo)準(zhǔn).創(chuàng)建最終動(dòng)態(tài)庫(kù)的方法一般采用C語(yǔ)言編譯器的-G或者-shared選項(xiàng),或者直接使用工具ld創(chuàng)建。
最主要的是GCC命令行的一個(gè)選項(xiàng):
-shared 該選項(xiàng)指定生成動(dòng)態(tài)連接庫(kù)(讓連接器生成T類型的導(dǎo)出符號(hào)表,有時(shí)候也生成弱連接W類型的導(dǎo)出符號(hào)),不用該標(biāo)志外部程序無(wú)法連接。相當(dāng)于一個(gè)可執(zhí)行文件
-fPIC:表示編譯為位置獨(dú)立的代碼,不用此選項(xiàng)的話編譯后的代碼是位置相關(guān)的所以動(dòng)態(tài)載入時(shí)是通過(guò)代碼拷貝的方式來(lái)滿足不同進(jìn)程的需要,而不能達(dá)到真正代碼段共享的目的。
-L.:表示要連接的庫(kù)在當(dāng)前目錄中
-ltest:編譯器查找動(dòng)態(tài)連接庫(kù)時(shí)有隱含的命名規(guī)則,即在給出的名字前面加上lib,后面加上.so來(lái)確定庫(kù)的名稱
LD_LIBRARY_PATH:這個(gè)環(huán)境變量指示動(dòng)態(tài)連接器可以裝載動(dòng)態(tài)庫(kù)的路徑。
?當(dāng)然如果有root權(quán)限的話,可以修改/etc/ld.so.conf文件,然后調(diào)用 /sbin/ldconfig來(lái)達(dá)到同樣的目的,不過(guò)如果沒(méi)有root權(quán)限,那么只能采用輸出LD_LIBRARY_PATH的方法了。
這里分別將源文件d1.c和d2.c編譯為動(dòng)態(tài)庫(kù)d1.so和d2.so.
/************ d1.h***************/ void print(); /*************** d1.cpp *******************/#include <iostream> #include "d1.h" using namespace std int p = 1; void print(){cout<< p <<endl;} /************ d2.h***************/ void print(); /*************** d2.cpp *******************/ #include <iostream> #include "d2.h" using namespace std;int p = 2; void print() {cout<< p <<endl; }LINUX和其他gcc編譯器
g++ -fpic -c d1.cpp d2.cpp?? ? /*?編譯為.o為擴(kuò)展名的中間目標(biāo)文件d1.o,d2.o*/
g++ -shared -o libd1.so d1.o? ??/*根據(jù)中間目標(biāo)文件d1.o創(chuàng)建動(dòng)態(tài)庫(kù)文件d1.so*/
g++ -shared -o libd2.so d2.o? ??/*根據(jù)中間目標(biāo)文件d2.o創(chuàng)建動(dòng)態(tài)庫(kù)文件d2.so*/
或者直接一步到位
?g++ -O -fpic -shared -o libd1.so d1.cpp
?g++ -O -fpic -shared -o libd2.so d2.cpp
某些版本的gcc上也可以使用-G替換-shared選項(xiàng)
?
調(diào)用動(dòng)態(tài)庫(kù)
?隱身調(diào)用動(dòng)態(tài)庫(kù)
?
/************** main.cpp *********************/void print(); //或者用#include"d1.h"(#include"d2.h")替換 int main(int argc,char *argv[]) {print(); }#cp ./libd1.so libd.so (cp ./libd2.so libd.so )
#?g++ -o dOut main.cpp ./libd.so (或者g++ -o dOut main.cpp -L./ -ld)
hc@linux-v07j:~/weiming/tt/dd> ldd dOut
??????? linux-gate.so.1 =>? (0xffffe000)
??????? libd.so => ./libd.so (0xb7f0f000)? //這個(gè)動(dòng)態(tài)庫(kù)文件比靜態(tài)編譯多的
??????? libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0xb7e2b000)
??????? libm.so.6 => /lib/libm.so.6 (0xb7e06000)
??????? libgcc_s.so.1 => /lib/libgcc_s.so.1 (0xb7dfa000)
??????? libc.so.6 => /lib/libc.so.6 (0xb7cd8000)
??????? /lib/ld-linux.so.2 (0xb7f12000)
在上例中,動(dòng)態(tài)庫(kù)libd.so與執(zhí)行程序在同一目錄下,如果將libd.so移走再執(zhí)行程序,程序?qū)⒉荒苷?zhí)行。
當(dāng)需要載入動(dòng)態(tài)庫(kù)代碼時(shí),UNIX會(huì)按照某種路徑查找動(dòng)態(tài)庫(kù)
通知UNIX系統(tǒng)動(dòng)態(tài)庫(kù)的正確位置有如下兩種方法.,
1)帶編譯路徑
#g++ -o dOut main.cpp ./libd.so (或者g++ -o dOut main.cpp -L./ -ld)
當(dāng)執(zhí)行程序時(shí),程序會(huì)自動(dòng)在當(dāng)前路徑下操作動(dòng)態(tài)庫(kù)libd.so
2)更改環(huán)境變量
#LD_LIBPARY_PATH=./
#export LD_LIBPARY_PATH
不同的UNIX所依賴的動(dòng)態(tài)庫(kù)查找路徑環(huán)境變量名稱各不相同
UNIX版本????????????? 動(dòng)態(tài)庫(kù)查找路徑環(huán)境變量
AIX???????????????? LIB_PATH
LINUX?????????? LD_LIBPARY_PATH
HP_UNIX????? PAHT
SCO UNIX???? LD_LIBPARY_PAHT
?
動(dòng)態(tài)鏈接庫(kù)取代靜態(tài)庫(kù)的好處之一就是可以隨時(shí)升級(jí)庫(kù)的內(nèi)容。
?當(dāng)動(dòng)態(tài)庫(kù)被接口完全相同的庫(kù)文件取代后,可執(zhí)行程序能迅速的切換到新動(dòng)態(tài)庫(kù)中代碼,省去了編譯的麻煩。
例如將libd2.so換成libd.so
?
顯示調(diào)用動(dòng)態(tài)庫(kù)
顯示調(diào)用動(dòng)態(tài)庫(kù),編譯時(shí)無(wú)需庫(kù)文件,執(zhí)行時(shí)動(dòng)態(tài)可存儲(chǔ)于任意位置,庫(kù)里共享對(duì)象必須先申請(qǐng)后使用,不同動(dòng)態(tài)庫(kù)版本,只要其共享對(duì)象接口相同,就可以直接動(dòng)態(tài)加載。
//打開(kāi)動(dòng)態(tài)庫(kù) #include<dlfcn.h> void *dlopen(const char * pathname,int mode);//獲取動(dòng)態(tài)庫(kù)對(duì)象地址 include<dlfcn.h> void *dlsym(void *handle,const char *name);//錯(cuò)誤檢測(cè) include<dlfcn.h> char *dlerror(vid);//關(guān)閉動(dòng)態(tài)庫(kù) include<dlfcn.h> int dlclose(void * handle);動(dòng)態(tài)庫(kù)的加載或多或少會(huì)占用一定的系統(tǒng)資源,比如內(nèi)存等。因此當(dāng)不需要或者一段時(shí)間內(nèi)不需要共享動(dòng)態(tài)庫(kù)時(shí)就要卸載之。函數(shù)dlclose關(guān)閉參數(shù)handle所指向的動(dòng)態(tài)庫(kù),卸載其所占的內(nèi)存等資源,此調(diào)用后參數(shù)handle無(wú)效。
實(shí)際上,由于動(dòng)態(tài)庫(kù)可能同時(shí)被多個(gè)進(jìn)程共享,當(dāng)一個(gè)進(jìn)程指向dlclose時(shí),資源并不馬上被卸載,只有當(dāng)全部進(jìn)程都宣布關(guān)閉動(dòng)態(tài)庫(kù)后,操作系統(tǒng)才開(kāi)始回收動(dòng)態(tài)庫(kù)資源。
?
?
?
總結(jié):
編譯靜態(tài)庫(kù)時(shí)先使用-c選項(xiàng),再利用ar工具產(chǎn)生.編譯動(dòng)態(tài)庫(kù)的方式依不同版本的UNXI而定。隱式調(diào)用動(dòng)態(tài)庫(kù)與靜態(tài)庫(kù)的用法相一致,而顯示調(diào)用動(dòng)態(tài)庫(kù)則需要借助動(dòng)態(tài)加載共享庫(kù)函數(shù)族。
隱式調(diào)用動(dòng)態(tài)庫(kù)和靜態(tài)庫(kù)使用方法一致,使用靜態(tài)庫(kù)和使用動(dòng)態(tài)庫(kù)編譯成目標(biāo)程序使用的gcc命令完全一樣,那當(dāng)靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)同名時(shí),gcc命令會(huì)使用哪個(gè)庫(kù)文件呢?
通過(guò)測(cè)試可以發(fā)現(xiàn),當(dāng)靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)同名時(shí), gcc命令將優(yōu)先使用動(dòng)態(tài)庫(kù).為了確保使用的是靜態(tài)庫(kù), 編譯時(shí)可以加上 -static? 選項(xiàng),因此多第三方程序?yàn)榱舜_保在沒(méi)有相應(yīng)動(dòng)態(tài)庫(kù)時(shí)運(yùn)行正常,喜歡在編譯最后應(yīng)用程序時(shí)加入-static
總結(jié)
以上是生活随笔為你收集整理的Linux下动态库(.so)和静态库(.a)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Apache PHP-fpm
- 下一篇: QWT中Qdial的入门介绍