linux下動態共享庫的建立,使用與更新(包括ldconfig的使用)

查志強發表於2014-06-21

【原文:http://blog.csdn.net/the9thbit/article/details/5902107

一 建立並編譯共享庫

動態連結庫一般以lib開頭,形如libmymodule.so.1.0.0.   

後面跟的三個版本號,從左到右的含義為:

(1) 大版本號,當介面變得和之前不相容,則新增一個大版本號。

(2) 一般增加了介面,不過舊的介面不變,則新增此版本號。

(3) 介面不做任何變化,只是實現做了修改,則新增此版本號。 

假設我們的庫只包含 module.cpp, 則用命令

[c-sharp] view plaincopy
  1. g++ -fPIC -Wall -c module.cpp  

生成module.o,再用命令

[c-sharp] view plaincopy
  1. g++ -shared -Wl,-soname,libmymodule.so.1 -o libmymodule.so.1.0.0 module.o  

生成libmymodule.so.1.0.0共享庫。這裡的libmymodule.so.1.0.0也叫做共享庫的real name.

  • -fPIC是使生成的目標檔案“位置無關(Position Independent Code)”從而可以被多個程式共享。
  • -shared 指定產生共享庫。
  • -Wl,-soname,libmymodule.so.1 指定共享庫的soname為libmymodule.so.1,若不指定,則無soname. soname的作用後面會提到。可以用objdump -p libmymodule.so.1.0.0 | grep SONAME  檢視soname. 

 一般soname帶且只帶大版本號,比如這裡的libmymodule.so.1, 是因為共享庫的相互相容的不同版本,都具有相同的soname(在升級的時候,需要用soname來對應)。而如果大版本號變了,意味著介面變得不相容了,也就沒有必要讓這個版本和之前的共享庫相容了。這個時候,啟用一個新的soname是更好的做法。

二 編譯主程式

因為gcc中,用-l引數指定的庫檔案必須滿足格式lib*.so 所以我們需要建立軟連線libmymodule.so, 並令其指向libmymodule.so.1.0.0

[c-sharp] view plaincopy
  1. ln -s libmymodule.so.1.0.0 libmymodule.so  

這裡的libmymodule.so就是所謂的"link name"。 

假設我們的主程式(相對於庫來說),程式碼在main.cpp裡,則用命令

[c-sharp] view plaincopy
  1. g++ -o main main.cpp -L. -lmymodule  

生產可執行程式main。注意link name只在編譯的時候需要,主程式會記住根據這個檔案最終所指的共享庫的soname,用來在執行的時候,查詢載入動態庫。如果共享庫沒有指定soname, 那麼主程式會記住這個'link name",也就是lib*.so這樣形式的名字,做為soname, 在執行的時候,用這個名字來查詢載入動態庫。

  • -L指定搜尋庫的資料夾,
  • -l 指定所依賴的庫。注意這裡需要去掉前面的“lib”和後面的“.so”

 三 執行期 

這個時候,執行./main會出現以下錯誤:

./main: error while loading shared libraries: libmymodule.so.1: cannot open shared object file: No such file or directory 

使用ldd main可以檢視所依賴的動態庫是否被滿足,執行“ldd main":

發現有一行libmymodule.so.1 => not found,說明在執行期,載入器沒有找到對應的共享庫檔案。 

載入器會在以下地方查詢main程式中記住的所需要的庫的soname:

(1) /etc/ld.so.cache 這是一個cache,存放soname到共享庫檔案的soname link (一個檔名為soname的軟連線) 的對映(key->value值對)。

(2) /usr/lib 和 /lib

(3) 環境變數LD_LIBRARY_PATH指定的資料夾。

可以用ldconfig 命令,解決這個問題。ldconfig主要做2件事情:

一是掃描/lib和/usr/lib和/etc/ld.so.conf裡指定資料夾,對裡面的共享庫建立soname link (ldconfig會根據檔名裡的版本號,自動找到最新的共享庫檔案,並把soname link指向這個最新的共享庫檔案)

二是更新/etc/ld.so.cache,建立soname到soname link的對映。 

載入器如果在ld.so.cache中查到所需的soname,則會依次找到 soname-->soname link-->實際的共享庫檔案。如果找不到,則會在/usr/lib和/lib中查詢檔名為soname的檔案,作為共享庫檔案載入。注意,在這裡,載入器不會去找/etc/ld.so.conf裡指定的資料夾,只會找/usr/lib和/lib所以如果在/etc/ld.so.conf裡指定的資料夾,增加了soname link,必須要執行ldconfig來更新ld.so.cache。 

ldconfig -n /path/to/dir 命令可以指定某資料夾,不過只會做第一件事,也就是建立soname link-->實際的共享庫檔案。不會更新ld.so.cache 所以不需要root許可權。 

ldconfig -p 命令可以列印ld.so.conf裡已經有的鍵值對。 

好了,瞭解了以上知識,我們可以用以下方法解決找不到共享庫的問題: 

(a) 將libmymodule.so.1.0.0拷到/lib或者/usr/lib裡,或者/etc/ld.so.conf指定的資料夾,然後執行sudo ldconfig. 於是ldconfig會自動在/lib或/usr/lib或/etc/ld.so.conf指定的資料夾裡生成soname link (檔名為libmymodule.so.1) 指向libmymodule.so.1.0.0,然後在ld.so.cache中增加libmymodule.so.1到/lib /libmymodule.so.1或/usr/lib/libmymodule.so.1的對映(前者是個名字,後者是個軟連線檔案) 

(b) 使用LD_LIBRARY_PATH=/PATH/TO/SO ./main來執行程式。前提是在/PATH/TO/SO中建立一個名字為soname的軟連線,使其指向實際的共享檔案。 

(c) 在/lib或/usr/lib中,手動建立軟連線 libmymodule.so.1 令其指向實際的共享庫檔案 libmymodule.so.1.0.0。 因為前面提到的,載入器在ld.so.cache中找不到要找的鍵為soname的項,會在/lib或/usr/lib中找名字為soname的檔案。這方法雖然可以用,不過感覺比較粗暴。不推薦。

四 升級共享庫

如果是用上述(a)方法使用共享庫的,只需要將新的共享庫,比如 libmymodule.so.1.0.1拷到原來的目錄(/lib或/usr/lib或者/etc/ld.so.conf指定的資料夾),然後執行 ldconfig即可。ldconfig會更新soname link, 使其指向最新的共享庫檔案libmymodule.so.1.0.1,ld.so.cache不需要更新。 

如果是用上述(b)(c)方法是用共享庫的,需要更新相應的軟連線,使其指向最新的共享庫檔案。 

 

五 動態載入共享庫

在主程式中動態載入共享庫時,如果指定的是絕對路徑("/"開頭的),則載入該絕對路徑指向的共享庫檔案。如果不是絕對路徑,而是一個檔名,則將這個檔名當作soname,然後依照上述的方法,查詢載入相應的共享庫檔案。

六 注意點

如果在共享庫中,要用到主程式的變數或者方法,要在編譯主程式時,加上-rdynamic引數,使得所有名字在共享庫的空間中可見。


相關文章