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