轉自:https://blog.csdn.net/u010649766/article/details/78528601
函數的必要性
我們在編寫一個 C 語言程序的時候,經常會遇到好多重復或常用的部分,如果每次都重新編寫固然是可以的,不過那樣會大大降低工作效率,並且影響代碼的可讀性,更不利於后期的代碼維護。我們可以把他們制作成相應的功能函數,使用時直接調用就會很方便,還可以進行后期的功能升級。
例如我要在一段代碼中多次交換兩個變量的值,我可以在代碼中多次寫入
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); 即可,是否方便了許多?
那么我們要討論的和這些有什么關系呢?庫通俗的說就是把這些常用函數的目標文件打包在一起,提供相應函數的接口,便於程序員使用。庫是別人寫好的現有的,成熟的,可以復用的代碼,我們只需要知道其接口如何定義,便可以自如使用。
共享庫=動態庫
現實中每個程序都要依賴很多基礎的底層庫,不可能每個人的代碼都從零開始,因此庫的存在意義非同尋常。比如我們常使用的printf函數,就是 C 標准庫提供的函數。我們在使用時只需要包含相應的頭文件就可以使用(非靜態編譯還要有相應的庫文件)。而不用關心printf函數具體是如何實現的,這樣就大大提高了程序員編寫代碼的效率。從使用方法上分庫大體上可以分為兩類:靜態庫和共享庫。在windows中靜態庫是以 .lib 為后綴的文件,共享庫是以 .dll 為后綴的文件。在linux中靜態庫是以 .a 為后綴的文件,共享庫是以 .so為后綴的文件。
以 linux 下的靜態庫和動態庫為例我們研究一下,首先我們看一下他們的生成方式:
-
靜態庫:
首先將源文件編譯成目標文件:gcc –c a.c b.c
生成靜態庫:ar –rc libstatic.a a.o b.o -
共享庫:
同靜態庫一樣編譯成目標文件: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即可。
總結:
(一) 靜態庫和動態庫在兩種系統下存在形式
- Windows下
.dll 動態庫
.lib 靜態庫
庫即為源代碼的二進制文件
-
Linux下
.so 動態庫
.a 靜態庫
(二) 靜態庫和動態庫的優缺點
靜態庫在程序編譯時會被連接到目標代碼中,程序運行時將不再需要該靜態庫。
動態庫在程序編譯時並不會被連接到目標代碼中,而是在程序運行是才被載入,因此在程序運行時還需要動態庫存在。
1.庫文件是如何產生的在 linux 下
靜態庫的后綴是.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 *.
2. 庫文件是如何命名的,有沒有什么規范
在linux下,庫文件一般放在/usr/lib和/lib下,
靜態庫的名字一般為libxxxx.a,其中xxxx是該lib的名稱
動態庫的名字一般為libxxxx.so.major.minor,xxxx是該lib的名稱,major是主版本號, minor是副版本號
3.如何知道一個可執行程序依賴哪些庫
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庫
4.可執行程序在執行的時候如何定位共享庫文件
當系統加載可執行代碼時候,能夠知道其所依賴的庫的名字,但是還需要知道絕對路徑
此時就需要系統動態載入器(dynamiclinker/loader)
對於elf格式的可執行程序,是由ld-linux.so*來完成的
它先后搜索elf文件的 DT_RPATH段—環境變量LD_LIBRARY_PATH—/etc/ld.so.cache文件列表—/lib/,/usr/lib目錄
找到庫文件后將其載入內存
5.在新安裝一個庫之后如何讓系統能夠找到他
如果安裝在/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中的函數了.
(四) 動態庫
1. 創建共享庫
gcc -shared -o libapue.soerror.o errorlog.o
這樣就創建了共享庫!
2. 編譯共享庫
假設共享庫位於當前目錄(即跟程序文件相同的目錄中)
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 的 庫文件.
連接器 在 標准搜索目錄 中 尋找 這個 庫文件, 庫文件 的 真正 名 字