linux 庫的理解

 

一.庫的分類
    有兩種說法,如果熟悉WIN平臺下的DLL,相信不難理解:

    庫可以有三種使用的形式:靜態、共享和動態。靜態庫的代碼在編譯時就已連接到開發人員開發的應用程序中,而共享庫只是在程序開始運行時才載入,在編譯時, 只是簡單地指定需要使用的庫函數。動態庫則是共享庫的另一種變化形式。動態庫也是在程序運行時載入,但與共享庫不同的是,使用的庫函數不是在程序運行開 始,而是在程序中的語句需要使用該函數時才載入。動態庫可以在程序運行期間釋放動態庫所佔用的內存,騰出空間供其它程序使用。由於共享庫和動態庫並沒有在 程序中包括庫函數的內容,只是包含了對庫函數的引用,因此代碼的規模比較小。

    Linux下的庫文件分爲共享庫和靜態庫兩大類,它們兩者的差別僅在程序執行時所需的代碼是在運行時動態加載的,還是在編譯時靜態加載的。區分庫類型最好 的方法是看它們的文件後綴,通常共享庫以.so(Shared Object的縮寫)結尾,靜態鏈接庫通常以.a結尾(Archive的縮寫)。在終端缺省情況下,共享庫通常爲綠色,而靜態庫爲黑色。


  已經開發的大多數庫都採取共享庫的方式。ELF格式的可執行文件使得共享庫能夠比較容易地實現,當然使用舊的a.out模式也可以實現庫的共享。Linux系統中目前可執行文件的標準格式爲ELF格式。

  .a的是爲了支持較老的a.out格式的可執行文件的
  .so的是支持elf格式的可執行文件的庫。

   .a是靜態庫文件,可以用ar 命令生成。
  .so是動態庫文件,編譯時加上指定的選項即可生成,具體選項看相應的系統文檔了。


二.庫的命名規則
    GNU庫的使用必須遵守Library GNU Public License(LGPL許可協議)。該協議與GNU許可協議略有不同,開發人員可以免費使用GNU庫進行軟件開發,但必須保證向用戶提供所用的庫函數的源代碼。

  系統中可用的庫都存放在/usr/lib和/lib目錄中。庫文件名由前綴lib和庫名以及後綴組成。根據庫的類型不同,後綴名也不一樣。共享庫的後綴名由.so和版本號組成,靜態庫的後綴名爲.a。採用舊的a.out格式的共享庫的後綴名爲.sa。
  libname.so.major.minor
  libname.a

  這裏的name可以是任何字符串,用來唯一標識某個庫。該字符串可以是一個單字、幾個字符、甚至一個字母。數學共享庫的庫名爲 libm.so.5,這裏的標識字符爲m,版本號爲5。libm.a則是靜態數學庫。X-Windows庫名爲libX11.so.6,這裏使用X11作 爲庫的標識,版本號爲6。

三。庫操作命令

   Linux庫操作可以使用命令完成,目前常用的命令是ldd和ldconfig。

   1.ldd
 ldd是Library Dependency Display縮寫,它的作用是顯示一個可執行程序必須使用的共享庫。

 $ 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.ldconfig
 庫安裝到系統以後,爲了讓動態鏈接庫爲系統所認識及共享,就需要運行ldconfig。ldconfig命 令的用途,主要是在默認搜尋目錄(/lib和/usr/lib)以及動態庫配置文件/etc/ld.so.conf內所列的目錄下,搜索出可共享的動態鏈 接庫(格式如lib*.so*),進而創建出動態裝入程序(ld.so)所需的連接和緩存文件。緩存文件默認爲/etc/ld.so.cache,此文件 保存已排好序的動態鏈接庫名字列表,ldconfig通常在系統啓動時運行,而當用戶安裝了一個新的動態鏈接庫時,就需要手工運行這個命令。

     (1)命令格式
 ldconfig [選項] [libs]

     (2)主要選項
 -v或--verbose ldconfig將顯示正在掃描的目錄、搜索到的動態鏈接庫,以及它所創建的連接的名字。

 -f CONF 指定動態鏈接庫的配置文件爲CONF,系統默認爲/etc/ld.so.conf。

 -C CACHE 指定生成的緩存文件爲CACHE,系統默認的是/etc/ld.so.cache,文件存放已排好序的可共享的動態鏈接庫的列表。

 -p或--print-cache 讓ldconfig打印出當前緩存文件所保存的所有共享庫的名字。

 -r ROOT 改變應用程序的根目錄爲ROOT。

 -n ldconfig僅掃描命令行指定的目錄,不掃描默認目錄(/lib、/usr/lib),也不掃描配置文件/etc/ld.so.conf所列的目錄。

 運行沒有選項的ldconfig命令時,用於更新高速緩衝文件。這個命令主要用於高速緩衝DNS服務器(Caching DNS Server)。高速緩衝DNS服務器的原理是提供查詢的歷史記錄,並且利用這些記錄來提高查詢的效率。

 當某個查詢是第一次被髮送到高速緩衝DNS服務器時,高速緩衝DNS服務器就將此查詢的整個過程記錄下來,在一定的時期內用它來回答所有相同的查詢,從而減少整個DNS系統的負擔並且提高查詢速度。

四。庫的升級

 Linux系統軟件更新很快,新的核心幾乎每幾個星期就公佈一次,其它軟件的更新也是非常頻繁。多數情況下,盲目跟隨潮流的升級並不必要,如果確 實需要新版本的特性時再升級。換句話說,不要爲了升級而升級。Linux系統中多數軟件都是用共享庫來編譯的,其中包含了在不同程序之間共享的公用子例 程。

在運行某個程序時,如果看到如下信息:“Incompatible library version.”則表明需要將該庫升級到程序所需要的版本。庫是向下兼容的,也就是說,用老版本庫編譯的程序可以在新安裝的版本庫上運行,反之則不行。

Linux庫函數的升級是一項重要的工作,往往與其它軟件包的升級有一定關聯作用,所以操作前一定要備份文件。下面看一下如何把Glibc 2.2.4.13升級至2.3.2版本,其過程如下:

  1.下載.gz壓縮文件並解壓

在GUN C網站下載的四個.gz壓縮文件,解壓至一臨時目錄中:
cd /usr/caolinux
tar xzvf glibc-2.3.2.tar.gz
cd glibc-2.3.2
tar xzvf ../glibc-linuxthreads-2.3.2.tar.gz
tar xzvf ../glibc-crypt-2.3.2.tar.gz
tar xzvf ../glibc-localedata-2.3.2.tar.gz

  2.建立庫函數的安裝目錄
mkdir /usr/higlibc
cd /usr/higlibc

   3.建立編譯目錄
mkdir cao
cd cao
./configure --enable-add-ons=linuxthreads,crypt,localedata -prefix=/usr/higlibc

  4.編譯與安裝
make
make check
make install

  5.改變數據庫的鏈接
ln -s /usr/higlibc/lib/ld-linux.so.2 /lib/ld-linux.so.2

然後,修改/etc/ld.so.conf,加入一行/usr/higlibc/lib,執行下面代碼:
ldconfig -v

更新/etc/ld.so.cache的內容,列出每個庫的版本號,掃描目錄和所要創建及更新的鏈接。

 6.更改GCC設置

cd /usr/lib/gcc-lib
cp -r i386-redhat-linux higlibc

 7.更新符號鏈接
cd /usr/higlibc/include
ln -s /usr/src/linux/include/linux
ln -s /usr/src/linux/include/asm
ln -s /usr/X11R6/include/X11

8.測試並完成

五。高級共享庫特性
 1. soname

共享庫的一個非常重要的,也是非常難的概念是 soname——簡寫共享目標名(short for shared object name)。這是一個爲共享庫(.so)文件而內嵌在控制數據中的名字。如前面提到的,每一個程序都有一個需要使用的庫的清單。這個清單的內容是一系列庫 的 soname,如同 ldd 顯示的那樣,共享庫裝載器必須找到這個清單。

soname 的關鍵功能是它提供了兼容性的標準。當要升級系統中的一個庫時,並且新庫的 soname 和老的庫的 soname 一樣,用舊庫連接生成的程序,使用新的庫依然能正常運行。這個特性使得在 Linux 下,升級使用共享庫的程序和定位錯誤變得十分容易。

在 Linux 中,應用程序通過使用 soname,來指定所希望庫的版本。庫作者也可以通過保留或者改變 soname 來聲明,哪些版本是相互兼容的,這使得程序員擺脫了共享庫版本衝突問題的困擾。

查看/usr/local/lib 目錄,分析 MiniGUI 的共享庫文件之間的關係

2. 共享庫裝載器

當程序被調用的時候,Linux 共享庫裝載器(也被稱爲動態連接器)也自動被調用。它的作用是保證程序所需要的所有適當版本的庫都被調入內存。共享庫裝載器名字是 ld.so 或者是 ld-linux.so,這取決於 Linux libc 的版本,它必須使用一點外部交互,才能完成自己的工作。然而它接受在環境變量和配置文件中的配置信息。

文件 /etc/ld.so.conf 定義了標準系統庫的路徑。共享庫裝載器把它作爲搜索路徑。爲了改變這個設置,必須以 root 身份運行 ldconfig 工具。這將更新 /etc/ls.so.cache 文件,這個文件其實是裝載器內部使用的文件之一。

3. 使用 dlopen

另外一個強大的庫函數是 dlopen()。該函數將打開一個新庫,並把它裝入內存。該函數主要用來加載庫中的符號,這些符號在編譯的時候是不知道的。比如 Apache Web 服務器利用這個函數在運行過程中加載模塊,這爲它提供了額外的能力。一個配置文件控制了加載模塊的過程。這種機制使得在系統中添加或者刪除一個模塊時,都 不需要重新編譯了。

可以在自己的程序中使用 dlopen()。dlopen() 在 dlfcn.h 中定義,並在 dl 庫中實現。它需要兩個參數:一個文件名和一個標誌。文件名可以是我們學習過的庫中的 soname。標誌指明是否立刻計算庫的依賴性。如果設置爲 RTLD_NOW 的話,則立刻計算;如果設置的是 RTLD_LAZY,則在需要的時候才計算。另外,可以指定 RTLD_GLOBAL,它使得那些在以後才加載的庫可以獲得其中的符號。

當庫被裝入後,可以把 dlopen() 返回的句柄作爲給 dlsym() 的第一個參數,以獲得符號在庫中的地址。使用這個地址,就可以獲得庫中特定函數的指針,並且調用裝載庫中的相應函數。

六、LINUX下動態鏈接庫的使用
重要的dlfcn.h頭文件
LINUX下使用動態鏈接庫,源程序需要包含dlfcn.h頭文件,此文件定義了調用動態鏈接庫的函數的原型。下面詳細說明一下這些函數。
1。 dlerror
原型爲: const char *dlerror(void);
當動態鏈接庫操作函數執行失敗時,dlerror可以返回出錯信息,返回值爲NULL時表示操作函數執行成功。
2。 dlopen
原型爲: void *dlopen (const char *filename, int flag);
dlopen用於打開指定名字(filename)的動態鏈接庫,並返回操作句柄。
filename: 如果名字不以/開頭,則非絕對路徑名,將按下列先後順序查找該文件。
(1) 用戶環境變量中的LD_LIBRARY值;
(2) 動態鏈接緩衝文件/etc/ld.so.cache
(3) 目錄/lib,/usr/lib
flag表示在什麼時候解決未定義的符號(調用)。取值有兩個:
1) RTLD_LAZY : 表明在動態鏈接庫的函數代碼執行時解決。
2) RTLD_NOW : 表明在dlopen返回前就解決所有未定義的符號,一旦未解決,dlopen將返回錯誤。
dlopen調用失敗時,將返回NULL值,否則返回的是操作句柄。
3。 dlsym : 取函數執行地址
原型爲: void *dlsym(void *handle, char *symbol);
dlsym根據動態鏈接庫操作句柄(handle)與符號(symbol),返回符號對應的函數的執行代碼地址。由此地址,可以帶參數執行相應的函數。
如程序代碼: void (*add)(int x,int y); /* 說明一下要調用的動態函數add */
add=dlsym("xxx.so","add"); /* 打開xxx.so共享庫,取add函數地址 */
add(89,369); /* 帶兩個參數89和369調用add函數 */
4。 dlclose : 關閉動態鏈接庫
原型爲: int dlclose (void *handle);
dlclose用於關閉指定句柄的動態鏈接庫,只有當此動態鏈接庫的使用計數爲0時,纔會真正被系統卸載。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章