




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
1、.Linux靜態(tài)and動(dòng)態(tài)鏈接庫創(chuàng)建和使用目 錄1.概述42.靜態(tài)庫與動(dòng)態(tài)庫43.靜態(tài)庫的編寫和使用43.1概述43.2編寫最簡單的靜態(tài)庫文件53.3制作庫文件53.3.1生成目標(biāo)文件53.3.2用ar命令歸檔,格式為ar -rc53.4使用庫文件53.4.1編寫一個(gè)測試程序main.c53.4.2編譯目標(biāo)文件53.4.3生成可執(zhí)行文件63.4.4執(zhí)行可執(zhí)行文件查看效果64.動(dòng)態(tài)庫的編寫和使用64.1概述64.2編寫最簡單的動(dòng)態(tài)庫文件64.3編譯生成動(dòng)態(tài)庫 ,庫文件名以lib開頭, 以.so 結(jié)尾。74.4動(dòng)態(tài)庫的隱式調(diào)用74.4.1編寫測試文件74.4.2編譯測試程序,與靜態(tài)庫類似,要把頭文
2、件的路徑加到-I參數(shù)里面74.4.3連接生成測試程序74.4.4執(zhí)行測試程序74.5使動(dòng)態(tài)庫被系統(tǒng)共享的三種辦法84.5.1方法一:拷貝動(dòng)態(tài)鏈接庫到系統(tǒng)共享目錄下,或在系統(tǒng)共享目錄下為該動(dòng)態(tài)鏈接庫建立連接(硬連接或符號連接均可,常用符號連接)。84.5.2方法二:將動(dòng)態(tài)鏈接庫所在目錄名追加到動(dòng)態(tài)鏈接庫配置文件/etc/ld.so.conf中。84.5.3方法三:利用動(dòng)態(tài)鏈接庫管理命令ldconfig,強(qiáng)制其搜索指定目錄,并更新緩存文件,便于動(dòng)態(tài)裝入。84.5.4注意:84.5.5lconfig命令說明84.6動(dòng)態(tài)庫的顯式調(diào)用94.6.1編寫測試文件104.6.2編譯測試文件 使用-ldl選項(xiàng)指
3、明生成的對象模塊需要使用共享庫104.6.3執(zhí)行測試程序114.6.4顯式調(diào)用例子程序2114.6.5相關(guān)函數(shù)的說明如下:124.7相關(guān)的命令134.7.1nm命令-查看庫中的符號134.7.2ldd命令-檢查程序都使用到哪些共享庫134.8使用動(dòng)態(tài)庫時(shí)應(yīng)注意的其他問題144.8.1動(dòng)態(tài)庫的路徑設(shè)置144.8.2同名的靜態(tài)和動(dòng)態(tài)庫的連接順序141. 概述我們在實(shí)際編程工作中肯定會遇到這種情況:有幾個(gè)項(xiàng)目里有一些函數(shù)模塊的功能相同,實(shí)現(xiàn)代碼也相同,也是我們所說的重復(fù)代碼。比如,很多項(xiàng)目里都有一個(gè)用戶驗(yàn)證的功能。代碼段如下: /UserLogin.h文件,提供函數(shù)聲明
4、60; int IsValidUser(char* username, int namelen); /UserLogin.c文件,實(shí)現(xiàn)對用戶信息的驗(yàn)證 int IsValidUser(char* username, int namelen) int IsValid = 0; /*下面是具體的處理代碼,略去*/ return IsValid
5、0; 如果每個(gè)項(xiàng)目都保存著這兩個(gè)UserLogin.h和UserLogin.c文件,會有以下幾個(gè)弊端:1、每個(gè)項(xiàng)目里都有重復(fù)的模塊,造成代碼重復(fù)。 2、代碼的重用性不好,一旦IsValidUser的代碼發(fā)生了變化,為了保持設(shè)計(jì)的一致性,我們還要手工修改其他項(xiàng)目里的UserLogin.c文件,既費(fèi)時(shí)又費(fèi)力,還容易出錯(cuò)。庫文件就是對公共代碼的一種組織形式。為了解決上面兩個(gè)弊端,就提出了用庫文件存放公共代碼的解決方案,其要點(diǎn)就是把公共的(也就是可以被多次復(fù)用的)目標(biāo)代碼從項(xiàng)目中分離出來,統(tǒng)一存放到庫文件中,項(xiàng)目要用到這些代碼的時(shí)候,在編譯或者運(yùn)行的時(shí)候從庫文件中取得目標(biāo)代碼即可
6、。庫文件又分兩種:靜態(tài)庫和動(dòng)態(tài)庫。2. 靜態(tài)庫與動(dòng)態(tài)庫如果程序是在編譯時(shí)加載庫文件的,就是使用了靜態(tài)庫。如果是在運(yùn)行時(shí)加載目標(biāo)代碼,就成為動(dòng)態(tài)庫。換句話說,如果是使用靜態(tài)庫,則靜態(tài)庫代碼在編譯時(shí)就拷貝到了程序的代碼段,程序的體積會膨脹。如果使用動(dòng)態(tài)庫,則程序中只保留庫文件的名字和函數(shù)名,在運(yùn)行時(shí)去查找?guī)煳募秃瘮?shù)體,程序的體積基本變化不大。靜態(tài)庫的原則是“以空間換時(shí)間”,增加程序體積,減少運(yùn)行時(shí)間;動(dòng)態(tài)庫則是“以時(shí)間換空間”,增加了運(yùn)行時(shí)間,但減少了程序本身的體積。3. 靜態(tài)庫的編寫和使用3.1 概述 靜態(tài)庫文件的擴(kuò)展名一般為.a,其編寫步驟很簡單。編寫函數(shù)代碼編譯生成各目標(biāo)文件用a
7、r文件對目標(biāo)文件歸檔,生成靜態(tài)庫文件。注意歸檔文件名必須以lib打頭。使用要點(diǎn):在gcc 的-I參數(shù)后加上靜態(tài)庫頭文件的路徑。在gcc 的-L參數(shù)后加上庫文件所在目錄在gcc 的-l參數(shù)后加上庫文件名,但是要去掉lib和.a擴(kuò)展名。比如庫文件名是libtest.a 那么參數(shù)就是 -l test3.2 編寫最簡單的靜態(tài)庫文件編寫如下兩個(gè)文件,注意放在同一目錄中myalib.h /靜態(tài)庫頭文件myalib.c /靜態(tài)庫實(shí)現(xiàn)文件 /myalib.h 文件的內(nèi)容void test();/myalib.c 文件的
8、內(nèi)容#inlcude void test() printf("testn");3.3 制作庫文件3.3.1 生成目標(biāo)文件gcc -c myalib.c 執(zhí)行完后會生成一個(gè)myalib.o文件3.3.2 用ar命令歸檔,格式為ar -rc 再次提醒,歸檔文件名一定要以lib打頭, .a結(jié)尾。 ar -rc libtest.a myalib.o 執(zhí)行完后會生成一個(gè)libtest.a文件3.4 使用庫文件 3.4.1 編寫一個(gè)測試程序main.c內(nèi)容為/main.
9、c 測試靜態(tài)庫調(diào)用的程序#include "myalib.h" /要把函數(shù)的頭文件包含進(jìn)來,否則編譯時(shí)會報(bào)錯(cuò) int main(int argc,char* argv) test(); return 0;3.4.2 編譯目標(biāo)文件注意:要把靜態(tài)庫頭文件的路徑加到-I參數(shù)里面gcc -I /root/exercise -o main.o -c main.c現(xiàn)在生成了一個(gè)main.o文件3.4.3 生成可執(zhí)行文件注意:要把靜態(tài)庫文件的路徑加到-L參數(shù)里面,把庫文件名(去掉打頭的lib和結(jié)尾的
10、.a)加到-l參數(shù)后面。如下面所示gcc -o main -L/root/exercise main.o ltest此時(shí)就會生成一個(gè)名為main的可執(zhí)行文件另外,注意- l參數(shù)好象應(yīng)該加到輸入文件名的后面,否則會報(bào)錯(cuò)。3.4.4 執(zhí)行可執(zhí)行文件查看效果執(zhí)行./main, 輸出 test,說明執(zhí)行成功。 4. 動(dòng)態(tài)庫的編寫和使用4.1 概述 動(dòng)態(tài)庫一般以.so結(jié)尾,就是shared object的意思. 其基本生成步驟為1) 編寫函數(shù)代碼2) 編譯生成動(dòng)態(tài)庫文件,要加上 -shared 和 -fp
11、ic 選項(xiàng) ,庫文件名以lib開頭, 以.so 結(jié)尾。使用方式分為兩種: 隱式調(diào)用和顯式調(diào)用 :隱式調(diào)用類似于靜態(tài)庫的使用,但需修改動(dòng)態(tài)鏈接庫的配置文件/etc/ld.so.conf;顯示調(diào)用則是在主程序里使用dlopen、dlsym、dlerror、dlclose等系統(tǒng)函數(shù)。生成動(dòng)態(tài)庫用gcc來完成,由于可能存在多個(gè)版本,因此通常指定版本號: $gcc -shared -Wl,-soname,libhello.so.1 -o libhello.so.1.0 hello.o 另外再建立兩個(gè)符號連接: $ln -s libhello.so.1.0 libh
12、ello.so.1$ln -s libhello.so.1 libhello.so這樣一個(gè)libhello的動(dòng)態(tài)連接庫就生成了。最重要的是傳gcc -shared 參數(shù)使其生成是動(dòng)態(tài)庫而不是普通執(zhí)行程序。 -Wl 表示后面的參數(shù)也就是-soname,libhello.so.1直接傳給連接器ld進(jìn)行處理。實(shí)際上,每一個(gè)庫都有一個(gè)soname,當(dāng)連接器發(fā)現(xiàn)它正在查找的程序庫中有這樣一個(gè)名稱,連接器便會將soname嵌入連結(jié)中的二進(jìn)制文件內(nèi),而不是它正在運(yùn)行的實(shí)際文件名,在程序執(zhí)行期間,程序會查找擁有 soname名字的文件,而不是庫的文件名,換句話說,soname是庫的區(qū)分標(biāo)志。
13、0;這樣做的目的主要是允許系統(tǒng)中多個(gè)版本的庫文件共存,習(xí)慣上在命名庫文件的時(shí)候通常與soname相同 。libxxxx.so.major.minor其中,xxxx是庫的名字,major是主版本號,minor 是次版本號具體的調(diào)用方式會在 "五、動(dòng)態(tài)庫的調(diào)用" 中詳細(xì)說明.4.2 編寫最簡單的動(dòng)態(tài)庫文件為了便于對照, 我們?nèi)匀徊捎渺o態(tài)庫中的文件做例子。編寫如下兩個(gè)文件,注意放在同一目錄中 myalib.h /靜態(tài)庫頭文件myalib.c /靜態(tài)庫實(shí)現(xiàn)文件/myalib.h 文件的內(nèi)容vo
14、id test();/myalib.c 文件的內(nèi)容#inlcude void test() printf("testn");4.3 編譯生成動(dòng)態(tài)庫 ,庫文件名以lib開頭, 以.so 結(jié)尾。gcc -fpic -shared -o libtest.so myalib.c此時(shí)就生成一個(gè)libtest.so文件4.4 動(dòng)態(tài)庫的隱式調(diào)用隱式調(diào)用的含義是代碼里不出現(xiàn)庫文件名,就是說這個(gè)代碼和調(diào)用靜態(tài)庫的代碼是類似的。4.4.1 編寫測試文件/main.c 測試動(dòng)態(tài)庫隱式調(diào)用的程序#include "
15、;myalib.h" /要把函數(shù)的頭文件包含進(jìn)來,否則編譯時(shí)會報(bào)錯(cuò)int main(int argc,char* argv) test(); return 0;4.4.2 編譯測試程序,與靜態(tài)庫類似,要把頭文件的路徑加到-I參數(shù)里面gcc -I /root/exercise -o main.o -c main.c現(xiàn)在生成了一個(gè)main.o文件4.4.3 連接生成測試程序gcc -o main -L/root/exercise main.o ltest現(xiàn)在生成了一個(gè)main文件4.4.4 執(zhí)行測試程序./main
16、60;此時(shí)出現(xiàn)提示./main: error while loading shared libraries: libtest.so: cannot open shared object file: No such file or directory。 這個(gè)原因就是程序運(yùn)行時(shí)并不知道動(dòng)態(tài)庫所在的路徑,因此自然找不到。解決這個(gè)問題的辦法有三種。見下節(jié)4.5 使動(dòng)態(tài)庫被系統(tǒng)共享的三種辦法4.5.1 方法一:拷貝動(dòng)態(tài)鏈接庫到系統(tǒng)共享目錄下,或在系統(tǒng)共享目錄下為該動(dòng)態(tài)鏈接庫建立連接(硬連接或符號連接均可,常用符號連接)。這里說的系統(tǒng)共享目錄,指的是LINUX動(dòng)態(tài)鏈接庫存放的目錄,包括/lib
17、,/usr/lib以及/etc/ld.so.conf文件內(nèi)所列的一系列目錄.實(shí)例:執(zhí)行# cp libtest.so /lib# ldconfig 或:# ln -s pwd/libtest.so /lib# ldconfig注意:pwd前后有兩個(gè)反引號,其目的是取得pwd命令的輸出,即當(dāng)前目錄.此時(shí)再執(zhí)行main,即可成功.4.5.2 方法二:將動(dòng)態(tài)鏈接庫所在目錄名追加到動(dòng)態(tài)鏈接庫配置文件/etc/ld.so.conf中。# pwd >> /etc/ld.so.conf # ldconfig 此時(shí)再執(zhí)行main,即可成功.4.
18、5.3 方法三:利用動(dòng)態(tài)鏈接庫管理命令ldconfig,強(qiáng)制其搜索指定目錄,并更新緩存文件,便于動(dòng)態(tài)裝入。# ldconfig pwd 此時(shí)再執(zhí)行main,即可成功.4.5.4 注意:第三種方法雖然有效,但效果是暫時(shí)的,供程序測試還可以,一旦再度運(yùn)行l(wèi)dconfig,文件內(nèi)容可能改變,所需的動(dòng)態(tài)鏈接庫可能不被系統(tǒng)共享了。而且無論哪種辦法,其實(shí)質(zhì)都是用ldconfig命令把動(dòng)態(tài)庫文件。所在路徑加入到系統(tǒng)庫列表中,(前兩種永久,第三種臨時(shí)) 。4.5.5 ldconfig命令說明ldconfig是一個(gè)動(dòng)態(tài)鏈接庫管理命令4.5.5.1 路徑ldconfig在/sbin里面。4.5
19、.5.2 用途linux下的共享庫機(jī)制采用了類似于高速緩存的機(jī)制,將庫信息保存在/etc/ld.so.cache里邊。程序連接的時(shí)候首先從這個(gè)文件里邊查找,然后再到ld.so.conf的路徑里邊去詳細(xì)找。這就是為什么修改了ld.so.conf要重新運(yùn)行一下ldconfig的原因ldconfig 命令的用途,主要是在默認(rèn)搜尋目錄(/lib和/usr/lib)以及動(dòng)態(tài)庫配置文件/etc/ld.so.conf內(nèi)所列的目錄下,搜索出可共享的動(dòng)態(tài) 鏈接庫(格式如前介紹,lib*.so*),進(jìn)而創(chuàng)建出動(dòng)態(tài)裝入程序(ld.so)所需的連接和緩存文件.緩存文件默認(rèn)為 /etc/ld.s
20、o.cache,此文件保存已排好序的動(dòng)態(tài)鏈接庫名字列表.4.5.5.3 用法ldconfig通常在系統(tǒng)啟動(dòng)時(shí)運(yùn)行,而當(dāng)用戶安裝了一個(gè)新的動(dòng)態(tài)鏈接庫時(shí),就需要手工運(yùn)行這個(gè)命令.ldconfig命令行用法如下:ldconfig -v|-verbose -n -N -X -f CONF -C CACHE -r ROOT -l -p|-print-cache -c FORMAT -format=FORMAT -V -?|-help|-usage
21、 path.ldconfig可用的選項(xiàng)說明如下:(1) -v或-verbose : 用此選項(xiàng)時(shí),ldconfig將顯示正在掃描的目錄及搜索到的動(dòng)態(tài)鏈接庫,還有它所創(chuàng)建的連接的名字.(2) -n : 用此選項(xiàng)時(shí),ldconfig僅掃描命令行指定的目錄,不掃描默認(rèn)目錄(/lib,/usr/lib),也不掃描配置文件/etc/ld.so.conf所列的目錄.(3) -N : 此選項(xiàng)指示ldconfig不重建緩存文件(/etc/ld.so.cache).若未用-X選項(xiàng),ldconfig照常更新文件的連接
22、.(4) -X : 此選項(xiàng)指示ldconfig不更新文件的連接.若未用-N選項(xiàng),則緩存文件正常更新.(5) -f CONF : 此選項(xiàng)指定動(dòng)態(tài)鏈接庫的配置文件為CONF,系統(tǒng)默認(rèn)為/etc/ld.so.conf.(6) -C CACHE : 此選項(xiàng)指定生成的緩存文件為CACHE,系統(tǒng)默認(rèn)的是/etc/ld.so.cache,此文件存放已排好序的可共享的動(dòng)態(tài)鏈接庫的列表.(7) -r ROOT : 此選項(xiàng)改變應(yīng)用程序的根目錄為ROOT(是調(diào)用ch
23、root函數(shù)實(shí)現(xiàn)的).選擇此項(xiàng)時(shí),系統(tǒng)默認(rèn)的配置文件 /etc/ld.so.conf,實(shí)際對應(yīng)的為 ROOT/etc/ld.so.conf.如用-r /usr/zzz時(shí),打開配置文件 /etc/ld.so.conf時(shí),實(shí)際打開的是/usr/zzz/etc/ld.so.conf文件.用此選項(xiàng),可以大大增加動(dòng)態(tài)鏈接庫管理的靈活性.(8) -l : 通常情況下,ldconfig搜索動(dòng)態(tài)鏈接庫時(shí)將自動(dòng)建立動(dòng)態(tài)鏈接庫的連接.選擇此項(xiàng)時(shí),將進(jìn)入專家模式,需要手工設(shè)置連接.一般用戶不用此項(xiàng).(9) -p或-print-cache :
24、60;此選項(xiàng)指示ldconfig打印出當(dāng)前緩存文件所保存的所有共享庫的名字.(10) -c FORMAT 或 -format=FORMAT : 此選項(xiàng)用于指定緩存文件所使用的格式,共有三種: ld(老格式),new(新格式)和compat(兼容格式,此為默認(rèn)格式).(11) -V : 此選項(xiàng)打印出ldconfig的版本信息,而后退出.(12) -? 或 -help 或 -usage : 這三個(gè)選項(xiàng)作用相同,都是讓ldconfi
25、g打印出其幫助信息,而后退出.4.6 動(dòng)態(tài)庫的顯式調(diào)用顯式調(diào)用的含義是代碼出現(xiàn)庫文件名,用戶需要自己去打開和管理庫文件。其要點(diǎn)為:3) dlfcn.h系統(tǒng)頭文件包含進(jìn)來4) 用dlopen函數(shù)打開庫文件,并指定打開方式5) 用dlerror()函數(shù)測試是否打開成功,并進(jìn)行錯(cuò)誤處理;6) 用dlsym獲得函數(shù)地址,存放在一個(gè)函數(shù)指針中7) 用獲得的函數(shù)指針進(jìn)行函數(shù)調(diào)用。8) 程序結(jié)束時(shí)用dlclose關(guān)閉打開的動(dòng)態(tài)庫,防止資源泄露。9) 用ldconfig工具把動(dòng)態(tài)庫的路徑加到系統(tǒng)庫列表中4.6.1 編寫測試文件/main.c 測試動(dòng)態(tài)庫顯式調(diào)用的程序#include &
26、#160; /用于動(dòng)態(tài)庫管理的系統(tǒng)頭文件 #include "myalib.h" /要把函數(shù)的頭文件包含進(jìn)來,否則編譯時(shí)會報(bào)錯(cuò) int main(int argc,char* argv) /聲明對應(yīng)的函數(shù)的函數(shù)指針 void (*pTest)(); /加載動(dòng)態(tài)庫 void *pdlHandle = dlopen("libtest.so", RTLD_LAZY);
27、60; /錯(cuò)誤處理 if(pdlHandle = NULL ) printf("Failed load libraryn"); return -1; char* pszErr = dlerror();
28、160; if(pszErr != NULL) printf("%sn", pszErr); return -1; /獲取函數(shù)的地址 pTest = dlsym(pdlHandle, "test&qu
29、ot;); pszErr = dlerror(); if(pszErr != NULL) printf("%sn", pszErr); dlclose(pdlHandle); return -1
30、; /實(shí)現(xiàn)函數(shù)調(diào)用 (*pTest)(); /程序結(jié)束時(shí)關(guān)閉動(dòng)態(tài)庫 dlclose(pdlHandle); return 0; 4.6.2 編譯測試文件 使用-ldl選項(xiàng)指明生成的對象模塊需要使用共享庫gcc -o main -ldl main.c執(zhí)行完后就生成了一個(gè)main文件4.6.3 執(zhí)行測試程序執(zhí)行 ./main 輸出test 說明
31、成功。4.6.4 顯式調(diào)用例子程序2/*FileName: main2.cDescription: test static/dynamic libraryAuthor: HCJDate : 2005-5-7*/#include<stdio.h>#include<dlfcn.h>int main(int argc, char* argv) /define function pointor int (*pStrlenFun)(char* pStr);
32、60; /聲明對應(yīng)的函數(shù)的函數(shù)指針 int (*pStrnlenFun)(char* pStr, int ulMaxLen); char str = "hello world" unsigned long ulLength = 0; void *pdlHandle; char *pszErr; pdlHandle = dlopen("./libstr.
33、so", RTLD_LAZY); /加載鏈接庫/libstr.so if(!pdlHandle) printf("Failed load libraryn"); pszErr = dlerror(); if(pszErr != NULL) &
34、#160; printf("%sn", pszErr); return 0; /get function from lib pStrlenFun = dlsym(pdlHandle, "Strlen"); /獲取函數(shù)的地址 pszErr = dlerror();
35、0; if(pszErr != NULL) printf("%sn", pszErr); return 0; pStrnlenFun = dlsym(pdlHandle, "StrNlen"); pszErr = dle
36、rror(); if(pszErr != NULL) printf("%sn", pszErr); return 0; printf("The string is : %sn", str); ulLength
37、 = pStrlenFun(str); /調(diào)用相關(guān)的函數(shù) printf("The string length is : %d(use Strlen)n", ulLength); ulLength = pStrnlenFun(str, 10); printf("The string length is : %d(use StrNlen)n", ulLength); dlclose(pdlHandle); &
38、#160; return 0; gcc -o mian2 -ldl main2.c用gcc編譯對應(yīng)的源文件生成可執(zhí)行文件,-ldl選項(xiàng),表示生成的對象模塊需要使用共享庫。執(zhí)行對應(yīng)得文件同樣可以得到正確的結(jié)果。4.6.5 相關(guān)函數(shù)的說明如下:4.6.5.1 dlopen()dlopen的第一個(gè)參數(shù)為共享庫的名稱,將會在下面位置查找指定的共享庫。l 環(huán)境變量LD_LIBRARY_PATH列出的用分號間隔的所有目錄。l 文件/etc/ld.so.cache中找到的庫的列表,由ldconfig命令刷新。l 目錄usr/lib。l 目錄/lib。l 當(dāng)前目錄
39、。第二個(gè)參數(shù)為打開共享庫的方式。有兩個(gè)取值l RTLD_NOW:將共享庫中的所有函數(shù)加載到內(nèi)存l RTLD_LAZY:會推后共享庫中的函數(shù)的加載操作,直到調(diào)用dlsym()時(shí)方加載某函數(shù)4.6.5.2 dlsym()調(diào)用dlsym時(shí),利用dlopen()返回的共享庫的phandle以及函數(shù)名稱作為參數(shù),返回要加載函數(shù)的入口地址。4.6.5.3 dlerror()該函數(shù)用于檢查調(diào)用共享庫的相關(guān)函數(shù)出現(xiàn)的錯(cuò)誤。 這樣我們就用簡單的例子說明了在Linux下靜態(tài)/動(dòng)態(tài)庫的創(chuàng)建和使用。 4.6.5.4 dlclose()關(guān)閉動(dòng)態(tài)庫.4.7 相關(guān)的命令4.7.1 nm命令-
40、查看庫中的符號有時(shí)候可能需要查看一個(gè)庫中到底有哪些函數(shù),nm命令可以打印出庫中的涉及到的所有符號。庫既可以是靜態(tài)的也可以是動(dòng)態(tài)的。nm列出的符號有很多,常見的有三種,一種是在庫中被調(diào)用,但并沒有在庫中定義(表明需要其他庫支持),用U表示;一種是庫中定義的函數(shù),用T表示,這是最常見的;另外一種是所謂的“弱態(tài)”符號,它們雖然在庫中被定義,但是可能被其他庫中的同名符號覆蓋,用W表示。例如,假設(shè)開發(fā)者希望知道上央提到的hello庫中是否定義了 printf(): $nm libhello.so |grep printfU printfU表示符號printf被引用,但是并沒有在函數(shù)內(nèi)定義,由此
41、可以推斷,要正常使用hello庫,必須有其它庫支持,再使用ldd命令查看hello依賴于哪些庫: $ldd hello libc.so.6=>/lib/libc.so.6(0x400la000) /lib/ld-linux.so.2=>/lib/ld-linux.so.2 (0x40000000) 從上面的結(jié)果可以繼續(xù)查看printf最終在哪里被定義,有興趣可以go on 4.7.2 ldd命令-檢查程序都使用到哪些共享庫4.7.2.1 ldd命令行用法如下: ldd -version -v|-verbose -d|-da
42、ta-relocs -r|-function-relocs -help FILE. 各選項(xiàng)說明如下: (1) -version : 此選項(xiàng)用于打印出ldd的版本號. (2) -v 或 -verbose : 此選項(xiàng)指示ldd輸出關(guān)于所依賴的動(dòng)態(tài)鏈接庫的盡可能詳細(xì)的信息. (3) -d 或 -data-relocs : 此選項(xiàng)執(zhí)行重定位,并且顯示不存在的函數(shù). (4) -r 或 -function-relocs : 此選項(xiàng)執(zhí)行數(shù)據(jù)對象與函數(shù)的重定位,同時(shí)報(bào)告不存在的對象.
43、60; (5) -help : 此選項(xiàng)用于打印出ldd的幫助信息. 我們一般用-v選項(xiàng).4.7.2.2 實(shí)例4.7.2.2.1 用靜態(tài)庫連接時(shí)的結(jié)果 #ldd mainlibc.so.6 => /lib/tls/libc.so.6 (0xb74ad000)/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0xb75eb000)可見使用靜態(tài)庫時(shí),由于庫已經(jīng)被編譯成程序的一部分,因此ldd的輸出中就只有用到的系統(tǒng)庫。4.7.2.2.2 用動(dòng)態(tài)庫隱式連接時(shí)的結(jié)果 li
44、btest.so => /root/exercise/libtest.so (0xb75e2000)libc.so.6 => /lib/tls/libc.so.6 (0xb74ab000)/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0xb75eb000)可見隱式使用動(dòng)態(tài)庫時(shí),所有用到的動(dòng)態(tài)庫(包括系統(tǒng)和用戶的)都會被顯示出來。4.7.2.2.3 動(dòng)態(tài)庫顯式連接時(shí)的結(jié)果ldd mainlibdl.so.2 => /lib/libdl.so.2 (0xb75e1000)libc.so.6 => /lib/tls/libc.so.
45、6 (0xb74aa000)/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0xb75eb000)可見顯式使用動(dòng)態(tài)庫時(shí),程序中不再保存運(yùn)行時(shí)打開動(dòng)態(tài)庫的信息,只保留用到的系統(tǒng)庫的信息. 這個(gè)與使用靜態(tài)庫時(shí)的輸出是類似的.4.8 使用動(dòng)態(tài)庫時(shí)應(yīng)注意的其他問題4.8.1 動(dòng)態(tài)庫的路徑設(shè)置無論是動(dòng)態(tài)庫的顯式調(diào)用還是隱式調(diào)用,都需要用ldconfig工具將動(dòng)態(tài)庫的路徑加到系統(tǒng)庫列表中,否則運(yùn)行時(shí)會出錯(cuò)。 為了讓執(zhí)行程序順利找到動(dòng)態(tài)庫,有三種方法: (1)把庫拷貝到/usr/lib和/lib目錄下。(2)在LD_LIBRARY_PA
46、TH環(huán)境變量中加上庫所在路徑。例如動(dòng)態(tài)庫libhello.so在/home/ting/lib目錄下,以bash為例,使用命令:$export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/ting/lib(3) 修改/etc/ld.so.conf文件,把庫所在的路徑加到文件末尾,并執(zhí)行l(wèi)dconfig刷新。這樣,加入的目錄下的所有庫文件都可見。4.8.2 同名的靜態(tài)和動(dòng)態(tài)庫的連接順序當(dāng)要使用靜態(tài)的程序庫時(shí),連接器會找出程序所需的函數(shù),然后將它們拷貝到執(zhí)行文件,由于這種拷貝是完整的,所以一旦連接成功,靜態(tài)程序庫也就不再需要了。然而,對動(dòng)態(tài)庫而言,就不是這樣。動(dòng)態(tài)庫
47、會在執(zhí)行程序內(nèi)留下一個(gè)標(biāo)記指明當(dāng)程序執(zhí)行時(shí),首先必須載入這個(gè)庫。由于動(dòng)態(tài)庫節(jié)省空間,linux下進(jìn)行連接的缺省操作是首先連接動(dòng)態(tài)庫,也就是說,如果同時(shí)存在靜態(tài)和動(dòng)態(tài)庫,不特別指定的話,將與動(dòng)態(tài)庫相連接。4.8.2.1 例子現(xiàn)在假設(shè)有一個(gè)叫hello的程序開發(fā)包,它提供一個(gè)靜態(tài)庫libhello.a 一個(gè)動(dòng)態(tài)庫libhello.so,一個(gè)頭文件hello.h,頭文件中提供sayhello()這個(gè)函數(shù) /* hello.h */void sayhello();另外還有一些說明文檔。這一個(gè)典型的程序開發(fā)包結(jié)構(gòu) 4.8.2.1.1 與動(dòng)態(tài)庫連接 linux默認(rèn)的就是與動(dòng)態(tài)
48、庫連接,下面這段程序testlib.c使用hello庫中的sayhello()函數(shù) /*testlib.c*/#include#include int main() sayhello();return 0; 使用如下命令進(jìn)行編譯 $gcc -c testlib.c -o testlib.o 用如下命令連接: $gcc testlib.o -lhello -o testlib在連接時(shí)要注意,假設(shè)libhello.so 和libhello.a都在缺省的庫搜索路徑下/usr/lib下,如果在其它位置要加上-L參數(shù) 4.8
49、.2.1.2 與靜態(tài)庫連接與靜態(tài)庫連接與麻煩一些,主要是參數(shù)問題。還是上面的例子:$gcc testlib.o -o testlib -WI,-Bstatic -lhello 注:這個(gè)特別的"-WI,-Bstatic"參數(shù),實(shí)際上是傳給了連接器ld.指示它與靜態(tài)庫連接,如果系統(tǒng)中只有靜態(tài)庫當(dāng)然就不需要這個(gè)參數(shù)了。如果要和多個(gè)庫相連接,而每個(gè)庫的連接方式不一樣,比如上面的程序既要和libhello進(jìn)行靜態(tài)連接,又要和libbye進(jìn)行動(dòng)態(tài)連接,其命令應(yīng)為: $gcc testlib.o -o testlib -WI,-Bstatic -lhello -WI,
50、-Bdynamic -lbye 5. 其他相關(guān)知識5.1 指針函數(shù)和函數(shù)指針的區(qū)別這兩個(gè)概念都是簡稱,指針函數(shù)是指帶指針的函數(shù),即本質(zhì)是一個(gè)函數(shù)。我們知道函數(shù)都又有返回類型(如果不返回值,則為無值型),只不過指針函數(shù)返回類型是某一類型的指針。5.2 函數(shù)指針5.2.1 概述函數(shù)指針是指向函數(shù)的指針變量。 因而“函數(shù)指針”本身首先應(yīng)是指針變量,只不過該指針變量指向函數(shù)。這正如用指針變量可指向整型變量、字符型、數(shù)組一樣,這里是指向函數(shù)。如前所述,C在編譯時(shí),每一個(gè)函數(shù)都有一個(gè)入口地址,該入口地址就是函數(shù)指針?biāo)赶虻牡刂贰S辛酥赶蚝瘮?shù)的指針變量后,可用該指針變量調(diào)用函數(shù),就如同用指針變量可
51、引用其他類型變量一樣,在這些概念上是一致的。函數(shù)指針有兩個(gè)用途:調(diào)用函數(shù)和做函數(shù)的參數(shù)。5.2.2 方法函數(shù)指針的聲明方法為: 數(shù)據(jù)類型標(biāo)志符 (指針變量名) (形參列表); 注1:“函數(shù)類型”說明函數(shù)的返回類型,由于“()”的優(yōu)先級高于“*”,所以指針變量名外的括號必不可少,后面的“形參列表”表示指針變量指向的函數(shù)所帶的參數(shù)列表。例如: int func(int x); /* 聲明一個(gè)函數(shù) */ int (*f) (int x); /* 聲明一個(gè)函數(shù)指針 */ f=func; /* 將func函數(shù)的首地址賦給指針f */ 賦值時(shí)函數(shù)func不帶括號,也不帶參數(shù),由于func代表函數(shù)的首地址,
52、因此經(jīng)過賦值以后,指針f就指向函數(shù)func(x)的代碼的首地址。 注2:函數(shù)括號中的形參可有可無,視情況而定。 下面的程序說明了函數(shù)指針調(diào)用函數(shù)的方法: 5.2.3 例一 #include<stdio.h> int max(int x,int y) return(x>y?x:y); void main() int (*ptr)(int, int); int a,b,c; ptr=max; scanf("%d,%d",&a,&b); c=(*ptr)(a,b); printf("a=%d,b=%d,max=%d",a,b,
53、c); ptr是指向函數(shù)的指針變量,所以可把函數(shù)max()賦給ptr作為ptr的值,即把max()的入口地址賦給ptr,以后就可以用ptr來調(diào)用該函數(shù),實(shí)際上ptr和max都指向同一個(gè)入口地址,不同就是ptr是一個(gè)指針變量,不像函數(shù)名稱那樣是死的,它可以指向任何函數(shù),就看你想怎么做了。在程序中把哪個(gè)函數(shù)的地址賦給它,它就指向哪個(gè)函數(shù)。而后用指針變量調(diào)用它,因此可以先后指向不同的函數(shù)。不過注意,指向函數(shù)的指針變量沒有+和-運(yùn)算,用時(shí)要小心。 不過,在某些編譯器中這是不能通過的。這個(gè)例子的補(bǔ)充如下。 應(yīng)該是這樣的: 1.定義函數(shù)指針類型: typedef int (*fun_ptr)(int,in
54、t); 2.申明變量,賦值: fun_ptr max_func=max; 也就是說,賦給函數(shù)指針的函數(shù)應(yīng)該和函數(shù)指針?biāo)傅暮瘮?shù)原型是一致的。 5.2.4 例二 #include<stdio.h> void FileFunc() printf("FileFuncn"); void EditFunc() printf("EditFuncn"); void main() typedef void (*funcp)(); funcp=FileFunc; funcp(); funcp=EditFunc; funcp(); 5.2.5 簡單的函數(shù)指針的應(yīng)
55、用。/形式1:返回類型(*函數(shù)名)(參數(shù)表) char (*pFun)(int); char glFun(int a) return; void main() pFun = glFun; (*pFun)(2); 第一行定義了一個(gè)指針變量pFun。首先我們根據(jù)前面提到的“形式1”認(rèn)識到它是一個(gè)指向某種函數(shù)的指針,這種函數(shù)參數(shù)是一個(gè)int型,返回值是char類型。只有第一句我們還無法使用這個(gè)指針,因?yàn)槲覀冞€未對它進(jìn)行賦值。
56、60; 第二行定義了一個(gè)函數(shù)glFun()。該函數(shù)正好是一個(gè)以int為參數(shù)返回char的函數(shù)。我們要從指針的層次上理解函數(shù)函數(shù)的函數(shù)名實(shí)際上就是一個(gè)指針,函數(shù)名指向該函數(shù)的代碼在內(nèi)存中的首地址。 然后就是可愛的main()函數(shù)了,它的第一句您應(yīng)該看得懂了它將函數(shù)glFun的地址賦值給變量pFun。main()函數(shù)的第二句中“*pFun”顯然是取pFun所指向地址的內(nèi)容,當(dāng)然也就是取出了函數(shù)glFun()的內(nèi)容,然后給定參數(shù)為2。5.2.6 使用typedef更直觀更方便。 /形式2:typedef 返回類型(*新類型)(參數(shù)表)typedef char (*PTRFUN)(int); PTRFUN pFun; ch
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 公寓物業(yè)管理管理制度
- 密室逃脫運(yùn)營管理制度
- 化工車間怎樣管理制度
- 帳篷露營改造方案(3篇)
- 沉井流砂處理方案(3篇)
- 國際學(xué)校員工管理制度
- 農(nóng)村門面開發(fā)方案(3篇)
- 工地現(xiàn)場統(tǒng)籌管理制度
- 宿舍木門維修方案(3篇)
- 商城疫情期間管理制度
- 職業(yè)技術(shù)學(xué)院2024級人工智能技術(shù)應(yīng)用專業(yè)人才培養(yǎng)方案
- 2025年中考數(shù)學(xué)必考基礎(chǔ)知識點(diǎn)中考總復(fù)習(xí)總結(jié)歸納
- 2025-2030中國合成生物學(xué)行業(yè)市場發(fā)展趨勢與前景展望戰(zhàn)略分析研究報(bào)告
- 2025至2030年酒制品紙托盤項(xiàng)目投資價(jià)值分析報(bào)告
- 2024園藝師考試田間管理試題及答案
- 2025年安徽物理中考模擬練習(xí)卷(含答案)
- 2024年全球及中國便攜式步態(tài)和姿勢分析系統(tǒng)行業(yè)頭部企業(yè)市場占有率及排名調(diào)研報(bào)告
- 畢業(yè)設(shè)計(jì)(論文)-垂直循環(huán)立體車庫機(jī)械設(shè)計(jì)
- 2025-2030中國劃船機(jī)行業(yè)市場發(fā)展分析及前景趨勢與投資研究報(bào)告
- 2024年度無人駕駛技術(shù)課件
- 三菱D700變頻器說明書
評論
0/150
提交評論