庫技術 - Linux庫詳解_第1頁
庫技術 - Linux庫詳解_第2頁
庫技術 - Linux庫詳解_第3頁
庫技術 - Linux庫詳解_第4頁
庫技術 - Linux庫詳解_第5頁
已閱讀5頁,還剩7頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1、Linux庫詳解1 庫基本概念1.1 庫的分類 有兩種說法,如果熟悉WIN平臺下的DLL,相信不難理解: 庫可以有三種使用的形式:靜態(tài)、共享和動態(tài)。靜態(tài)庫的代碼在編譯時就已連接到開發(fā)人員開發(fā)的應用程序中,而共享庫只是在程序開始運行時才載入,在編譯時, 只是簡單地指定需要使用的庫函數(shù)。動態(tài)庫則是共享庫的另一種變化形式。動態(tài)庫也是在程序運行時載入,但與共享庫不同的是,使用的庫函數(shù)不是在程序運行開 始,而是在程序中的語句需要使用該函數(shù)時才載入。動態(tài)庫可以在程序運行期間釋放動態(tài)庫所占用的內存,騰出空間供其它程序使用。由于共享庫和動態(tài)庫并沒有在程序中包括庫函數(shù)的內容,只是包含了對庫函數(shù)的引用,因此代碼的

2、規(guī)模比較小。 Linux下的庫文件分為共享庫和靜態(tài)庫兩大類,它們兩者的差別僅在程序執(zhí)行時所需的代碼是在運行時動態(tài)加載的,還是在編譯時靜態(tài)加載的。區(qū)分庫類型最好的方法是看它們的文件后綴,通常共享庫以.so(Shared Object的縮寫)結尾,靜態(tài)鏈接庫通常以.a結尾(Archive的縮寫)。在終端缺省情況下,共享庫通常為綠色,而靜態(tài)庫為黑色。 已經開發(fā)的大多數(shù)庫都采取共享庫的方式。ELF格式的可執(zhí)行文件使得共享庫能夠比較容易地實現(xiàn),當然使用舊的a.out模式也可以實現(xiàn)庫的共享。Linux系統(tǒng)中目前可執(zhí)行文件的標準格式為ELF格式。 .a的是為了支持較老的a.out格式的可執(zhí)行文件的 .so的

3、是支持elf格式的可執(zhí)行文件的庫。 .a是靜態(tài)庫文件,可以用ar 命令生成。 .so是動態(tài)庫文件,編譯時加上指定的選項即可生成,具體選項看相應的系統(tǒng)文檔了。1.2 庫的命名規(guī)則GNU庫的使用必須遵守Library GNU Public License(LGPL許可協(xié)議)。該協(xié)議與GNU許可協(xié)議略有不同,開發(fā)人員可以免費使用GNU庫進行軟件開發(fā),但必須保證向用戶提供所用的庫函數(shù)的源代碼。庫的命名和約定:(1)所以的庫都以lib開頭,許多開發(fā)工具都依賴這個約定,特別是GCC,它會在-l選項所指定的文件名前自動地插入lib. (2)文件名以.a(代表存檔,archive)結尾的庫都是靜態(tài)庫,文件名以

4、.so(代表共享目標文件,shared object)結尾的庫都是共享庫,例如,libdl.a是一個靜態(tài)庫,而libc.so是一個共享庫。采用舊的a.out格式的共享庫的后綴名為.sa。(3) 以_g和_p結尾的庫,如libform_g.a和libfor_p.a,是基本庫libform的特殊版本。通常以_g結尾的庫是調試庫,它們編入了特殊的符號和功能,能夠增加對采用了這個庫的應用程序進行調試的能力。類似地,代碼剖析(profiling)庫通常在名字后面附加_p,他們包含地代碼和符號能夠進行復雜地代碼剖析和性能分析。如果你使用了這些庫中的某一個,那么一旦完成調試或剖析工作,需要使用基本庫重新編譯

5、你的程序。庫名的一般格式為:libname.major_num.minor_num.patch_num, 例如,libgdbm.so.2.0.0, libgdbm.so是庫libname, major_num是2,minor_num和patch_num均為0.系統(tǒng)中可用的庫都存放在/usr/lib和/lib目錄中。這里的name可以是任何字符串,用來唯一標識某個庫。該字符串可以是一個單字、幾個字符、甚至一個字母。數(shù)學共享庫的庫名為libm.so.5,這里的標識字符為m,版本號為5。libm.a則是靜態(tài)數(shù)學庫。X-Windows庫名為libX11.so.6,這里使用X11作為庫的標識,版本號為6

6、。每個共享函數(shù)庫都有個特殊的名字,稱作“soname”。Soname名字命名必須以“l(fā)ib”作為前綴,然后是函數(shù)庫的名字,然后是“.so”,最后是版本號信息。不過有個特例,就是非常底層的C庫函數(shù)都不是以lib開頭這樣命名的。每個共享函數(shù)庫都有一個真正的名字(“real name”),它是包含真正庫函數(shù)代碼的文件。真名有一個主版本號,和一個發(fā)行版本號。最后一個發(fā)行版本號是可選的,可以沒有。主版本號和發(fā)行版本號使你可以知道你到底是安裝了什么版本的庫函數(shù)。另外,還有一個名字是編譯器編譯的時候需要的函數(shù)庫的名字,這個名字就是簡單的soname名字,而不包含任何版本號信息。管理共享函數(shù)庫的關鍵是區(qū)分好這

7、些名字。當可執(zhí)行程序需要在自己的程序中列出這些他們需要的共享庫函數(shù)的時候,它只要用soname就可以了;反過來,當你要創(chuàng)建一個新的共享函數(shù)庫的時候,你要指定一個特定的文件名,其中包含很細節(jié)的版本信息。當你安裝一個新版本的函數(shù)庫的時候,你只要先將這些函數(shù)庫文件拷貝到一些特定的目錄中,運行l(wèi)dconfig這個實用就可以。Ldconfig檢查已經存在的庫文件,然后創(chuàng)建soname的符號鏈接到真正的函數(shù)庫,同時設置/etc/ld.so.cache這個緩沖文件。這個我們稍后再討論。Ldconfig并不設置鏈接的名字,通常的做法是在安裝過程中完成這個鏈接名字的建立,一般來說這個符號鏈接就簡單的指向最新的s

8、oname或者最新版本的函數(shù)庫文件。最好把這個符號鏈接指向soname,因為通常當你升級你的庫函數(shù)后,你就可以自動使用新版本的函數(shù)庫。我們來舉例看看:/usr/lib/libreadline.so.3 是一個完全的完整的soname,ldconfig可以設置一個符號鏈接到其他某個真正的函數(shù)庫文件,例如是 /usr/lib/libreadline.so.3.0。同時還必須有一個鏈接名字,例如/usr/lib/libreadline.so 就是一個符號鏈接指向/usr/lib/libreadline.so.3。2 庫操作命令 Linux庫操作可以使用命令完成,目前常用的命令是ldd和ldconfi

9、g。2.1 ldd ldd是Library Dependency Display縮寫,它的作用是顯示一個可執(zhí)行程序必須使用的共享庫。 $ ldd /usr/bin/mesg libc.so.6 = /lib/tls/i686/cmov/libc.so.6 (0xb7eaf000) /lib/ld-linux.so.2 = /lib/ld-linux.so.2 (0xb7feb000)2.2 ldconfig 庫安裝到系統(tǒng)以后,為了讓動態(tài)鏈接庫為系統(tǒng)所認識及共享,就需要運行l(wèi)dconfig。ldconfig命令的用途,主要是在默認搜尋目錄(/lib和 /usr/lib)以及動態(tài)庫配置文件/etc

10、/ld.so.conf內所列的目錄下,搜索出可共享的動態(tài)鏈接庫(格式如lib*.so*),進而創(chuàng)建出動態(tài)裝入程序(ld.so)所需的連接和緩存文件。緩存文件默認為/etc/ld.so.cache,此文件保存已排好序的動態(tài)鏈接庫名字列表, ldconfig通常在系統(tǒng)啟動時運行,而當用戶安裝了一個新的動態(tài)鏈接庫時,就需要手工運行這個命令。(1)命令格式 ldconfig 選項 libs(2)主要選項 -v或-verbose ldconfig將顯示正在掃描的目錄、搜索到的動態(tài)鏈接庫,以及它所創(chuàng)建的連接的名字。 -f CONF 指定動態(tài)鏈接庫的配置文件為CONF,系統(tǒng)默認為/etc/ld.so.con

11、f。 -C CACHE 指定生成的緩存文件為CACHE,系統(tǒng)默認的是/etc/ld.so.cache,文件存放已排好序的可共享的動態(tài)鏈接庫的列表。 -p或-print-cache 讓ldconfig打印出當前緩存文件所保存的所有共享庫的名字。 -r ROOT 改變應用程序的根目錄為ROOT。 n ldconfig僅掃描命令行指定的目錄,不掃描默認目錄(/lib、/usr/lib),也不掃描配置文件/etc/ld.so.conf所列的目錄。 運行沒有選項的ldconfig命令時,用于更新高速緩沖文件。這個命令主要用于高速緩沖DNS服務器(Caching DNS Server)。高速緩沖DNS服務

12、器的原理是提供查詢的歷史記錄,并且利用這些記錄來提高查詢的效率。當某個查詢是第一次被發(fā)送到高速緩沖DNS服務器時,高速緩沖DNS服務器就將此查詢的整個過程記錄下來,在一定的時期內用它來回答所有相同的查詢,從而減少整個DNS系統(tǒng)的負擔并且提高查詢速度。3 庫的路徑問題為了讓執(zhí)行程序順利找到動態(tài)庫,有三種方法:(1)把庫拷貝到/usr/lib和/lib目錄下。(2)在LD_LIBRARY_PATH環(huán)境變量中加上庫所在路徑。例如動態(tài)庫libhello.so在/home/ting/lib目錄下,以bash為例,使用命令:$export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:

13、/home/ting/lib(3) 修改/etc/ld.so.conf文件,把庫所在的路徑加到文件末尾,并執(zhí)行l(wèi)dconfig刷新。這樣,加入的目錄下的所有庫文件都可見4 查看庫中的符號有時候可能需要查看一個庫中到底有哪些函數(shù),nm命令可以打印出庫中的涉及到的所有符號。庫既可以是靜態(tài)的也可以是動態(tài)的。nm列出的符號有很多,常見的有三種,一種是在庫中被調用,但并沒有在庫中定義(表明需要其他庫支持),用U表示;一種是庫中定義的函數(shù),用T表示,這是最常見的;另外一種是所謂的 “弱態(tài)”符號,它們雖然在庫中被定義,但是可能被其他庫中的同名符號覆蓋,用W表示。例如,假設開發(fā)者希望知道上文提到的hello庫

14、中是否定義了 printf():$nm libhello.so |grep printfU printfU表示符號printf被引用,但是并沒有在函數(shù)內定義,由此可以推斷,要正常使用hello庫,必須有其它庫支持,再使用ldd命令查看hello依賴于哪些庫:$ldd hellolibc.so.6=/lib/libc.so.6(0x400la000)/lib/ld-linux.so.2=/lib/ld-linux.so.2 (0x40000000)從上面的結果可以繼續(xù)查看printf最終在哪里被定義,有興趣可以go on5 庫升級Linux系統(tǒng)軟件更新很快,新的核心幾乎每幾個星期就公布一次,其它

15、軟件的更新也是非常頻繁。多數(shù)情況下,盲目跟隨潮流的升級并不必要,如果確實需要新版本的特性時再升級。換句話說,不要為了升級而升級。Linux系統(tǒng)中多數(shù)軟件都是用共享庫來編譯的,其中包含了在不同程序之間共享的公用子例程。在運行某個程序時,如果看到如下信息:“Incompatible library version”則表明需要將該庫升級到程序所需要的版本。庫是向下兼容的,也就是說,用老版本庫編譯的程序可以在新安裝的版本庫上運行,反之則不行。Linux庫函數(shù)的升級是一項重要的工作,往往與其它軟件包的升級有一定關聯(lián)作用,所以操作前一定要備份文件。下面看一下如何把Glibc 2.2.4.13升級至2.3.

16、2版本,其過程如下: 1.下載.gz壓縮文件并解壓在GUN C網站下載的四個.gz壓縮文件,解壓至一臨時目錄中:cd /usr/caolinuxtar xzvf glibc-2.3.2.tar.gzcd glibc-2.3.2tar xzvf ./glibc-linuxthreads-2.3.2.tar.gztar xzvf ./glibc-crypt-2.3.2.tar.gztar xzvf ./glibc-localedata-2.3.2.tar.gz 2.建立庫函數(shù)的安裝目錄mkdir /usr/higlibccd /usr/higlibc 3.建立編譯目錄mkdir caocd cao

17、./configure -enable-add-ons=linuxthreads,crypt,localedata -prefix=/usr/higlibc 4.編譯與安裝makemake checkmake install 5.改變數(shù)據庫的鏈接ln -s /usr/higlibc/lib/ld-linux.so.2 /lib/ld-linux.so.2然后,修改/etc/ld.so.conf,加入一行/usr/higlibc/lib,執(zhí)行下面代碼:ldconfig -v更新/etc/ld.so.cache的內容,列出每個庫的版本號,掃描目錄和所要創(chuàng)建及更新的鏈接。 6.更改GCC設置cd /

18、usr/lib/gcc-libcp -r i386-redhat-linux higlibc 7.更新符號鏈接cd /usr/higlibc/includeln -s /usr/src/linux/include/linuxln -s /usr/src/linux/include/asmln -s /usr/X11R6/include/X118.測試并完成6 高級共享庫特性6.1 soname為了在同一系統(tǒng)中使用不同版本的庫,可以在庫文件名后加上版本號為后綴,例如: libhello.so.1.0,由于程序連接默認以.so為文件后綴名。所以為了使用這些庫,通常使用建立符號連接的方式。ln -s

19、 libhello.so.1.0 libhello.so.1ln -s libhello.so.1 libhello.so共享庫的一個非常重要的,也是非常難的概念是 soname簡寫共享目標名(short for shared object name)。這是一個為共享庫(.so)文件而內嵌在控制數(shù)據中的名字。如前面提到的,每一個程序都有一個需要使用的庫的清單。這個清單的內容是一系列庫的 soname,如同 ldd 顯示的那樣,共享庫裝載器必須找到這個清單。soname 的關鍵功能是它提供了兼容性的標準。當要升級系統(tǒng)中的一個庫時,并且新庫的 soname 和老的庫的 soname 一樣,用舊庫連

20、接生成的程序,使用新的庫依然能正常運行。這個特性使得在 Linux 下,升級使用共享庫的程序和定位錯誤變得十分容易。在 Linux 中,應用程序通過使用 soname,來指定所希望庫的版本。庫作者也可以通過保留或者改變 soname 來聲明,哪些版本是相互兼容的,這使得程序員擺脫了共享庫版本沖突問題的困擾。查看/usr/local/lib 目錄,分析 MiniGUI 的共享庫文件之間的關系6.2 共享庫裝載器當程序被調用的時候,Linux 共享庫裝載器(也被稱為動態(tài)連接器)也自動被調用。它的作用是保證程序所需要的所有適當版本的庫都被調入內存。共享庫裝載器名字是 ld.so 或者是 ld-lin

21、ux.so,這取決于 Linux libc 的版本,它必須使用一點外部交互,才能完成自己的工作。然而它接受在環(huán)境變量和配置文件中的配置信息。文件 /etc/ld.so.conf 定義了標準系統(tǒng)庫的路徑。共享庫裝載器把它作為搜索路徑。為了改變這個設置,必須以 root 身份運行 ldconfig 工具。這將更新 /etc/ls.so.cache 文件,這個文件其實是裝載器內部使用的文件之一。6.3 使用 dlopen另外一個強大的庫函數(shù)是 dlopen()。該函數(shù)將打開一個新庫,并把它裝入內存。該函數(shù)主要用來加載庫中的符號,這些符號在編譯的時候是不知道的。比如 Apache Web 服務器利用這

22、個函數(shù)在運行過程中加載模塊,這為它提供了額外的能力。一個配置文件控制了加載模塊的過程。這種機制使得在系統(tǒng)中添加或者刪除一個模塊時,都不需要重新編譯了??梢栽谧约旱某绦蛑惺褂?dlopen()。dlopen() 在 dlfcn.h 中定義,并在 dl 庫中實現(xiàn)。它需要兩個參數(shù):一個文件名和一個標志。文件名可以是我們學習過的庫中的 soname。標志指明是否立刻計算庫的依賴性。如果設置為 RTLD_NOW 的話,則立刻計算;如果設置的是 RTLD_LAZY,則在需要的時候才計算。另外,可以指定 RTLD_GLOBAL,它使得那些在以后才加載的庫可以獲得其中的符號。當庫被裝入后,可以把 dlopen

23、() 返回的句柄作為給 dlsym() 的第一個參數(shù),以獲得符號在庫中的地址。使用這個地址,就可以獲得庫中特定函數(shù)的指針,并且調用裝載庫中的相應函數(shù)。7 動態(tài)庫的編寫和使用現(xiàn)在假設有一個叫hello的程序開發(fā)包,它提供一個靜態(tài)庫libhello.a 一個動態(tài)庫libhello.so,一個頭文件hello.h,頭文件中提供sayhello()這個函數(shù)/* hello.h */void sayhello();另外還有一些說明文檔。這是一個典型的程序開發(fā)包結構。7.1 生成庫第一步要把源代碼編繹成目標代碼。以下面的代碼為例,生成上面用到的hello庫:/* hello.c */#include vo

24、id sayhello()printf(hello,worldn);用gcc編繹該文件,在編繹時可以使用任何合法的編繹參數(shù),例如-g加入調試代碼等:gcc -c hello.c -o hello.o1.連接成靜態(tài)庫連接成靜態(tài)庫使用ar命令,其實ar是archive的意思$ar cqs libhello.a hello.o2.連接成動態(tài)庫生成動態(tài)庫用gcc來完成,由于可能存在多個版本,因此通常指定版本號:$gcc -shared -Wl, -soname, libhello.so.1 -o libhello.so.1.0 hello.o另外再建立兩個符號連接:$ln -s libhello.so

25、.1.0 libhello.so.1$ln -s libhello.so.1 libhello.so這樣一個libhello的動態(tài)連接庫就生成了。最重要的是傳gcc -shared 參數(shù)使其生成是動態(tài)庫而不是普通執(zhí)行程序。-Wl 表示后面的參數(shù)也就是-soname,libhello.so.1直接傳給連接器ld進行處理。實際上,每一個庫都有一個soname,當連接器發(fā)現(xiàn)它正在查找的程序庫中有這樣一個名稱,連接器便會將soname嵌入連結中的二進制文件內,而不是它正在運行的實際文件名,在程序執(zhí)行期間,程序會查找擁有 soname名字的文件,而不是庫的文件名,換句話說,soname是庫的區(qū)分標志。這

26、樣做的目的主要是允許系統(tǒng)中多個版本的庫文件共存,習慣上在命名庫文件的時候通常與soname相同:libxxxx.so.major.minor 其中,xxxx是庫的名字,major是主版本號,minor 是次版本號。7.2 使用庫當要使用靜態(tài)的程序庫時,連接器會找出程序所需的函數(shù),然后將它們拷貝到執(zhí)行文件,由于這種拷貝是完整的,所以一旦連接成功,靜態(tài)程序庫也就不再需要了。 然而,對動態(tài)庫而言,就不是這樣。動態(tài)庫會在執(zhí)行程序內留下一個標記指明當程序執(zhí)行時,首先必須載入這個庫。由于動態(tài)庫節(jié)省空間,linux下進行連接 的缺省操作是首先連接動態(tài)庫,也就是說,如果同時存在靜態(tài)和動態(tài)庫,不特別指定的話,將

27、與動態(tài)庫相連接。1.與動態(tài)庫連接linux默認的就是與動態(tài)庫連接,下面這段程序testlib.c使用hello庫中的sayhello()函數(shù)/*testlib.c*/#include #include int main()sayhello();return 0;使用如下命令進行編譯$gcc -c testlib.c -o testlib.o用如下命令連接:$gcc testlib.o -lhello -o testlib在連接時要注意,假設libhello.o 和libhello.a都在缺省的庫搜索路徑下/usr/lib,如果在其它位置要加上-L參數(shù)2. 與靜態(tài)庫連接與靜態(tài)庫連接麻煩一些,主要是參數(shù)問題。還是上面的例子:$gc

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論