傳統方式下,庫函數的鏈接是在編譯器完成的,所有相關對象在編譯的時候被整合成一個可執行文件。與此相比,我們也可以把對庫函數的鏈接載入推遲到程序運行的時期,也就是我們所稱作的動態鏈接。
動態鏈接的優點
除了靜態鏈接庫所有的模塊化和代碼復用外,動態鏈接庫還有如下優點。
-
可以實現進程之間的庫共享:
當多個進程共享一個庫時(如stl庫和一些系統庫是基本上大多數程序都用的),動態鏈接方式可以只在內存中保留一份副本,節約內存。 -
升級變得簡單:
用戶只需要升級動態鏈接庫,而無需重新編譯鏈接其他原有的代碼就可以完成整個程序的升級(很多Windows的補丁就是這種方式發布的)。 -
可以動態載入:
當軟件比較大的時候,可以根據需要動態載入/卸載相應的鏈接庫,而無需像靜態鏈接的方式那樣必須一次性全部載入
創建動態鏈接庫
創建動態鏈接庫的方式比較簡單,以前文靜態鏈接庫的例子為例,我們只需要通過gcc -shared指令即可創建一個libstack.so的動態庫(靜態庫一般以.a作為擴展名,動態庫一般以.so作為擴展名)。
gcc -shared -o libstack.so stack.o
在鏈接階段使用動態庫的方式基本上和靜態庫一致。
gcc -o run main.c -L. –lstack
編譯玩這個程序后,我們執行后卻發現,它報動態鏈接庫找不到的錯誤提示。
tianfang > ./run
./run: error while loading shared libraries: libstack.so: cannot open shared object file: No such file or directory
我們也可以通過ldd命令查看某程序當前對動態鏈接庫的依賴情況:
tianfan > ldd run
linux-gate.so.1 (0xb7707000)
libstack.so => not found
libc.so.6 => /lib/libc.so.6 (0xb7546000)
/lib/ld-linux.so.2 (0xb7708000)
ldd的結果表明了我們生成的libstack.so找不到。因為動態鏈接庫是一個可以共享的文件,因此往往存放在一個公共的位置,在Linux系統中程序查找動態鏈接庫的規則如下:
-
首先在環境變量LD_LIBRARY_PATH所記錄的路徑中查找。
-
然后從緩存文件/etc/ld.so.cache中查找。
-
如果上述步驟都找不到,則到默認的系統路徑中查找,先是/lib然后是/usr/lib。
很明顯,這幾個路徑都不包含當前路徑。要解決上述問題,一個簡單的方式就是把當前路徑加到環境變量中:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
然后再次用ldd名稱測試,發現現在就能找到我們的鏈接庫了。
tianfan > ldd run
linux-gate.so.1 (0xb77ce000)
libstack.so => ./libstack.so (0xb77ca000)
libc.so.6 => /lib/libc.so.6 (0xb760a000)
/lib/ld-linux.so.2 (0xb77cf000)
不過,很多大牛並不建議通過修改LD_LIBRARY_PATH這種方式
-
LD_LIBRARY_PATH is not the answer http://prefetch.net/articles/linkers.badldlibrary.html
-
Why LD_LIBRARY_PATH is bad http://xahlee.org/UnixResource_dir/_/ldpath.html
-
LD_LIBRARY_PATH - just say no http://blogs.sun.com/rie/date/20040710
小結
本文主要介紹了一下如何通過gcc命令創建動態鏈接庫,如何通過ldd命令查看對動態鏈接庫的依賴情況和解決動態鏈接庫找不到的問題。並沒有對動態鏈接庫的原理進行詳細的介紹,感興趣的朋友可以在網上查找相關文章。