標籤:它的 位置 計算 過程 定義 添加 [] 目錄 fine
一、什麼是庫
本質上來說庫是一種可執行代碼的二進位形式,可以被作業系統載入記憶體執行。由於windows和linux的本質不同,因此二者庫的二進位是不相容的。 Linux作業系統支援的函數庫分為靜態庫和動態庫,動態庫又稱共用庫。Linux系統有幾個重要的目錄存放相應的函數庫,如/lib /usr/lib。 二、靜態函數庫、動態函數庫 A. 這類庫的名字一般是libxxx.a;利用靜態函數庫編譯成的檔案比較大,因為整個函數庫的所有資料都被整合進目標代碼中,他的優點就顯而易見了,即編譯後的執行程式不需要外部的函數庫支援,因為所有使用的函數都已經被編譯進可執行檔了。當然這也會稱為它的缺點,因為如果靜態函數庫改變了,那麼你的程式必須重新編譯,而且體積也較大。 B.這類庫德名字一般是libxxx.so,動態庫又稱共用庫;相對於靜態函數庫,動態函數庫在編譯的時候並沒有被編譯進目標代碼中,你的程式執行到相關函數時才調用函數庫裡的相應函數,因此動態函數庫所產生的可執行檔比較小。由於函數庫沒有被整合進你的程式,而是程式運行時動態申請並調用,所以程式的運行環境中必須提供相應的庫。動態函數庫的改變並不影響你的程式,所以動態函數庫的升級比較方便。而且如果多個應用程式都要使用同一函數庫,動態庫就非常適合,可以減少應用程式的體積。 注意:不管是靜態函數庫還是動態函數庫,都是由*.o目標檔案產生。 三、函數庫的建立 A.靜態函數庫的建立 ar -cr libname.a test1.o test2.o ar:靜態函數庫建立的命令-c :create的意思-r :replace的意思,表示當前插入的模組名已經在庫中存在,則替換同名的模組。如果若干模組中有一個模組在庫中不存在,ar顯示一個錯誤資訊,並不替換其他同名的模組。預設的情況下,新的成員增加在庫德結尾處。 B.動態函數庫的建立 gcc -shared -fpic -o libname.so test1.c test2.c
-fpic:產生代碼位置無關代碼
-shared :產生共用庫 四、靜態庫和動態庫的使用 案例: add.c#include int add(int a,int b){return a + b;}sub.c#include int sub(int a,int b){return a - b;} head.h #ifndef _HEAD_H_#define _HEAD_H_extern int add(int a,int b);extern int sub(int a,int b);#endifmain.c#include int main(int argc,char *argv[]){int a,b; if(argc < 3){fprintf(stderr,"Usage : %s argv[1] argv[2].\n",argv[0]);return -1;} a = atoi(argv[1]);b = atoi(argv[2]); printf("a + b = %d\n",add(a,b));printf("a - b = %d\n",sub(a,b)); return 0;}產生靜態庫
產生動態庫:
使用產生的產生的庫:
其中-L 指定函數庫尋找的位置,注意L後面還有‘.‘,表示在目前的目錄下尋找-l則指定函數庫名,其中的lib和.a(.so)省略。 注意:-L是指定尋找位置,-l指定需要操作的庫名。 從上面的運行結果中,我們可以看到: A.當動態庫和靜態庫同時存在的時候,gcc預設使用的是動態庫。如果強制使用靜態庫則需要加-static選項支援。B.動態庫產生的可執行檔,test1不能正常的運行。C.連結靜態庫的可執行程式明顯比連結動態庫的可執行檔大。 五、讓連結動態庫的可執行程式正常運行。 當系統載入可執行代碼時候,能夠知道其所依賴的庫的名字,但是還需要知道絕對路勁。此時就需要系統動態載入器(dynamic linker/loader)。 對於elf格式的可執行程式,是由ld-linux.so*來完成的,它先後搜尋elf檔案的DT_RPATH段---環境變數LD_LIBRARY_PATH、/etc/ld.so.cache檔案清單、/usr/lib、/lib目錄找到庫檔案後將其載入記憶體。 A.一種最直接的方法,就是把產生的動態庫拷貝到/usr/lib或/lib中去。
B.使用LD_LIBRARY_PATH環境變數,這個環境變數在ubuntu作業系統中預設沒有,需要手動添加
C.動態在安裝在其他目錄下,如果想作業系統能找到它,可以通過一下步驟 <1>建立並編輯/etc/ld.so.conf.d/my.conf檔案,加入庫所在目錄的路徑<2>執行ldconfig命令更新ld.so.cache檔案
此時,在執行連結動態庫的可執行檔則可以正常運行。 六、查看庫中的符號 A.nm命令可以列印出庫中涉及到的所有符號。庫既可以是靜態庫也可以是動態。 常見的三種符號:<1>在庫中被調用,但沒有在庫中定義(表明需要其他庫支援),用U表示<2>在庫中定義的函數,用T表示<3>“弱態”符號,他們雖然在庫中被定義,但是可能被其他庫中同名的符號覆蓋,用W表示。
B.ldd命令可以查看一個可執行程式依賴的共用庫
七、動態載入庫 用gcc -shared產生的我們稱為動態庫(共用庫),其中動態庫在啟動並執行過程中有兩種方式 A.動態連結 這種方式下,可執行程式只是做一個動態連結,當需要用到動態庫中的函數時,有載入器隱士的載入。 B.動態載入 這種方式下,在可執行程式的內部,我們可以用dlopen()這樣的函數,手動進行載入,dlsym()函數找到我們想要調用函數的入口地址,然後進行調用。這種方式,在寫外掛程式程式中得到廣泛應用。 相關的API:
<1>dlopen()開啟一個新的動態庫,並把它裝入記憶體。該函數主要用來記載庫中的符號,這些符號在編譯的時候是不知道的。 dlopen()函數需要兩個參數:一個檔案名稱和一個標誌。 A.檔案名稱是我們之前接觸過的動態庫的名字,如果它是一個絕對路徑,如:/home/cyg/worddir/libname.so,此時dlopen直接到指定的路徑下開啟動態庫。如果沒有指定路徑,僅僅指定了一個動態庫的名字,此時dlopen將它先後搜尋elf檔案的DT_RPATH段、環境變數LD_LIBRARY_PATH、/etc/ld.so.cache檔案清單、/lib、/usr/lib目錄找到庫檔案後將其載入記憶體。
B.標誌指明是否立刻計算庫的依賴性。
常常一個庫中還依賴別的庫,就是這個函數實現的時候,調用了別的庫函數。不是在這個庫中實現的函數我們稱為位定義的符號。
如果將標誌 設定為RTLD_NOW的話,則會在dlopen函數返回前,將這些未定義的符號解析出來。如果設定為RTLD_LAZY,則會在需要的時候才會去解析。 傳回值:dlopen()函數會返回一個控制代碼作為dlsym()函數的第一個參數,以獲得符號在庫中的地址。使用這個地址,就可以獲得庫中特定函數的指標,並且調用裝載庫中的相應函數。 <2>dlerror() 當動態連結程式庫操作函數執行失敗時,dlerror可以返回出錯資訊,傳回值為NULL時表示操作函數執行成功。 <3>void *dlsym(void *handle,char *symbol); dlsym根據動態連結程式庫操作控制代碼(handle)與符號(symbol),返回符號對應的函數的執行代碼地址。由此地址,可以帶參數執行相應的函數。 如程式碼 :int (*add)(int x,int y);//函數指標 handle = dlopen("xxx.so",RTLD_LAZY);//開啟共用庫add = dlsym(handle,"add");//擷取add函數在共用庫的地址value = add(12,34);//調用add函數 <4>int dlclose(void *handle);dlclose用於關閉指定控制代碼的動態連結程式庫,只有當此動態連結程式庫的使用計數為0時,才會真正被系統卸載。 案例:#include #include #include #include int test_dl(char *pso,char *pfu){void *handle;int (*ptest)(int x,int y); if((handle = dlopen(pso,RTLD_LAZY)) == NULL){printf("%s.\n",dlerror());return -1;} if((ptest = dlsym(handle,pfu)) == NULL){printf("%s.\n",dlerror());return -1;} printf("ptest complete : %d.\n",ptest(12,13)); dlclose(handle); return 0;} int main(int argc,char *argv[]){char buf[100];char *pso,*pfun; printf("Input xxx.so:function_name.\n");while(1){printf(">");fgets(buf,sizeof(buf),stdin);buf[strlen(buf)-1] = 0; pso = strdup(strtok(buf,":"));pfun = strdup(strtok(NULL,":")); test_dl(pso,pfun);} return 0;}
運行結果:
linux 靜態庫、共用庫