C语言的本质(35)——共享库
庫(kù)用于將相似函數(shù)打包在一個(gè)單元中。然后這些單元就可為其他開(kāi)發(fā)人員所共享,并因此有了模塊化編程這種說(shuō)法— 即,從模塊中構(gòu)建程序。Linux支持兩種類型的庫(kù),每一種庫(kù)都有各自的優(yōu)缺點(diǎn)。靜態(tài)庫(kù)包含在編譯時(shí)靜態(tài)綁定到一個(gè)程序的函數(shù)。動(dòng)態(tài)庫(kù)則不同,它是在加載應(yīng)用程序時(shí)被加載的,而且它與應(yīng)用程序是在運(yùn)行時(shí)綁定的。
?使用共享庫(kù)的方法有兩種:您既可以在運(yùn)行時(shí)動(dòng)態(tài)鏈接庫(kù),也可以動(dòng)態(tài)加載庫(kù)并在程序控制之下使用它們。本文對(duì)這兩種方法都做了探討。
?靜態(tài)庫(kù)較適宜于較小的應(yīng)用程序,因?yàn)樗鼈冎恍枰钚∠薅鹊暮瘮?shù)。而對(duì)于需要多個(gè)庫(kù)的應(yīng)用程序來(lái)說(shuō),則適合使用共享庫(kù),因?yàn)樗鼈兛梢詼p少應(yīng)用程序?qū)?nèi)存(包括運(yùn)行時(shí)中的磁盤占用和內(nèi)存占用)的占用。這是因?yàn)槎鄠€(gè)應(yīng)用程序可以同時(shí)使用一個(gè)共享庫(kù);因此,每次只需要在內(nèi)存上復(fù)制一個(gè)庫(kù)。要是靜態(tài)庫(kù)的話,每一個(gè)運(yùn)行的程序都要有一份庫(kù)的副本。
?GNU/Linux 提供兩種處理共享庫(kù)的方法(每種方法都源于Sun Solaris)。您可以動(dòng)態(tài)地將程序和共享庫(kù)鏈接并讓 Linux 在執(zhí)行時(shí)加載庫(kù)(如果它已經(jīng)在內(nèi)存中了,則無(wú)需再加載)。另外一種方法是使用一個(gè)稱為動(dòng)態(tài)加載的過(guò)程,這樣程序可以有選擇地調(diào)用庫(kù)中的函數(shù)。使用動(dòng)態(tài)加載過(guò)程,程序可以先加載一個(gè)特定的庫(kù)(已加載則不必),然后調(diào)用該庫(kù)中的某一特定函數(shù)(圖 2 展示了這兩種方法)。這是構(gòu)建支持插件的應(yīng)用程序的一個(gè)普遍的方法。我稍候?qū)⒃诒疚奶接懖⑹痉对搼?yīng)用程序編程接口(API)。
?
Linux下的共享庫(kù)類似windows下的dll,共命令約定如下:
?
靜態(tài)庫(kù)一般由字母lib 開(kāi)頭,并有 .a 的擴(kuò)展名,而共享對(duì)象有兩個(gè)不同的名稱:soname 和 real name。
?
soname 包含前綴 "lib",然后緊跟庫(kù)名,其次是".so"(后面緊跟另一個(gè)圓點(diǎn)),以及表明主版本號(hào)的數(shù)字。
?
soname 可以由前綴的路徑信息來(lái)限定。realname 是包含庫(kù)的已編譯代碼的真正文件名。
?
real name 在 soname 后添加一個(gè)圓點(diǎn)、小的數(shù)字、另外一個(gè)圓點(diǎn)和發(fā)布號(hào)。格式如下:
?
libxxxx.so.major.minor
?
其中,xxxx是庫(kù)的名字,major是主版本號(hào),minor 是次版本號(hào)或叫發(fā)布號(hào),次版本號(hào)和其相應(yīng)的圓點(diǎn)是可選的。
?
soname是記錄在共享庫(kù)中的,其它庫(kù)使用這個(gè)共享庫(kù)時(shí),實(shí)際上只需要的提供soname,動(dòng)態(tài)鏈接器會(huì)找到名稱是soname的動(dòng)態(tài)庫(kù)給程序使用。
?
這種帶版本號(hào)的共享庫(kù)主要是為了你可以很方便的升級(jí)你的函數(shù)庫(kù),如果某個(gè)API改變了,創(chuàng)建庫(kù)的程序會(huì)改變主版本號(hào),然而,如果一個(gè)函數(shù)升級(jí)了某個(gè)函數(shù)庫(kù),而功能沒(méi)有發(fā)生變化,這時(shí)只需要改變次版本號(hào),由于只改變了次版本號(hào),所以soname沒(méi)有發(fā)生改變,這樣就可以做到與舊的共享庫(kù)保持兼容。
?
下面簡(jiǎn)要說(shuō)明動(dòng)態(tài)庫(kù)的編寫(xiě)過(guò)程:
?
/* file libhello.h - for example use! */ void printhello();庫(kù)的代碼很基本,在下一個(gè)清單中顯示。
?
/* file libprint.c */ #include "stdio.h" void printhello() {printf("hello opendba/n"); }?編譯:
gcc -fPIC -c libhello.c ld -shared -soname libhello.so.1 -olibhello.so.1.0 -lc libhello.o-soname也可以用-h代替。
?
注意,gcc 命令行中的 -fPIC 選項(xiàng)。這是生成 Position-Independent Code 所必須要的。把這個(gè)命令翻譯出來(lái)就是:生成可以在進(jìn)程的進(jìn)程空間的任何地方載入的代碼。這對(duì)于共享對(duì)象是非常重要的。使用這個(gè)選項(xiàng),使得必須執(zhí)行重定位的數(shù)量降低到最少。一旦載入可執(zhí)行程序使用的共享對(duì)象,就必須給它分配一些空間。必須給文本和數(shù)據(jù)部分配一些位置。如果它們不是以“位置獨(dú)立”方式來(lái)構(gòu)建,那么載入共享對(duì)象時(shí),程序要做大量的重定位,這會(huì)影響到性能。
?
現(xiàn)在我們分析一下傳給 ld 的選項(xiàng)。-shared 選項(xiàng)表明輸出的文件被認(rèn)為是共享的庫(kù)。通過(guò) -soname name 選項(xiàng),可以指定 soname 是什么。-o name 指定了共享對(duì)象的real name,也就是實(shí)際生成的動(dòng)態(tài)庫(kù)的文件名稱。
?
為了讓動(dòng)態(tài)鏈接庫(kù)為系統(tǒng)所共享,還需運(yùn)行動(dòng)態(tài)鏈接庫(kù)的管理命令--ldconfig
ldconfig?命令的用途,主要是在默認(rèn)搜尋目錄(/lib和/usr/lib)以及動(dòng)態(tài)庫(kù)配置文件/etc/ld.so.conf內(nèi)所列的目錄下,搜索出可共享的動(dòng)態(tài)鏈接庫(kù)(格式如前介紹,lib*.so*),進(jìn)而創(chuàng)建出動(dòng)態(tài)裝入程序(ld.so)所需的連接和緩存文件.緩存文件默認(rèn)為? /etc/ld.so.cache,此文件保存已排好序的動(dòng)態(tài)鏈接庫(kù)名字列表.
?
ldconfig -p 輸出共享庫(kù)soname實(shí)際對(duì)應(yīng)的共享庫(kù)的文件名稱。
?
一個(gè)程序/shared庫(kù)一般都要依賴其他的一些庫(kù),這可以用ldd來(lái)查看,它列出了依賴的庫(kù)的soname,因?yàn)閷?shí)際依賴是庫(kù)的接口,而 soname正是反映了庫(kù)的接口信息。linux使用ELF作為可執(zhí)行程序和庫(kù)的格式,這些依賴的庫(kù)的soname保存在ELF的某個(gè)fileld里。當(dāng)一個(gè)可執(zhí)行程序執(zhí)行時(shí),ld.so負(fù)責(zé)把它所依賴的shared庫(kù)加載到內(nèi)存并鏈接,它按照以下順序?qū)ふ襰hared庫(kù):
?
???1. 在LD_LIBRARY_PATH環(huán)境變量指定的目錄下
2. ld.so.cache文件該shared庫(kù)對(duì)應(yīng)的文件
3. /usr/lib和/lib目錄下
?
環(huán)境變量:
?
LD_BIND_NOW?--- 正常來(lái)講,函數(shù)在呼叫之前是不會(huì)讓程式尋找(looked up)的.設(shè)定這個(gè)旗號(hào)會(huì)使得程式庫(kù)一載入,所有的尋找(lookups)便會(huì)發(fā)生,同時(shí)也造成起始的時(shí)間(startup time)較慢.當(dāng)你想測(cè)試程式,確定所有的連結(jié)都沒(méi)有問(wèn)題時(shí),這項(xiàng)旗號(hào)就變得很有用.
LD_PRELOAD?可以設(shè)定一個(gè)檔案,使其具有*覆蓋*(overriding)函數(shù)定義的能力.例如,如果你要測(cè)試記憶體分配的方略(strategies),而且還想置換*malloc*,那麼你可以寫(xiě)好準(zhǔn)備替換的副程式(routine),并把它編譯成mallolc. ,然後:
$LD_PRELOAD=malloc.o; export LD_PRELOAD $ some_test_program ?LD_ELF_PRELOAD? 與LD_AOUT_PRELOAD? 很類似,但是僅適用於正確的二進(jìn)位型態(tài).如果設(shè)定了 LD_ something _PRELOAD? 與LD_PRELOAD? ,比較明確的那一個(gè)會(huì)被用到.
LD_LIBRARY_PATH? 是一連串以分號(hào)隔離的目錄名稱,用來(lái)搜尋共享程式庫(kù).對(duì)ld而言,并沒(méi)有 任何的影響;這項(xiàng)只有在執(zhí)行期間才有影響.另外,對(duì)執(zhí)行setuid與setgid的程式而言,這一項(xiàng)是無(wú)效的.而LD_ELF_LIBRARY_PATH與LD_AOUT_LIBRARY_PATH 這兩種旗號(hào)可根據(jù)各別的二進(jìn)位型式分別導(dǎo)向不同的搜尋路徑.一般正常的運(yùn)作下,不應(yīng)該會(huì)用到LD_LIBRARY_PATH ;把需要搜尋的目錄加到/etc/ld.so.conf/ 里;然後重新執(zhí)行l(wèi)dconfig.
LD_NOWARN?僅適用於a.out.一旦設(shè)定了這一項(xiàng)(LD_NOWARN=true; export LD_NOWARN ),它會(huì)告訴載入器必須處理fatal-warnings(像是次要版本不相容等)的警告訊息.
LD_WARN?僅適用於ELF.設(shè)定這一項(xiàng)時(shí),它會(huì)將通常是致命訊息的"Can*t find library"轉(zhuǎn)換成警告訊息.對(duì)正常的操作而言,這并沒(méi)有多大的用處,可是對(duì)ldd就很重要了.
LD_TRACE_LOADED_OBJECTS? 僅適用於ELF.而且會(huì)使得程式以為它們是由ldd所執(zhí)行的:
$LD_TRACE_LOADED_OBJECTS=true /usr/bin/lynxlibncurses.so.1 => /usr/lib/libncurses.so.1.9.6libc.so.5 => /lib/libc.so.5.2.18?
轉(zhuǎn)載于:https://www.cnblogs.com/new0801/p/6177045.html
總結(jié)
以上是生活随笔為你收集整理的C语言的本质(35)——共享库的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 【solr专题之二】配置文件:solr.
- 下一篇: 设置JAVA环境变量