Linux下動態(tài)庫(.so)和靜態(tài)庫(.a) 的區(qū)別

二者的不同點在于代碼被載入的時刻不同胁赢。

靜態(tài)庫的代碼在編譯過程中已經(jīng)被載入可執(zhí)行程序,因此體積比較大。

動態(tài)庫(共享庫)的代碼在可執(zhí)行程序運行時才載入內(nèi)存系馆,在編譯過程中僅簡單的引用代兵,因此代碼體積比較小。

不同的應用程序如果調用相同的庫,那么在內(nèi)存中只需要有一份該動態(tài)庫(共享庫)的實例土辩。

靜態(tài)庫和動態(tài)庫的最大區(qū)別,靜態(tài)情況下,把庫直接加載到程序中,而動態(tài)庫鏈接的時候,它只是保留接口,將動態(tài)庫與程序代碼獨立,這樣就可以提高代碼的可復用度,和降低程序的耦合度。

靜態(tài)庫在程序編譯時會被連接到目標代碼中,程序運行時將不再需要該靜態(tài)庫松忍。

動態(tài)庫在程序編譯時并不會被連接到目標代碼中摊溶,而是在程序運行是才被載入,因此在程序運行時還需要動態(tài)庫存在

一 靜態(tài)庫

這類庫的名字一般是libxxx.a;利用靜態(tài)函數(shù)庫編譯成的文件比較大,因為整個 函數(shù)庫的所有數(shù)據(jù)都會被整合進目標代碼中,他的優(yōu)點就顯而易見了,即編譯后的執(zhí)行程序不需要外部的函數(shù)庫支持硼婿,因為所有使用的函數(shù)都已經(jīng)被編譯進去了。當然這也會成為他的缺點记焊,因為如果靜態(tài)函數(shù)庫改變了瓢颅,那么你的程序必須重新編譯信柿。

靜態(tài)庫的代碼在編譯時鏈接到應用程序中,因此編譯時庫文件必須存在,并且需要通過“-L”參數(shù)傳遞路徑給編譯器,應用程序在開始執(zhí)行時,庫函數(shù)代碼將隨程序一起調入進程內(nèi)存段直到進程結束蝗岖,其執(zhí)行過程不需要原靜態(tài)庫存在铅鲤。

在UNIX中,使用ar命令創(chuàng)建或者操作靜態(tài)庫

ar archivefile objfile

archivefile:archivefile是靜態(tài)庫的名稱

objfile:objfile是已.o為擴展名的中間目標文件名骇塘,可以多個并列

參數(shù) 意義

-r 將objfile文件插入靜態(tài)庫尾或者替換靜態(tài)庫中同名文件

-x 從靜態(tài)庫文件中抽取文件objfile

-t 打印靜態(tài)庫的成員文件列表

-d 從靜態(tài)庫中刪除文件objfile

-s 重置靜態(tài)庫文件索引

-v 創(chuàng)建文件冗余信息

-c 創(chuàng)建靜態(tài)庫文件

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;??

}??

需要C/C++ Linux服務器架構師學習資料加群563998835(資料包括C/C++,Linux溢陪,golang技術咆霜,Nginx,ZeroMQ,MySQL戳寸,Redis袖瞻,fastdfs奕枢,MongoDB谷浅,ZK掌猛,流媒體,CDN,P2P十偶,K8S,Docker坦敌,TCP/IP,協(xié)程穷当,DPDK德撬,ffmpeg等)摇天,免費分享

1.編譯成靜態(tài)庫

無論靜態(tài)庫纯丸,還是動態(tài)庫,都是由.o文件創(chuàng)建的若未。因此倾鲫,我們必須將源程序hello.c通過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)

注意:如果hello() 里面還使用了其他的庫函數(shù)比如pthread_create,則最后生成Out1 時還需 -lpthread,但ar 時可以不用邻吞,只需要在 include 的頭文件中找到函數(shù)符號聲明即可瘦癌,但最終生成可執(zhí)行文件時需要找到所有的符號定義牙寞。

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)

二: 動態(tài)庫

這類庫的名字一般是libxxx.so;相對于靜態(tài)函數(shù)庫,動態(tài)函數(shù)庫在編譯的時候 并沒有被編譯進目標代碼中莫秆,你的程序執(zhí)行到相關函數(shù)時才調用該函數(shù)庫里的相應函數(shù)间雀,因此動態(tài)函數(shù)庫所產(chǎn)生的可執(zhí)行文件比較小。由于函數(shù)庫沒有被整合進你的程序镊屎,而是程序運行時動態(tài)的申請并調用惹挟,所以程序的運行環(huán)境中必須提供相應的庫。動態(tài)函數(shù)庫的改變并不影響你的程序缝驳,所以動態(tài)函數(shù)庫的升級比較方便

不同的UNIX系統(tǒng),鏈接動態(tài)庫方法连锯,實現(xiàn)細節(jié)不一樣

編譯PIC型.o中間文件的方法一般是采用C語言編譯器的-KPIC或者-fpic選項,有的UNIX版本C語言編譯器默認帶上了PIC標準.創(chuàng)建最終動態(tài)庫的方法一般采用C語言編譯器的-G或者-shared選項,或者直接使用工具ld創(chuàng)建用狱。

最主要的是GCC命令行的一個選項:

-shared 該選項指定生成動態(tài)連接庫(讓連接器生成T類型的導出符號表运怖,有時候也生成弱連接W類型的導出符號),不用該標志外部程序無法連接齿拂。相當于一個可執(zhí)行文件-fPIC:表示編譯為位置獨立的代碼驳规,不用此選項的話編譯后的代碼是位置相關的所以動態(tài)載入時是通過代碼拷貝的方式來滿足不同進程的需要,而不能達到真正代碼段共享的目的署海。(轉者注:共享庫各段的加載地址并沒有定死吗购,可以加載到任意位置,因為指令中沒有使用絕對地址(相對于鏈接后的可執(zhí)行文件各segment來說)砸狞,因此稱為位置無關代碼)-L.:表示要連接的庫在當前目錄中-ltest:編譯器查找動態(tài)連接庫時有隱含的命名規(guī)則捻勉,即在給出的名字前面加上lib,后面加上.so來確定庫的名稱

這里分別將源文件d1.c和d2.c編譯為動態(tài)庫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為擴展名的中間目標文件d1.o刀森,d2.o*/

g++ -shared -o libd1.so d1.o /*根據(jù)中間目標文件d1.o創(chuàng)建動態(tài)庫文件d1.so*/

g++ -shared -o libd2.so d2.o /*根據(jù)中間目標文件d2.o創(chuàng)建動態(tài)庫文件d2.so*/

或者直接一步到位

g++ -O -fpic -shared -o libd1.so d1.cpp

g++ -O -fpic -shared -o libd2.so d2.cpp

某些版本的gcc上也可以使用-G替換-shared選項

調用動態(tài)庫

隱式調用動態(tài)庫

/**************??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)?

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)

ldd模擬運行一遍main踱启,在運行過程中做動態(tài)鏈接,從而得知這個可執(zhí)行文件依賴于哪些共享庫研底,每個共享庫都在什么路徑下埠偿,加載到進程地址空間的什么地址。

總之榜晦,共享庫的搜索路徑由動態(tài)鏈接器決定冠蒋,從ld.so(8)的Man Page可以查到共享庫路徑的搜索順序:

1. 首先在環(huán)境變量LD_LIBRARY_PATH所記錄的路徑中查找。

2. 在程序鏈接時指定的 rpath 中查找乾胶,可以 readelf binfile | grep RPATH 抖剿。

3. 然后從緩存文件/etc/ld.so.cache中查找朽寞。這個緩存文件由/sbin/ldconfig命令讀取配置文件/etc/ld.so.conf 之后生成。

(也可以在 ld.so.conf.d 目錄下增加 *.conf 文件斩郎,里面寫入庫路徑脑融,在 ld.so.conf 中 include ld.so.conf.d/*.conf )

4. 如果上述步驟都找不到,則到默認的系統(tǒng)路徑中查找缩宜,先是/usr/lib然后是/lib肘迎。

不同的UNIX所依賴的動態(tài)庫查找路徑環(huán)境變量名稱各不相同

UNIX版本 動態(tài)庫查找路徑環(huán)境變量

AIX LIB_PATH

LINUX LD_LIBRARY_PATH

HP_UNIX PAHT

SCO UNIX LD_LIBRARY_PATH

動態(tài)鏈接庫取代靜態(tài)庫的好處之一就是可以隨時升級庫的內(nèi)容。

當動態(tài)庫被接口完全相同的庫文件取代后,可執(zhí)行程序能迅速的切換到新動態(tài)庫中代碼锻煌,省去了編譯的麻煩膜宋。

顯式調用動態(tài)庫

顯式調用動態(tài)庫,編譯時無需庫文件,執(zhí)行時動態(tài)可存儲于任意位置,庫里共享對象必須先申請后使用,不同動態(tài)庫版本,只要其共享對象接口相同,就可以直接動態(tài)加載。注意添加 "-ldl" 編譯參數(shù)炼幔。

//打開動態(tài)庫??

#include<dlfcn.h>??

void?*dlopen(const?char?*?pathname,int?mode);??


//獲取動態(tài)庫對象地址??

include<dlfcn.h>??

void?*dlsym(void?*handle,const?char?*name);??


//錯誤檢測??

include<dlfcn.h>??

char?*dlerror(vid);??


//關閉動態(tài)庫??

include<dlfcn.h>??

int?dlclose(void?*?handle);??

動態(tài)庫的加載或多或少會占用一定的系統(tǒng)資源,比如內(nèi)存等。因此當不需要或者一段時間內(nèi)不需要共享動態(tài)庫時就要卸載之史简。函數(shù)dlclose關閉參數(shù)handle所指向的動態(tài)庫乃秀,卸載其所占的內(nèi)存等資源,此調用后參數(shù)handle無效。

實際上圆兵,由于動態(tài)庫可能同時被多個進程共享,當一個進程指向dlclose時跺讯,資源并不馬上被卸載,只有當全部進程都宣布關閉動態(tài)庫后,操作系統(tǒng)才開始回收動態(tài)庫資源。

總結:

編譯靜態(tài)庫時先使用-c選項,再利用ar工具產(chǎn)生.編譯動態(tài)庫的方式依不同版本的UNXI而定殉农。隱式調用動態(tài)庫與靜態(tài)庫的用法相一致,而顯示調用動態(tài)庫則需要借助動態(tài)加載共享庫函數(shù)族刀脏。

隱式調用動態(tài)庫和靜態(tài)庫使用方法一致,使用靜態(tài)庫和使用動態(tài)庫編譯成目標程序使用的gcc命令完全一樣,那當靜態(tài)庫和動態(tài)庫同名時超凳,gcc命令會使用哪個庫文件呢愈污?

通過測試可以發(fā)現(xiàn),當靜態(tài)庫和動態(tài)庫同名時, gcc命令將優(yōu)先使用動態(tài)庫.為了確保使用的是靜態(tài)庫, 編譯時可以加上 -static 選項轮傍,因此多第三方程序為了確保在沒有相應動態(tài)庫時運行正常暂雹,喜歡在編譯最后應用程序時加入-static

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市创夜,隨后出現(xiàn)的幾起案子杭跪,更是在濱河造成了極大的恐慌,老刑警劉巖驰吓,帶你破解...
    沈念sama閱讀 217,084評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件涧尿,死亡現(xiàn)場離奇詭異,居然都是意外死亡檬贰,警方通過查閱死者的電腦和手機姑廉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來偎蘸,“玉大人庄蹋,你說我怎么就攤上這事瞬内。” “怎么了限书?”我有些...
    開封第一講書人閱讀 163,450評論 0 353
  • 文/不壞的土叔 我叫張陵虫蝶,是天一觀的道長。 經(jīng)常有香客問我倦西,道長能真,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,322評論 1 293
  • 正文 為了忘掉前任扰柠,我火速辦了婚禮粉铐,結果婚禮上,老公的妹妹穿的比我還像新娘卤档。我一直安慰自己蝙泼,他們只是感情好,可當我...
    茶點故事閱讀 67,370評論 6 390
  • 文/花漫 我一把揭開白布劝枣。 她就那樣靜靜地躺著汤踏,像睡著了一般。 火紅的嫁衣襯著肌膚如雪舔腾。 梳的紋絲不亂的頭發(fā)上溪胶,一...
    開封第一講書人閱讀 51,274評論 1 300
  • 那天,我揣著相機與錄音稳诚,去河邊找鬼哗脖。 笑死,一個胖子當著我的面吹牛扳还,可吹牛的內(nèi)容都是我干的才避。 我是一名探鬼主播,決...
    沈念sama閱讀 40,126評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼氨距,長吁一口氣:“原來是場噩夢啊……” “哼工扎!你這毒婦竟也來了?” 一聲冷哼從身側響起衔蹲,我...
    開封第一講書人閱讀 38,980評論 0 275
  • 序言:老撾萬榮一對情侶失蹤肢娘,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后舆驶,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體橱健,經(jīng)...
    沈念sama閱讀 45,414評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,599評論 3 334
  • 正文 我和宋清朗相戀三年沙廉,在試婚紗的時候發(fā)現(xiàn)自己被綠了拘荡。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,773評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡撬陵,死狀恐怖珊皿,靈堂內(nèi)的尸體忽然破棺而出网缝,到底是詐尸還是另有隱情,我是刑警寧澤蟋定,帶...
    沈念sama閱讀 35,470評論 5 344
  • 正文 年R本政府宣布粉臊,位于F島的核電站,受9級特大地震影響驶兜,放射性物質發(fā)生泄漏扼仲。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,080評論 3 327
  • 文/蒙蒙 一抄淑、第九天 我趴在偏房一處隱蔽的房頂上張望屠凶。 院中可真熱鬧,春花似錦肆资、人聲如沸矗愧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽贱枣。三九已至,卻和暖如春颤专,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背钠乏。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評論 1 269
  • 我被黑心中介騙來泰國打工栖秕, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人晓避。 一個月前我還...
    沈念sama閱讀 47,865評論 2 370
  • 正文 我出身青樓簇捍,卻偏偏與公主長得像,于是被迫代替她去往敵國和親俏拱。 傳聞我的和親對象是個殘疾皇子暑塑,可洞房花燭夜當晚...
    茶點故事閱讀 44,689評論 2 354