一,庫:一種可執行代碼的二進位形式,可以被載入記憶體執行。其中庫分為靜態庫、動態庫
二,靜態庫和動態庫的區別
1)Linux 下靜態庫:名字一般為 lib***.a利用靜態函數庫編譯成的檔案比較大,因為整個 函數庫的所有資料都會被整合進目標代碼中,他的優點就顯而易見了,即編譯後的執行程式不需要外部的函數庫支援,因為所有使用的函數都已經被編譯進去了。當然這也會成為他的缺點,因為如果靜態函數庫改變了,那麼你的程式必須重新編譯。
2)Linux 下動態庫:這類庫的名字一般是libxxx.so;相對於靜態函數庫,動態函數庫在編譯的時候 並沒有被編譯進目標代碼中,你的程式執行到相關函數時才調用該函數庫裡的相應函數,因此動態函數庫所產生的可執行檔比較小。由於函數庫沒有被整合進你的程式,而是程式運行時動態申請並調用,所以程式的運行環境中必須提供相應的庫。動態函數庫的改變並不影響你的程式,所以動態函數庫的升級比較方便。
linux系統有幾個重要的目錄存放相應的函數庫,如/lib /usr/lib。
三,靜態庫的使用
操作工具:gcc 和 ar 命令
1)設計庫的原始碼
part1.c:
<strong>void print1()<br />{<br /> printf("This is the first lib src\n");<br />}<br />part2.c<br /></strong><strong>void print2()<br />{<br /> printf("This is the secound lib src\n");<br />}</strong>
2)編譯.c檔案:
gcc -O -c part1.c part2.c
產生part1.o 和 part2.o檔案
3)連結靜態庫
ar -rsv libpart.a part1.c part2.c
-r replace existing or insert new file(s) into the archive
-s replace existing or insert new file(s) into the archive
-v replace existing or insert new file(s) into the archive
4)調用庫的原始碼
main.c
<strong>int main()<br />{<br /> print1();<br /> print2();</p><p> return 0;<br />}</strong>5)編譯庫並連結庫
gcc -o main main.c -L ./ -lpart //一定不要忘記了 -lpart
解釋: -L 後面跟庫的路徑 -l*** 是靜態庫 lib***.a 中去掉lib 和.a 名字後的
./main //執行
輸出:
This is the first lib src!
This is the second src lib!
四,動態庫的使用
1)設計庫代碼 part.c
<strong>#include <stdio.h> //這個需要加上<br />int p=8;<br />void print1()<br />{<br /> printf("This is the first so src\n");<br />}</strong>2)產生動態庫
gcc -O -fpic -shared -o dl.so pr1.c
-fpic :-f後面跟一些編譯選項,PIC是其中一種,表示產生位置無關代碼(Position Independent Code)
3)調用函數的寫法
<strong>int main()<br />{<br /> print1();<br /> return 0;<br />}</strong>4)調用編譯
gcc -o main ./dl.so
執行: ./main
輸出:
<strong>This is the first so src</strong>六,動態庫的顯式調用
顯式調用動態庫需要四個函數的支援, 函數 dlopen 開啟動態庫, 函數 dlsym 擷取動態庫中對象基址, 函數 dlerror 擷取顯式動態庫操作中的錯誤資訊, 函數 doclose 關閉動態庫.
調用代碼:main.c
<strong>#include <dlfcn.h><br />#include <stdio.h><br />int main()<br />{<br /> void *pHandle;<br /> void (*pFunc)(); // 指向函數的指標<br /> int *p;<br /> pHandle = dlopen("./dl.so", RTLD_NOW); // 開啟動態庫<br /> if(!pHandle){<br /> printf("Can't find dl.so \n");<br /> // exit(1);<br /> } </p><p> pFunc = (void (*)())dlsym(pHandle, "print1"); // 擷取庫函數 print 的地址<br /> if(pFunc)<br /> pFunc();<br /> else<br /> printf("Can't find function print\n"); </p><p> p = (int *)dlsym(pHandle, "p"); // 擷取庫變數 p 的地址 </p><p> if(p)<br /> printf("p = %d\n", *p);<br /> else<br /> printf("Can't find int p\n"); </p><p> dlclose(pHandle); // 關閉動態庫<br /> return 0;<br />} </p><p></strong>編譯調用:gcc -o main main.c -L ./ -ldl
前提:此時還不能立即./main,因為在動態函數庫使用時,會尋找/usr/lib、/lib目錄下的動態函數庫,而此時我們產生的庫不在裡邊。 這個時候有好幾種方法可以讓他成功運行: 最直接最簡單的方法就是把dl.so拉到/usr/lib或/lib中去。 還有一種方法 export LD_LIBRARY_PATH=$(pwd) 另外還可以在/etc/ld.so.conf檔案裡加入我們產生的庫的目錄,然後/sbin/ldconfig。
/etc/ld.so.conf是非常重要的一個目錄,裡面存放的是連結器和載入器搜尋共用庫時要檢查的目錄,預設是從/usr/lib /lib中讀取的,所以想要順利運行,我們也可以把我們庫的目錄加入到這個檔案中並執行/sbin/ldconfig 。另外還有個檔案需要瞭解/etc/ld.so.cache,裡面儲存了常用的動態函數庫,且會先把他們載入到記憶體中,因為記憶體的訪問速度遠遠大於硬碟的訪問速度,這樣可以提高軟體載入動態函數庫的速度了。
我們採用:export LD_LIBRARY_PATH=$(pwd) 可以echo $LD_LIBRARY_PATH 查看一下
執行: ./main
輸出:This is the first so src
p = 8
七,庫依賴的查看
使用ldd命令來查看執行檔案依賴於哪些庫。
該命令用於判斷某個可執行檔 binary 檔案含有什麼動態函式庫。
[root@test root]# ldd [-vdr] [filename]
參數說明:
--version 列印ldd的版本號碼
-v --verbose 列印所有資訊,例如包括符號的版本資訊
-d --data-relocs 執行符號重部署,並報告缺少的目標對象(只對ELF格式適用)
-r --function-relocs 對目標對象和函數執行重新部署,並報告缺少的目標對象和函數(只對ELF格式適用)
如果命令列中給定的庫名字包含'/',這個程式的libc5版本將使用它作為庫名字;否則它將在標準位置搜尋庫。運行一個目前的目錄下的共用庫,加首碼"./"。