關於shared library的描述

原文鏈接:https://blog.csdn.net/w_ww_w/article/details/7002880

以前搞共享庫動態加載管理時找的一些資料,放在這裏共享。

引言:
在xmeeting中,關於usb手柄部分,採用動態庫調用方式,下面翻譯一篇David A. Wheeler寫的文章。文章就如何創建和使用靜態庫,共享庫以及動如何動態裝載庫進行了論述。內容綱要如下:
 1.概述
 2.靜態庫
 3.共享庫
   3.1 約定
       3.2 使用
    3.3 環境變量
   3.4 創建共享庫
   3.5 安裝與使用
   3.6 兼容性
 4.動態加載
   4.1 dlopen()
   4.2 dlerror()
   4.3 dlsym()
   4.4 dlclose()
   4.5 
示例
 5.輔助知識
   5.1 nm命令
   5.2 庫的構建與析構函數
   5.3 腳本
   5.4 版本
   5.5 GNU libtool
    5.6 
去除符號空間
   5.7 外部執行體
    5.8 C++  C
   5.9 加速C++初始化

   5.10 Linux標準
1.概述
  本文就如何在Linux系統中運用GNU工具創建和使用程序庫進行論述。所謂"程序庫",簡單說,就是包含了數據和執行碼的文件。其不能單獨執行,可以作爲其它執行程序的一部分,來完成執行功能。庫的存在,可以使得程序模塊化,可以加快程序的再編譯,可以實現代碼重用,可以使得程序便於升級。程序庫可分三類:靜態庫,共享庫和動態加載庫。

  靜態庫,是在執行程序運行前就已經加入到執行碼中,在物理上成爲執行程序的一部分;共享庫,是在執行程序啓動時加載到執行程序中,可以被多個執行程序共享使用。動態加載庫,其實並不是一種真正的庫類型,應該是一種庫的使用技術,應用程序可以在運行過程中隨時加載和使用庫。
  建議庫開發人員創建共享庫,比較明顯的優勢在於庫是獨立的,便於維護和更新;而靜態庫的更新比較麻煩,一般不做推薦。然而,它們又各有優點,後面會講到。在C++編程中,要使用動態加載技術,需要參考文章"C++ dlopen MINI-Howto"。
  文章中講述的執行程序和庫都採用ELF(Executable and Linking Format)格式,儘管GNU GCC 工具可以處理其它格式,但不在本文的討論範圍。本文可以在 http://www.dwheeler.com/program-libraryhttp://www.linuxdoc.org 找到。

2.靜態庫
  靜態庫可以認爲是一些目標代碼的集合。按照習慣,一般以".a"做爲文件後綴名。使用ar(archiver)命令可以創建靜態庫。因爲共享庫有着更大的優勢,靜態庫已經不被經常使用。但靜態庫使用簡單,仍有使用的餘地,並會一直存在。

  靜態庫在應用程序生成時,可以不必再編譯,節省再編譯時間。但在編譯器越來越快的今天,這一點似乎已不重要。如果其他開發人員要使用你的代碼,而你又不想給其源碼,提供靜態庫是一種選擇。從理論上講,應用程序使用了靜態庫,要比使用動態加載庫速度快1-5%,但由於莫名的原因,實際上可能並非如此。由此看來,除了使用方便外,靜態庫可能並非一種好的選擇。

  要創建一個靜態庫,或要將目標代碼加入到已經存在的靜態庫中,可以使用以下命令:
  ar rcs my_libraty.a file1.o file2.o
  
以上表示要把目標碼file1.o和file2.o加入到靜態庫my_library.a中。若my_library.a不存在,會自動創建。
 
  
靜態庫創建成功後,需要連接到應用程序中來使用。如果你使用gcc(1)來產生執行程序,需要利用-l選項來指定靜態庫。更多信息,查看gcc使用手冊。

  在使用gcc時,要注意其參數的順序。-l是連接器選項,一定要放在被編譯的文件名稱之後;若放在文件名稱之前,你會連接失敗,並會出現莫名其妙的錯誤。這一點切記。
 
  
你也可以直接使用連接器ld(1),使用其選項-l或-L。但最好使用gcc(1),因ld(1)的接口有可能會有變化。

3.共享庫
  共享庫是在程序啓動時被裝載。當一個應用程序裝載了一個共享庫後,其它應用程序仍可以裝載同一個共享庫。基於linux的使用方法,共享庫還有其它靈活的而又精妙的特性:
   更新庫並不影響應用程序使用舊的,非向後兼容的版本;
   在執行特定程序時,可以覆蓋整個庫或更新庫中的特定函數;
   以上操作不會影響已經運行的程序,他們仍會使用已經裝載的庫。

3.1約定
   要想共享庫具有以上特性,一些約定需要遵守。你需要掌握共享庫名稱之間的區別,特別是搜名(soname)和實名(realname)之間的區別和關係;你還需要知道共享庫在文件系統的位置。
3.1.1名稱
  每個共享庫都有一個特定的搜名(soname),其組成如下:
  lib  +  庫名  +  .so  +  .  +  version
   |       |        |_______________|
  前綴    庫名            後綴

  在文件系統中,搜名是一個指向實名的符號聯結。

  每個共享庫也有一個實名,其真正包含有庫的代碼,組成如下:
  搜名 +  .   +  子版本號 + . + 發佈號
  最後的句點和發佈號是可選項。
 
  
另外,共享庫還有一個名稱,一般用於編譯連接,稱爲連名(linker name),它可以被看作是沒有任何版本號的搜名。

  看下面的例子:
  lrwxrwxrwx  1 root root  libpng.so -> libpng12.so
  lrwxrwxrwx  1 root root  libpng.so.2 ->   libpng.so.2.1.0.12
  -rw-r--r--  1 root root  libpng.so.2.1.0.12
  
在以上信息中,  libpng.so.2.1.0.12是共享庫的實名(real name),libpng.so.2是共享庫搜名(soname),libpng.so則是連接名(linker name),用於編譯連接。

3.2共享庫的裝載
  在所有基於GNU glibc的系統(當然包括Linux)中,在啓動一個ELF二進制執行程序時,一個特殊的程序"程序裝載器"會被自動裝載並運行。在linux中,這個程序裝載器就是/lib/ld-linux.so.X(X是版本號)。它會查找並裝載應用程序所依賴的所有共享庫。
  被搜索的目錄保存在/etc/ls.so.conf文件中,但一般/usr/local/lib並不在搜索之列,至少debian是這樣。這似乎是一個系統失誤,只好自己加上了。
  當然,如果程序的每次啓動,都要去搜索一番,勢必效率不堪忍受。Linux系統已經考慮這一點,對共享庫採用了緩存管理。ldconfig就是實現這一功能的工具,其缺省讀取/etc/ld.so.conf文件,對所有共享庫按照一定規範建立符號連接,然後將信息寫入/etc/ld.so.cache。 /etc/ld.so.cache的存在大大加快了程序的啓動速度。

3.3創建共享庫
  共享庫的創建比較簡單,基本有兩步。首先使用-fPIC或-fpic創建目標文件,PIC或pic表示位置無關代碼,然後就可以使用以下格式創建共享庫了:
 gcc -share _Wl,-soname,your_soname -o library_name file_list library_list
  
下面是使用a.c和b.c創建庫的示例:
   gcc -fPIC -g -c -Wall a.c
   gcc -fPIC -g -c -Wall b.c
   gcc -share -Wl,-soname, libmyab.so.1 -o libmyab.so.1.0.1 a.o b.o -lc
   -g
表示帶有調試信息,-Wall表示產生警告信息。
  幾個需要注意的地方:
  (1)不推薦使用strip處理共享庫,最好不要使用-fomit-frame-pointer編譯選項
  (2)-fPIC和-fpic都可以產生目標獨立代碼,一般採用-fPIC,儘管其產生的目標文件可能會大些;-fpic產生的代碼小,執行速度快,但可能有平臺依賴限制。
  (3)一般情況下,-Wall,-soname,your_soname編譯選項是需要的。當然,-share選項更不能丟。

4 動態加載庫
  DL技術可以允許應用程序在運行過程的任何時候去加載和使用指定的庫。這一技術在插件的實現上很實用。(CJP, alarm ui 使用插件技術。對於這裏邊的體會還是有一點的。最大的體會就是,要好好利用dlerror(), 如果早使用dlerror(),則不會浪費這麼多的時間在編譯修改plugin庫上面。)動態加載庫這一概念並不是着眼於庫的文件格式,而是指使用方式。存在着一組接口函數,使得應用程序可以採用DL技術。下面對這些接口函數逐一介紹,在最後給出應用示例。

4.1 dlopen
  函數原型:
void *dlopen(const char *libname,int flag);
  功能描述:dlopen必須在dlerror,dlsym和dlclose之前調用,表示要將庫裝載到內存,準備使用。如果要裝載的庫依賴於其它庫,必須首先裝載依賴庫。如果dlopen操作失敗,返回NULL值;如果庫已經被裝載過,則dlopen會返回同樣的句柄。

  參數中的libname一般是庫的全路徑,這樣dlopen會直接裝載該文件;如果只是指定了庫名稱,在dlopen會按照下面的機制去搜尋:
  (1)根據環境變量LD_LIBRARY_PATH查找
  (2)根據/etc/ld.so.cache查找
  (3)查找依次在/lib和/usr/lib目錄查找。
  flag參數表示處理未定義函數的方式,可以使用RTLD_LAZY或RTLD_NOW。RTLD_LAZY表示暫時不去處理未定義函數,先把庫裝載到內存,等用到沒定義的函數再說;RTLD_NOW表示馬上檢查是否存在未定義的函數,若存在,則dlopen以失敗告終。(cjp, 如果出錯返回了,則可以調用dlerror()查看到具體的原因,這個是非常重要的。)

4.2 dlerror
  函數原型:
char *dlerror(void);
  功能描述:dlerror可以獲得最近一次dlopen,dlsym或dlclose操作的錯誤信息,返回NULL表示無錯誤。dlerror在返回錯誤信息的同時,也會清除錯誤信息。

4.3 dlsym
  函數原型:
void *dlsym(void *handle,const char *symbol);
  功能描述:在dlopen之後,庫被裝載到內存。dlsym可以獲得指定函數(symbol)在內存中的位置(指針)。如果找不到指定函數,則dlsym會返回NULL值。但判斷函數是否存在最好的方法是使用dlerror函數,下面是示例:

  dlerror();/*清除錯誤信息*/
  function = dlsym(handle,"function_name");
  if((error=dlerror()) != NULL)
  {
    /*錯誤處理
*/
  }
  else
  {
    /*找到函數
*/
  }

4.4 dlclose
  函數原型:
int dlclose(void *);
  功能描述:將已經裝載的庫句柄減一,如果句柄減至零,則該庫會被卸載。如果存在析構函數,則在dlclose之後,析構函數會被調用。

4.5動態加載庫示例
#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>

int main(int argc,char **argv)
{
  void *handle;
  double (*cosine)(double);
  char *error;

  handle = dlopen("/lib/libm.so.6",RTLD_LAZY);
  if(!handle)
  {
    printf("%s/n",dlerror());
    exit(1);
  }

  printf("opened /lib/libm.so.6/n");

  cosine = dlsym(handle,"cos");
  if((error = dlerror()) != NULL)
  {
    printf("%s/n",error);
    dlclose(handle);
    printf("after error,closed /lib/libm.so.6/n");
    exit(1);
  }

  printf("%f/n",(*cosine)(2.0));

  dlclose(handle);
  printf("closed /lib/libm.so.6/n");

  return 0;
}
  編譯:gcc -o test test.c -ldl。在這個例子中,/lib/libm.so.6是動態加載庫,而/usr/lib/libdl.so則是共享庫。


5.相關知識
5.1 nm命令
  nm(1)命令可以報告庫的符號列表,對於查看庫的相關信息是一個不錯的工具。具體使用查看幫助文檔。示例:
nm -D libavcodec-0.4.7.so | grep 263
結果如下:

00109d40 T h263_encode_mb
00105f94 T h263_encode_picture_header
001a85a0 D h263_encoder
001162d0 T h263_get_picture_format
0010a7b4 T h263_pred_motion
00106df8 T h263_send_video_packet
001ab180 D h263i_decoder
001a85e0 D h263p_encoder
00115c68 T intel_h263_decode_picture_header
……………
其中,T表示正常代碼段,D表示初始化數據段

5.2庫的構建與析構函數
  關於構建與析構函數,一般不需要自己去編程實現。如果你一定要自己做,下面是函數原型:
  void __attribute__ ((constructor)) my_init(void);
  void __attribute__ ((destructor)) my_fini(void);
  
在編譯共享庫時,不能使用"-nonstartfiles"或"-nostdlib"選項,否則,構建與析構函數將不能正常執行(除非你採取一定措施)。

5.3腳本共享庫
  linux中,共享庫可以是腳本形式,當然需要專門的腳本語言。/usr/lib/libc.so是一個典型的例子,內容如下:
  /* GNU ld script
     Use the shared library, but some functions are only in
     the static library, so try that secondarily.  */
  OUTPUT_FORMAT(elf32-i386)
  GROUP ( /lib/libc.so.6 /usr/lib/libc_nonshared.a )

5.4 版本腳本(略)
5.5 GNU libtool(略)

5.6除去記號信息
  共享庫中的記號信息多爲調試之用,但佔用了磁盤空間。如果你的庫是爲嵌入式系統所用,最好去掉記號信息。一種方法,利用strip(1)命令,使用方法查看其幫助文檔;另一種方法,使用GNU LD選項-s或-S,例如"-Wl -s"或"-Wl -S"。-S僅除去調試記號信息;-s除去所有記號信息。

5.7編譯優化
  有一篇文章寫的不錯"Whirlwind tutorial On Creating really teensy ELF Executables For Linux"。這篇文章中可以說把程序的代碼優化到了極點。在我們實際的應用中,可能並需要那些技巧,但通過此文,我們可以更多的瞭解ELF。

5.8 C++與C
  要使得你編寫的共享庫能同時被C和C++程序使用,庫的頭文件需要使用"extern C"預定義,下面是一個例子:

  #ifndef LIB_HELLO_H
  #define LIB_HELLO_H

  #ifdef __cplusplus
  extern "C"
  {
  #endif

  .....頭文件代碼
 
  #ifdef __cplusplus
  }
  #endif

  #endif

5.9關於C++程序的啓動速度
  C++應用程序的啓動速度是比較慢的。我一直使用firefox,感受頗深。有人認爲這是因主函數啓動之前的代碼重定位所導致。有一篇文章"making C++ ready for the desktop"(by Waldo Bastian)對這問題作了分析。我讀了一下,理解不是很深刻。

5.10 Linux Standard Base(LSB)
  LSB是一個項目,致力於制訂和推動一系列標準,盡力提高不同Linux發佈版本之間的兼容性,從而爲應用程序的開發提供一致性的接口。關於linux標準項目的詳細信息,可查閱網站www.linuxbase.org




 

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1510037

 

 

 

 

 linux 下創建共享庫.so

類似Windows系統中的動態鏈接庫,Linux中也有相應的共享庫用以支持代碼的複用。Windows中爲*.dll,而Linux中爲*.so,我來詳細的告訴你如何在linux下編寫動態庫,以及如何使用它.

在linux下編寫動態鏈接庫的步驟:

 

1.      編寫庫的頭文件和源文件.

2.      把所有涉及到的源文件用如下方式編譯爲目標文件:

 

g++/gcc -g -c -fPIC -o library1.o library1.cpp

g++/gcc -g -c -fPIC -o library2.o library2.cpp
      ......

......

(註釋:-fPIC指通過這個選項來生成與位置無關的代碼,可以在任何地址被連接和裝載,-c指只編譯而不連接原程序)

 

3.      把所有的目標文件鏈接爲動態庫:

 

g++/gcc -g -shared -Wl,-soname,lib***.so -o lib***.so.1.0.0 library1.o library2.o ....  -lc

                

    (註釋:-lc選項,表示使用c語言庫,一般都要用到)

 

4.      建立一個庫名鏈接

 

ln -s lib***.so.1.0.0 lib***.so

 

現在你就可以引用庫了.下面我分別給出簡單例子告訴你如何動態和靜態使用動態庫:

假如你的應用程序源代碼叫testlib.cpp

採用/如下方式編譯:

 

      g++ -g -o testlib testlib.cpp -ldl

            

    (註釋:-ldl選項,表示生成的對象模塊需要使用共享庫)

 

////////這個例子告訴你如何動態的調用.so庫

testlib.cpp

#include <dlfcn.h>

#include <iostream.h>

#include ...

int main()

{

       void *handle=NULL;

       //define a pointer which will point to the function in the lib you want to use.

       YourFuntionType (*pFunc)(YourFunctionPerameterList........);

       //open the lib you want to use.

       handle=dlopen("/../../../yourlib.so",RTLD_LAZY);

       if(handle==NULL)

       {

              cout<<"failed loading library!"<<endl;

              return -1;

       }

       dlerror();

       //try to load the function in lib

       pFunc=(YourFuntionType(*)(YourFunctionPerameterList))dlsym(handle,"YourFuntionName");

       if(dlerror()!=NULL)

       {

              cout<<"Loading function in lib error!"<<endl;

              return -1;

       }

       //now you can use the funtion like this

       (*pFunc)(YourFuntionPerameterList);

       return 0;

}  

 

(註釋:dlopen()

              第一個參數:指定共享庫的名稱,將會在下面位置查找指定的共享庫。

-環境變量LD_LIBRARY_PATH列出的用分號間隔的所有目錄。

-文件/etc/ld.so.cache中找到的庫的列表,用ldconfig維護。

-目錄usr/lib。

-目錄/lib。

-當前目錄。(這裏就是這種情況)

 

第二個參數:指定如何打開共享庫。

-RTLD_NOW:將共享庫中的所有函數加載到內存

-RTLD_LAZY:             會推後共享庫中的函數的加載操作,直到調用dlsym()時方加載某函數

dlsym()

          調用dlsym時,利用dlopen()返回的共享庫的phandle以及函數名稱作爲參數,返回要加載函數的入口地址。

       dlerror()

    該函數用於檢查調用共享庫的相關函數出現的錯誤。

 

特別需要注意的幾點問題:

1.      當你想用c++寫動態庫的時候,記住千萬別忘了在頭文件裏面加上如下內容,否則生成的庫在動態調用的時候會出問題!!!!!!!

 #ifdef __cplusplus
       extern "C" {

#endif

....

....

   #ifdef __cplusplus

}

#endif 

 

2.      當你的庫中包括與omniORB3相關的東西的時候,一定要在makefile中加上 -D__x86__ -D__OSVERSION=4

 

/////////////這個例子告訴你如何靜態調用.so庫

首先你得確保你的應用程序能夠找到你的.so庫,這可以有幾種方法來實現.

方法一:

1.你可以把YourLib.so.1.0.0 和YourLib.so放到/usr/lib中,然後執行命令:ldconfig,這樣你就可以在你的應用程序中直接調用你庫中的函數了,當然你 得把庫的頭文件包含到你的應用程序中

2.編譯你的應用程序

g++/gcc -g -o yourapp yourapp.cpp –lYourLib

 

方法二:

1.你也可以採用在系統中設置環境變量的辦法來實現. 在root目錄下:

vi .bash_profile

然後添加LD_LIBRARY=/../YourDirIncludingYourLib

然後注消一次,環境變量就生效了,這樣你就可以在你的應用程序中直接調用庫中的函數了,同樣你得有頭文件.

2.編譯你的應用程序

g++/gcc -g -o yourapp yourapp.cpp –lYourLib

 

方法三:

你可以直接採用在編譯鏈接的時候告訴系統你的庫在什麼地方
       g++/gcc -g -o yourapp yourapp.cpp -L/YourDirIncludingYourLib –lYourLib

/////////////////////////////////

假如你的庫中有個函數:int eat(.....)

那麼採用如下方式調用它

yourapp.cpp

#include "YourLib.h"

int main()

{

       eat();

return 0;

}

 

是不是很easy?對了在靜態調用的時候好像不存在上面的"注意1"的問題,不過鑑於保險起見,最好還是按照標準的方式寫c++頭文件吧,這絕對是個好習慣.

 

 

 

 

 

 

 

 

一.庫的分類
    有兩種說法,如果熟悉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時,纔會真正被系統卸載。


發佈了8 篇原創文章 · 獲贊 32 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章