Linux 静态库 动态库
生活随笔
收集整理的這篇文章主要介紹了
Linux 静态库 动态库
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
轉(zhuǎn)自:http://blog.chinaunix.net/uid-26833883-id-3219335.html
?
一、什么是庫(kù)
本質(zhì)上來(lái)說(shuō)庫(kù)是一種可執(zhí)行代碼的二進(jìn)制形式,可以被操作系統(tǒng)載入內(nèi)存執(zhí)行。由于windows和linux的本質(zhì)不同,因此二者庫(kù)的二進(jìn)制是不兼容的。 Linux操作系統(tǒng)支持的函數(shù)庫(kù)分為靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù),動(dòng)態(tài)庫(kù)又稱(chēng)共享庫(kù)。Linux系統(tǒng)有幾個(gè)重要的目錄存放相應(yīng)的函數(shù)庫(kù),如/lib ? ?/usr/lib。 二、靜態(tài)函數(shù)庫(kù)、動(dòng)態(tài)函數(shù)庫(kù) A. ?這類(lèi)庫(kù)的名字一般是libxxx.a;利用靜態(tài)函數(shù)庫(kù)編譯成的文件比較大,因?yàn)檎麄€(gè)函數(shù)庫(kù)的所有數(shù)據(jù)都被整合進(jìn)目標(biāo)代碼中,他的優(yōu)點(diǎn)就顯而易見(jiàn)了,即編譯后的執(zhí)行程序不需要外部的函數(shù)庫(kù)支持,因?yàn)樗惺褂玫暮瘮?shù)都已經(jīng)被編譯進(jìn)可執(zhí)行文件了。當(dāng)然這也會(huì)稱(chēng)為它的缺點(diǎn),因?yàn)槿绻o態(tài)函數(shù)庫(kù)改變了,那么你的程序必須重新編譯,而且體積也較大。 B.這類(lèi)庫(kù)德名字一般是libxxx.so,動(dòng)態(tài)庫(kù)又稱(chēng)共享庫(kù);相對(duì)于靜態(tài)函數(shù)庫(kù),動(dòng)態(tài)函數(shù)庫(kù)在編譯的時(shí)候并沒(méi)有被編譯進(jìn)目標(biāo)代碼中,你的程序執(zhí)行到相關(guān)函數(shù)時(shí)才調(diào)用函數(shù)庫(kù)里的相應(yīng)函數(shù),因此動(dòng)態(tài)函數(shù)庫(kù)所產(chǎn)生的可執(zhí)行文件比較小。由于函數(shù)庫(kù)沒(méi)有被整合進(jìn)你的程序,而是程序運(yùn)行時(shí)動(dòng)態(tài)申請(qǐng)并調(diào)用,所以程序的運(yùn)行環(huán)境中必須提供相應(yīng)的庫(kù)。動(dòng)態(tài)函數(shù)庫(kù)的改變并不影響你的程序,所以動(dòng)態(tài)函數(shù)庫(kù)的升級(jí)比較方便。而且如果多個(gè)應(yīng)用程序都要使用同一函數(shù)庫(kù),動(dòng)態(tài)庫(kù)就非常適合,可以減少應(yīng)用程序的體積。 注意:不管是靜態(tài)函數(shù)庫(kù)還是動(dòng)態(tài)函數(shù)庫(kù),都是由*.o目標(biāo)文件生成。 三、函數(shù)庫(kù)的創(chuàng)建 A.靜態(tài)函數(shù)庫(kù)的創(chuàng)建 ar -cr ?libname.a ? test1.o ?test2.o ar:靜態(tài)函數(shù)庫(kù)創(chuàng)建的命令 -c :create的意思 -r :replace的意思,表示當(dāng)前插入的模塊名已經(jīng)在庫(kù)中存在,則替換同名的模塊。如果若干模塊中有一個(gè)模塊在庫(kù)中不存在,ar顯示一個(gè)錯(cuò)誤信息,并不替換其他同名的模塊。默認(rèn)的情況下,新的成員增加在庫(kù)德結(jié)尾處。 B.動(dòng)態(tài)函數(shù)庫(kù)的創(chuàng)建 gcc -shared ?-fpic ?-o libname.so ?test1.c test2.c -fpic:產(chǎn)生代碼位置無(wú)關(guān)代碼-shared :生成共享庫(kù) 四、靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)的使用? 案例: add.c 1 #include <stdio.h> 2 3 int add(int a,int b) 4 { 5 return a + b; 6 }
sub.c
1 #include <stdio.h> 2 3 int sub(int a,int b) 4 { 5 return a - b; 6 } ? head.h 1 #ifndef _HEAD_H_ 2 #define _HEAD_H_ 3 4 extern int add(int a,int b); 5 extern int sub(int a,int b); 6 7 #endif?
main.c 1 #include <stdio.h> 2 #include <stdlib.h> 3 #include "head.h" 4 5 int main(int argc,char *argv[]) 6 { 7 int a,b; 8 9 if(argc < 3) 10 { 11 fprintf(stderr,"Usage : %s argv[1] argv[2].\n",argv[0]); 12 return -1; 13 } 14 15 a = atoi(argv[1]); 16 b = atoi(argv[2]); 17 18 printf("a + b = %d\n",add(a,b)); 19 printf("a - b = %d\n",sub(a,b)); 20 21 return 0; 22 }?
生成靜態(tài)庫(kù)生成動(dòng)態(tài)庫(kù):
使用生成的生成的庫(kù):
其中 -L 指定函數(shù)庫(kù)查找的位置,注意L后面還有'.',表示在當(dāng)前目錄下查找 -l則指定函數(shù)庫(kù)名,其中的lib和.a(.so)省略。 注意:-L是指定查找位置,-l指定需要操作的庫(kù)名。 從上面的運(yùn)行結(jié)果中,我們可以看到: A.當(dāng)動(dòng)態(tài)庫(kù)和靜態(tài)庫(kù)同時(shí)存在的時(shí)候,gcc默認(rèn)使用的是動(dòng)態(tài)庫(kù)。如果強(qiáng)制使用靜態(tài)庫(kù)則需要加-static選項(xiàng)支持。 B.動(dòng)態(tài)庫(kù)生成的可執(zhí)行文件,test1不能正常的運(yùn)行。 C.鏈接靜態(tài)庫(kù)的可執(zhí)行程序明顯比鏈接動(dòng)態(tài)庫(kù)的可執(zhí)行文件大。 五、讓鏈接動(dòng)態(tài)庫(kù)的可執(zhí)行程序正常運(yùn)行。 當(dāng)系統(tǒng)加載可執(zhí)行代碼時(shí)候,能夠知道其所依賴(lài)的庫(kù)的名字,但是還需要知道絕對(duì)路勁。此時(shí)就需要系統(tǒng)動(dòng)態(tài)載入器(dynamic ?linker/loader)。 對(duì)于elf格式的可執(zhí)行程序,是由ld-linux.so*來(lái)完成的,它先后搜索elf文件的DT_RPATH段---環(huán)境變量LD_LIBRARY_PATH、/etc/ld.so.cache文件列表、/usr/lib、/lib目錄找到庫(kù)文件后將其載入內(nèi)存。 A.一種最直接的方法,就是把生成的動(dòng)態(tài)庫(kù)拷貝到/usr/lib或/lib中去。
B.使用LD_LIBRARY_PATH環(huán)境變量,這個(gè)環(huán)境變量在ubuntu操作系統(tǒng)中默認(rèn)沒(méi)有,需要手動(dòng)添加
C.動(dòng)態(tài)在安裝在其他目錄下,如果想操作系統(tǒng)能找到它,可以通過(guò)一下步驟 <1>新建并編輯/etc/ld.so.conf.d/my.conf文件,加入庫(kù)所在目錄的路徑 <2>執(zhí)行l(wèi)dconfig命令更新ld.so.cache文件
此時(shí),在執(zhí)行鏈接動(dòng)態(tài)庫(kù)的可執(zhí)行文件則可以正常運(yùn)行。 六、查看庫(kù)中的符號(hào) A.nm命令可以打印出庫(kù)中涉及到的所有符號(hào)。庫(kù)既可以是靜態(tài)庫(kù)也可以是動(dòng)態(tài)的。 常見(jiàn)的三種符號(hào): <1>在庫(kù)中被調(diào)用,但沒(méi)有在庫(kù)中定義(表明需要其他庫(kù)支持),用U表示 <2>在庫(kù)中定義的函數(shù),用T表示 <3>“弱態(tài)”符號(hào),他們雖然在庫(kù)中被定義,但是可能被其他庫(kù)中同名的符號(hào)覆蓋,用W表示。
B.ldd命令可以查看一個(gè)可執(zhí)行程序依賴(lài)的共享庫(kù)
七、動(dòng)態(tài)加載庫(kù) 用gcc -shared生成的我們稱(chēng)為動(dòng)態(tài)庫(kù)(共享庫(kù)),其中動(dòng)態(tài)庫(kù)在運(yùn)行的過(guò)程中有兩種方式 A.動(dòng)態(tài)鏈接 這種方式下,可執(zhí)行程序只是做一個(gè)動(dòng)態(tài)的鏈接,當(dāng)需要用到動(dòng)態(tài)庫(kù)中的函數(shù)時(shí),有加載器隱士的加載。 B.動(dòng)態(tài)加載 這種方式下,在可執(zhí)行程序的內(nèi)部,我們可以用dlopen()這樣的函數(shù),手動(dòng)進(jìn)行加載,dlsym()函數(shù)找到我們想要調(diào)用函數(shù)的入口地址,然后進(jìn)行調(diào)用。這種方式,在寫(xiě)插件程序中得到廣泛應(yīng)用。 相關(guān)的API:
<1>dlopen()打開(kāi)一個(gè)新的動(dòng)態(tài)庫(kù),并把它裝入內(nèi)存。該函數(shù)主要用來(lái)記載庫(kù)中的符號(hào),這些符號(hào)在編譯的時(shí)候是不知道的。 dlopen()函數(shù)需要兩個(gè)參數(shù):一個(gè)文件名和一個(gè)標(biāo)志。 A.文件名是我們之前接觸過(guò)的動(dòng)態(tài)庫(kù)的名字,如果它是一個(gè)絕對(duì)路徑,如:/home/cyg/worddir/libname.so,此時(shí)dlopen直接到指定的路徑下打開(kāi)動(dòng)態(tài)庫(kù)。 如果沒(méi)有指定路徑,僅僅指定了一個(gè)動(dòng)態(tài)庫(kù)的名字,此時(shí)dlopen將它先后搜索elf文件的DT_RPATH段、環(huán)境變量LD_LIBRARY_PATH、/etc/ld.so.cache文件列表、/lib、/usr/lib目錄找到庫(kù)文件后將其載入內(nèi)存。 B.標(biāo)志指明是否立刻計(jì)算庫(kù)的依賴(lài)性。
常常一個(gè)庫(kù)中還依賴(lài)別的庫(kù),就是這個(gè)函數(shù)實(shí)現(xiàn)的時(shí)候,調(diào)用了別的庫(kù)函數(shù)。不是在這個(gè)庫(kù)中實(shí)現(xiàn)的函數(shù)我們稱(chēng)為位定義的符號(hào)。
如果將標(biāo)志 設(shè)置為RTLD_NOW的話(huà),則會(huì)在dlopen函數(shù)返回前,將這些未定義的符號(hào)解析出來(lái)。如果設(shè)置為RTLD_LAZY,則會(huì)在需要的時(shí)候才會(huì)去解析。 返回值:dlopen()函數(shù)會(huì)返回一個(gè)句柄作為dlsym()函數(shù)的第一個(gè)參數(shù),以獲得符號(hào)在庫(kù)中的地址。使用這個(gè)地址,就可以獲得庫(kù)中特定函數(shù)的指針,并且調(diào)用裝載庫(kù)中的相應(yīng)函數(shù)。 <2>dlerror() 當(dāng)動(dòng)態(tài)鏈接庫(kù)操作函數(shù)執(zhí)行失敗時(shí),dlerror可以返回出錯(cuò)信息,返回值為NULL時(shí)表示操作函數(shù)執(zhí)行成功。 <3>void *dlsym(void *handle,char *symbol); dlsym根據(jù)動(dòng)態(tài)鏈接庫(kù)操作句柄(handle)與符號(hào)(symbol),返回符號(hào)對(duì)應(yīng)的函數(shù)的執(zhí)行代碼地址。由此地址,可以帶參數(shù)執(zhí)行相應(yīng)的函數(shù)。 如程序代碼 :int (*add)(int x,int y);//函數(shù)指針 handle = dlopen("xxx.so",RTLD_LAZY);//打開(kāi)共享庫(kù) add = dlsym(handle,"add");//獲取add函數(shù)在共享庫(kù)的地址 value = add(12,34);//調(diào)用add函數(shù) <4>int dlclose(void *handle); dlclose用于關(guān)閉指定句柄的動(dòng)態(tài)鏈接庫(kù),只有當(dāng)此動(dòng)態(tài)鏈接庫(kù)的使用計(jì)數(shù)為0時(shí),才會(huì)真正被系統(tǒng)卸載。 案例: #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; } 運(yùn)行結(jié)果:
轉(zhuǎn)載于:https://www.cnblogs.com/computer1-2-3/p/8315838.html
總結(jié)
以上是生活随笔為你收集整理的Linux 静态库 动态库的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 第五节 -- 字典
- 下一篇: 前端构建工具gulp之基本介绍