一般庫分爲:靜態庫,共享庫,動態加載庫,。下面分別介紹。
1、 靜態庫:
1.概念:
靜態庫就是一些目標文件的集合,以.a結尾。靜態庫在程序連接的時候使用,連接器會將程序中使用
到函數的代碼從庫文件中拷貝到應用程序中。一旦連接完成,在執行程序的時候就不須要靜態庫了。
因爲每一個使用靜態庫的應用程序都須要拷貝所用函數的代碼,因此靜態連接的文件會比較大。
2.建立與應用:
首先建立庫文件libhello.c
#include <stdio.h>
void hello()
{
printf("hello, welcome to library world!\n");
}
建立頭文件libhello.h
void hello();
如今咱們建立libhello靜態庫文件:
$ gcc -c libhello -o libhello.o
$ ar rcs libhello.a libhello.o
其中ar中的rcs的意思是: r代表將模塊加入到靜態庫中,c表示建立靜態庫,s表示生產索引。
咱們寫一個測試程序:
$ cat test.c
#include <stdio.h>
int main(void)
{
printf("use library hello.\n");
hello();
return 0;
}
編譯與連接:
$ gcc -c test.c -o test.o
$ gcc test.o -L. -lhello -o test
說明:-L.表示將當前目錄加入到庫搜索路徑。默認的庫搜索路徑在/usr/lib目錄下。
另外這裏說明一下易混淆的參數-I, 它表示搜索頭文件的路徑。這樣gcc在查找頭文件的時候會首先
到-I指定的目錄查找,而後纔是系統默認目錄。
-l參數: -lname表示庫搜索目錄下的libname.a 或者libname.so文件 ,
這也是爲何庫文件都以lib開頭的緣由之一。一個慣例嘛。固然了,若是你的庫文件不是
libhello,而是hello. 那就不能用-l參數編譯了。 能夠這樣:
gcc test.o -L. hello.a -o test
注意: $gcc -L. -lhello test.o -o test 會出錯!。
緣由是: -l是連接器選項,必需要放到被編譯文件的後面。 因此上面的命令中-lhello必定要
放到 test.o的後面。
運行:
$ ./test
use library hello.
hello, welcome to library world!
2、共享庫:
一、共享庫的概念:
共享庫以.so結尾. (so == share object) 在程序的連接時候並不像靜態庫那樣在拷貝
使用函數的代碼,而只是做些標記。而後在程序開始啓動運行的時候,動態地加載所需模塊。因此,
應用程序在運行的時候仍然須要共享庫的支持。 共享庫連接出來的文件比靜態庫要小得多。
二、共享庫的命名
通常一個共享庫的有三個名字:soname, real-name, linker-name。下面先看實例:
$ ls -l /usr/lib/libncurses*
lrwxrwxrwx 1 root root 20 2008-05-25 13:54 libncurses.so -> /lib/libncurses.so.5
lrwxrwxrwx 1 root root 13 2008-05-26 15:18 libncurses.so.5 -> libtermcap.so
上面的libncurses.so.5就是soname, 其中ncurses是庫名,5分別是主版本號(major),
固然也能夠有次版本號(minor)和發行號(release)。(相似於libncurses.so.5.0.0)
.so固然表示共享庫了。一般soname只是real name的一個連接。
而libtermcap.so 這是ncurse庫的real-name, 也就是包含真是代碼實現的文件.
libncurses.so 則是linker name,用於應用程序連接的時候的一個搜索名。 它一般是soname的
一個連接,形式爲libname.so
三、共享庫的裝載
(1) 在全部基於GNU glibc的系統(固然包括Linux)中,在啓動一個ELF二進制執行程序時,
一個特殊的程序"程序裝載器"會被自動裝載並運行。在linux中,這個程序裝載器就是
/lib/ld-linux.so.X(X是版本號)。它會查找並裝載應用程序所依賴的全部共享庫。
被搜索的目錄保存在/etc/ls.so.conf文件中,但通常/usr/local/lib並不在搜索之列,
至少debian/ubuntu是這樣。這彷佛是一個系統失誤,只好本身加上了。固然,若是程序的每次啓動,
都要去搜索一番,勢必效率不堪忍受。Linux系統已經考慮這一點,對共享庫採用了緩存管理。ldconfig
就是實現這一功能的工具,其缺省讀取/etc/ld.so.conf文件,對全部共享庫按照必定規範創建
符號鏈接,而後將信息寫入/etc/ld.so.cache。
/etc/ld.so.cache的存在大大加快了程序的啓動速度。
(2) 固然你也能夠經過設置環境變量LD_LIBRARY_PATH來設置ld的裝載路徑。這樣裝載器就會
首先搜索該變量的目錄,而後纔是默認目錄。可是記住,LD_LIBRARY_PATH是用於開發和測試的,
你能夠將一些用於測試的替代共享庫的目錄放到該變量中,相似於/etc/ld.so.preload的做用。
可是該變量不該該用於正經常使用戶的正常程序。
(3) 若是你不使用LD_LIBRARY_PATH環境變量,能夠經過以下方式給裝載器傳入路徑:
$ /lib/ld-linux.so.2 --library-path PATH EXECUTABLE
四、共享庫的建立與應用
(1) 建立共享庫:
gcc -fpic/fPIC -c source.c -o source.o
gcc -shared -Wl,-soname,your_soname -o library_name file_list library_list
說明: -fpic或者-fPIC代表建立position independent code,這一般是建立共享庫必須的。
-Wl 代表給連接器傳送參數,因此這裏-soname, library_name 爲給連接器的參數。
-shared 代表是使用共享庫
下面是使用a.c和b.c建立共享庫的示例:
gcc -fPIC -g -c -Wall a.c
gcc -fPIC -g -c -Wall b.c
gcc -shared -Wl,-soname, libmyab.so.1 -o libmyab.so.1.0.1 a.o b.o -lc
說明: lc == libc
幾個須要注意的地方:
a.不推薦使用strip處理共享庫,最好不要使用-fomit-frame-pointer編譯選項
b.-fPIC和-fpic均可以產生目標獨立代碼,具體應用取決於平臺,-fPIC是always work,
儘管其產生的目標文件可能會大些; -fpic產生的代碼小,執行速度快,但可能有平臺依賴限制。
c.通常狀況下,-Wall,-soname,your_soname編譯選項是須要的。固然,-share選項更不能丟。
(2) 安裝使用共享庫
一旦你建立好共享庫後就須要安裝使用了,最簡單的辦法是將庫拷貝到默認目錄下(/usr/lib)。
而後建立一些符號連接,最簡單的方式仍是使用ldconfig(8)來處理這裏符號連接。最後是從新
編譯連接你的程序,經過-L和-l參數指定庫路徑就能夠了。
3、 動態加載庫
1. 概念
動態加載庫(dynamically loaded (DL) libraries)是指在程序運行過程當中能夠加載的函數庫。
而不是像共享庫同樣在程序啓動的時候加載。DL對於實現插件和模塊很是有用,由於他們能夠然程序在容許
時等待插件的加載。在Linux中,動態庫的文件格式跟共享庫沒有區別,主要區別在於共享庫是運行時加載。
有專門的一組API用於完成打開動態庫,查找符號,
處理出錯,關閉動態庫等功能。
下面對這些接口函數逐一介紹:
(1) dlopen
函數原型:void *dlopen(const char *libname,int flag);
功能描述:dlopen必須在dlerror,dlsym和dlclose以前調用,表示要將庫裝載到內存,準備使用。
若是要裝載的庫依賴於其它庫,必須首先裝載依賴庫。若是dlopen操做失敗,返回NULL值;若是庫已經
被裝載過,則dlopen會返回一樣的句柄。
參數中的libname通常是庫的全路徑,這樣dlopen會直接裝載該文件;若是隻是指定了庫名稱,在dlopen
會按照下面的機制去搜尋:
a.根據環境變量LD_LIBRARY_PATH查找
b.根據/etc/ld.so.cache查找
c.查找依次在/lib和/usr/lib目錄查找。
flag參數表示處理未定義函數的方式,可使用RTLD_LAZY或RTLD_NOW。RTLD_LAZY表示暫時不去
處理未定義函數,先把庫裝載到內存,等用到沒定義的函數再說;RTLD_NOW表示立刻檢查是否存在未定義
的函數,若存在,則dlopen以失敗了結。
(2) dlerror
函數原型:char *dlerror(void);
功能描述:dlerror能夠得到最近一次dlopen,dlsym或dlclose操做的錯誤信息,返回NULL表示
無錯誤。dlerror在返回錯誤信息的同時,也會清除錯誤信息。
(3) dlsym
函數原型:void *dlsym(void *handle,const char *symbol);
功能描述:在dlopen以後,庫被裝載到內存。dlsym能夠得到指定函數(symbol)在內存中的位置(指針)。
若是找不到指定函數,則dlsym會返回NULL值。但判斷函數是否存在最好的方法是使用dlerror函數,
(4) dlclose
函數原型:int dlclose(void *);
功能描述:將已經裝載的庫句柄減一,若是句柄減至零,則該庫會被卸載。若是存在析構函數,則在dlclose
以後,析構函數會被調用。
2. 使用實例
$cat dltest.c
#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>
int main(int argc, char **argv)
{
void *handle;
double (*cosine)(double);
char *error;
handle = dlopen ("/lib/libm.so.6", RTLD_LAZY);
if (!handle) {
fputs (dlerror(), stderr);
exit(1);
}
cosine = dlsym(handle, "cos");
if ((error = dlerror()) != NULL) {
fputs(error, stderr);
exit(1);
}
printf ("%f\n", (*cosine)(2.0));
dlclose(handle);
return 0;
}
編譯: $ gcc -o dltest dltest.c -ldl -Wall
運行: $ ./dltest
-0.416147
4、其餘
(1) nm命令能夠查可能一個庫中的符號linux