轉自:https://blog.csdn.net/u010649766/article/details/78528601linux
咱們在編寫一個 C 語言程序的時候,常常會遇到好多重複或經常使用的部分,若是每次都從新編寫當然是能夠的,不過那樣會大大下降工做效率,而且影響代碼的可讀性,更不利於後期的代碼維護。咱們能夠把他們製做成相應的功能函數,使用時直接調用就會很方便,還能夠進行後期的功能升級。程序員
例如我要在一段代碼中屢次交換兩個變量的值,我能夠在代碼中屢次寫入windows
i=x; x=y; y=i;
不過這樣未免有點麻煩咱們能夠編寫一個change_two_int()函數進行簡化。
定義以下函數:函數
void change_two_int( int *a,int *b ) { int c; c=*a; a=b; *b=c; }
這樣每次要進行交換時只需調用 change_two_int(&x , &y); 便可,是否方便了許多?優化
那麼咱們要討論的和這些有什麼關係呢?庫通俗的說就是把這些經常使用函數的目標文件打包在一塊兒,提供相應函數的接口,便於程序員使用。庫是別人寫好的現有的,成熟的,能夠複用的代碼,咱們只須要知道其接口如何定義,即可以自如使用。spa
共享庫=動態庫操作系統
現實中每一個程序都要依賴不少基礎的底層庫,不可能每一個人的代碼都從零開始,所以庫的存在乎義非同尋常。好比咱們常使用的printf函數,就是 C 標準庫提供的函數。咱們在使用時只須要包含相應的頭文件就可使用(非靜態編譯還要有相應的庫文件)。而不用關心printf函數具體是如何實現的,這樣就大大提升了程序員編寫代碼的效率。從使用方法上分庫大致上能夠分爲兩類:靜態庫和共享庫。在windows中靜態庫是以 .lib 爲後綴的文件,共享庫是以 .dll 爲後綴的文件。在linux中靜態庫是以 .a 爲後綴的文件,共享庫是以 .so爲後綴的文件。
以 linux 下的靜態庫和動態庫爲例咱們研究一下,首先咱們看一下他們的生成方式:.net
靜態庫:命令行
首先將源文件編譯成目標文件:gcc –c a.c b.c
生成靜態庫:ar –rc libstatic.a a.o b.ocode
共享庫:
同靜態庫同樣編譯成目標文件:gcc –c a.c b.c
生成共享庫:gcc –fPIC –shared –o libshared.so a.o b.o
因而可知靜態庫和動態庫都是對目標文件的處理,也能夠說庫文件已是機器碼文件了,靜態庫和共享庫的加載過程有很大的區別。
靜態庫的連接方法:
gcc –o staticcode –L. –lstatic main.c –static (默認庫在當前文件夾)
共享庫的連接方法:
gcc –o sharedcode -L. –lshared main.c (默認庫在當前文件夾)
當程序與靜態庫鏈接時,庫中目標文件所含的全部將被程序使用的函數的機器碼被 copy 到最終的可執行文件中。這就會致使最終生成的可執行代碼量相對變多,至關於編譯器將代碼補充完整了,優勢,這樣運行起來相對就快些。不過會有個缺點: 佔用磁盤和內存空間. 靜態庫會被添加到和它鏈接的每一個程序中, 並且這些程序運行時, 都會被加載到內存中. 無形中又多消耗了更多的內存空間。
與共享庫鏈接的可執行文件只包含它須要的函數的引用表,而不是全部的函數代碼,只有在程序執行時, 那些須要的函數代碼才被拷貝到內存中。優勢,這樣就使可執行文件比較小, 節省磁盤空間,更進一步,操做系統使用虛擬內存,使得一份共享庫駐留在內存中被多個程序使用,也同時節約了內存。缺點,不過因爲運行時要去連接庫會花費必定的時間,執行速度相對會慢一些,總的來講靜態庫是犧牲了空間效率,換取了時間效率,共享庫是犧牲了時間效率換取了空間效率,沒有好與壞的區別,只看具體須要了。
另外,一個程序編好後,有時須要作一些修改和優化,若是咱們要修改的恰好是庫函數的話,在接口不變的前提下,使用共享庫的程序只須要將共享庫從新編譯就能夠了,而使用靜態庫的程序則須要將靜態庫從新編譯好後,將程序再從新編譯一便。這也是使用過程中的差異,以如今的項目舉例,在遠程更新的時候,若是隻是*.so動態庫封裝內容變化了,那麼只須要更新*.so便可。
.dll 動態庫
.lib 靜態庫
庫即爲源代碼的二進制文件
Linux下
.so 動態庫
.a 靜態庫
靜態庫在程序編譯時會被鏈接到目標代碼中,程序運行時將再也不須要該靜態庫。
動態庫在程序編譯時並不會被鏈接到目標代碼中,而是在程序運行是才被載入,所以在程序運行時還須要動態庫存在。
靜態庫的後綴是.a,它的產生分兩步
Step 1.由源文件編譯生成一堆.o,每一個.o裏都包含這個編譯單元的符號表
Step 2.ar命令將不少.o轉換成.a,成文靜態庫
動態庫的後綴是.so,它由gcc加特定參數編譯產生。
例如:
gcc−fPIC−c∗.cgcc−fPIC−c∗.c gcc -shared -Wl,-soname, libfoo.so.1 -olibfoo.so.1.0 *.
在linux下,庫文件通常放在/usr/lib和/lib下,
靜態庫的名字通常爲libxxxx.a,其中xxxx是該lib的名稱
動態庫的名字通常爲libxxxx.so.major.minor,xxxx是該lib的名稱,major是主版本號, minor是副版本號
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庫
當系統加載可執行代碼時候,可以知道其所依賴的庫的名字,可是還須要知道絕對路徑
此時就須要系統動態載入器(dynamiclinker/loader)
對於elf格式的可執行程序,是由ld-linux.so*來完成的
它前後搜索elf文件的 DT_RPATH段—環境變量LD_LIBRARY_PATH—/etc/ld.so.cache文件列表—/lib/,/usr/lib目錄
找到庫文件後將其載入內存
若是安裝在/lib或者/usr/lib下,那麼ld默認可以找到,無需其餘操做。
若是安裝在其餘目錄,須要將其添加到/etc/ld.so.cache文件中,步驟以下
1.編輯/etc/ld.so.conf文件,加入庫文件所在目錄的路徑
2.運行ldconfig,該命令會重建/etc/ld.so.cache文件
############################################################
linux中編譯靜態庫(.a)和動態庫(.so)的基本方法
在linux環境中, 使用ar命令建立靜態庫文件.以下是命令的選項:
d -----從指定的靜態庫文件中刪除文件 m -----把文件移動到指定的靜態庫文件中 p -----把靜態庫文件中指定的文件輸出到標準輸出 q -----快速地把文件追加到靜態庫文件中 r -----把文件插入到靜態庫文件中 t -----顯示靜態庫文件中文件的列表 x -----從靜態庫文件中提取文件
還有多個修飾符修改以上基本選項,詳細請man ar 如下列出三個:
a —–把新的目標文件(*.o)添加到靜態庫文件中現有文件以後
b—–*****以前
v —–使用詳細模式
ar 命令的命令行格式以下:
ar[-]{dmpqrtx}[abcfilNoPsSuvV][membername] [count] archive files…
參數archive定義庫的名稱, files是庫文件中包含的目標文件的清單, 用空格分隔每一個文件.
好比建立一個靜態庫文件的命令以下:
ar r libapue.a error.oerrorlog.o lockreg.o
這樣就了libapue.a靜態庫文件, 能夠用 t 選項顯示包含在庫中的文件
建立庫文件以後,能夠建立這個靜態庫文件的索引來幫助提升和庫鏈接的其餘程序的編譯速度:
使用ranlib程序建立庫的索引,索引存放在庫文件內部.
ranlib libapue.a
用nm程序顯示存檔文件的索引,它能夠顯示目標文件的符號
nm libapue.a | more
若是是顯示目標文件的符號:
nm error.o | more
如何使用呢?以下所示:
gcc -o test test.c libapue.a
這樣就能夠在test.c中調用在libapue.a中的函數了.
gcc -shared -o libapue.soerror.o errorlog.o
這樣就建立了共享庫!
假設共享庫位於當前目錄(即跟程序文件相同的目錄中)
gcc -o test -L. -lapue test.c
這樣就編譯出了不包含函數代碼可執行文件了,可是但你運行時會發現linux動態加載器找不到libapue.so文件.
能夠用ldd 命令查看可執行文件依賴什麼共享庫:
ldd test
如何才能讓動態加載器發現庫文件呢?有兩種方法能夠解決:
環境變量
exportLD_LIBRARY_PATH=」$LD_LIBRARY_PATH:.」
修改/etc/ld.so.conf文件.
通常應用程序的庫文件不與系統庫文件放在同一個目錄下,通常把應用程序的共享庫文件放在 /usr/local/lib 下,新建一個屬於本身的目錄 apue,而後把剛纔 libapue.so 複製過去就好了
同時在 /etc/ld.so.conf 中新增一行:
/usr/local/lib/apue
之後在編譯程序時加上編譯選項:
-L /usr/local/lib/apue -lapue
參數的配置經過 mangcc 能夠看到
-llibrary
鏈接名爲 library 的 庫文件.
鏈接器 在 標準搜索目錄 中 尋找 這個 庫文件, 庫文件 的 真正 名 字