linux 下有動態庫和靜態庫,動態庫以.so爲擴展名,靜態庫以.a爲擴展名。兩者都使用普遍。本文主要講動態庫方面知識。node
基本上每個linux 程序都至少會有一個動態庫,查看某個程序使用了那些動態庫,使用ldd命令查看 linux
這麼多so,是的。使用ldd顯示的so,並非全部so都是須要使用的,下面舉個例子ios
main.cpp c++
#include <stdio.h> #include <iostream> #include <string> using namespace std; int main () { cout << "test" << endl; return 0; }
使用缺省參數編譯結果shell
若是我連接一些so,可是程序並不用到這些so,又是什麼狀況呢,下面我加入連接壓縮庫,數學庫,線程庫bash
看看,雖然沒有用到,可是同樣有連接進來,那看看程序啓動時候有沒有去加載它們呢函數
看,有加載,因此一定會影響進程啓動速度,因此咱們最後不要把無用的so編譯進來,這裏會有什麼影響呢?編碼
你們知不知道linux從程序(program或對象)變成進程(process或進程),要通過哪些步驟呢,這裏若是詳細的說,估計要另開一篇文章。簡單的說分三步:spa
一、fork進程,在內核建立進程相關內核項,加載進程可執行文件;.net
二、查找依賴的so,一一加載映射虛擬地址
三、初始化程序變量。
能夠看到,第二步中dll依賴越多,進程啓動越慢,而且發佈程序的時候,這些連接但沒有使用的so,一樣要一塊兒跟着發佈,不然進程啓動時候,會失敗,找不到對應的so。因此咱們不能像上面那樣,把一些毫無心義的so連接進來,浪費資源。可是開發人員寫makefile 通常有沒有那麼細心,圖省事方便,那麼有什麼好的辦法呢。繼續看下去,下面會給你解決方法。
先使用 ldd -u demo 查看不須要連接的so,看下面,一面瞭然,無用的so所有暴露出來了吧
使用 -Wl,--as-needed 編譯選項
呵呵,辦法很簡單省事吧,本文主要講so依賴的一些問題,下一篇將介紹so的路徑方面一些鮮爲人知的小祕密
咱們知道linux連接so 有兩種途徑:顯示和隱式。所謂顯示就是程序主動調用dlopen打開相關so;這裏須要補充的是,若是使用顯示連接,上篇文章討論的那些問題都不存在。首先,dlopen的so使用ldd是查看不到的。其次,使用dlopen打開的so並非在進程啓動時候加載映射的,而是當進程運行到調用dlopen代碼地方纔加載該so,也就是說,若是每一個進程顯示連接a.so;可是若是發佈該程序時候忘記附帶發佈該a.so,程序仍然可以正常啓動,甚至若是運行邏輯沒有觸發運行到調用dlopen函數代碼地方。該程序還能正常運行,即便沒有a.so.
既然顯示加載這麼多優勢,那麼爲何實際生產中不多碼農使用它呢, 主要緣由仍是起使用不是很方便,須要開發人員多寫很多代碼。因此不被大多數碼農使用,還有一個重要緣由應該是能提早發現錯誤,在部署的時候就能發現缺乏哪些so,而不是等到實際上限運行的時候才發現缺東少西。
下面舉個工做中最常碰到的問題,來引伸出本篇內容吧。
寫一個最簡單的so, tmp.cpp
int test() { return 20; }
編譯=>連接=》運行, 下面main.cpp 內容請參見上一篇文章。
[stevenrao]$ g++ -fPIC -c tmp.cpp
[stevenrao]$ g++ -shared -o libtmp.so tmp.o
[stevenrao]$ mv libtmp.so /tmp/
[stevenrao]$ g++ -o demo -L/tmp -ltmp main.cpp
[stevenrao]$ ./demo
./demo: error while loading shared libraries: libtmp.so: cannot open shared object file: No such file or directory
[stevenrao]$ ldd demo
linux-vdso.so.1 => (0x00007fff7fdc1000)
libtmp.so => not found
這個錯誤是最多見的錯誤了。運行程序的時候找不到依賴的so。通常人使用方法是修改LD_LIBRARY_PATH這個環境變量
export LD_LIBRARY_PATH=/tmp
[stevenrao]$ ./demo
test
這樣就OK了, 不過這樣export 只對當前shell有效,當另開一個shell時候,又要從新設置。能夠把export LD_LIBRARY_PATH=/tmp 語句寫到 ~/.bashrc中,這樣就對當前用戶有效了,寫到/etc/bashrc中就對全部用戶有效了。
前面連接時候使用 -L/tmp/ -ltmp 是一種設置相對路徑方法,還有一種絕對路徑連接方法。
[stevenrao]$ g++ -o demo /tmp/libtmp.so main.cpp
[stevenrao]$ ./demo
test
[stevenrao]$ ldd demo
linux-vdso.so.1 => (0x00007fff083ff000)
/tmp/libtmp.so (0x00007f53ed30f000)
絕對路徑雖然申請設置環境變量步驟,可是缺陷也是致命的,這個so必須放在絕對路徑下,不能放到其餘地方,這樣給部署帶來很大麻煩。因此應該禁止使用絕對路徑連接so。
搜索路徑分兩種,一種是連接時候的搜索路徑,一種是運行時期的搜索路徑。像前面提到的 -L/tmp/ 是屬於連接時期的搜索路徑,即給ld程序提供的編譯連接時候尋找動態庫路徑;而 LD_LIBRARY_PATH則既屬於連接期搜索路徑,又屬於運行時期的搜索路徑。
這裏須要介紹鏈-rpath連接選項,它是指定運行時候都使用的搜索路徑。聰明的同窗立刻就想到,運行時搜索路徑,那它記錄在哪兒呢。也像. LD_LIBRARY_PATH那樣,每部署一臺機器就須要配一下嗎。呵呵,不須要..,由於它已經被硬編碼到可執行文件內部了。看看下面演示
[stevenrao] $ g++ -o demo -L /tmp/ -ltmp main.cpp
[stevenrao] $ ./demo
./demo: error while loading shared libraries: libtmp.so: cannot open shared object file: No such file or directory
[stevenrao] $ g++ -o demo -Wl,-rpath /tmp/ -L/tmp/ -ltmp main.cpp
[stevenrao] $ ./demo
test
[stevenrao] $ readelf -d demo
Dynamic section at offset 0xc58 contains 26 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libtmp.so]
0x0000000000000001 (NEEDED) Shared library: [libstdc++.so.6]
0x0000000000000001 (NEEDED) Shared library: [libm.so.6]
0x0000000000000001 (NEEDED) Shared library: [libgcc_s.so.1]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000000f (RPATH) Library rpath: [/tmp/]
0x000000000000001d (RUNPATH) Library runpath: [/tmp/]
看看是吧,編譯到elf文件內部了,路徑和程序深深的耦合到一塊兒
還有一個相似於-path,叫LD_RUN_PATH環境變量, 它也是把路徑編譯進可執行文件內,不一樣的是它只設置RPATH。
[stevenrao] $ g++ -o demo -L /tmp/ -ltmp main.cpp
[stevenrao] $ readelf -d demo
Dynamic section at offset 0xb98 contains 25 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libtmp.so]
....
0x000000000000000f (RPATH) Library rpath: [/tmp/]
另外還能夠經過配置/etc/ld.so.conf,在其中加入一行
/tmp/
這個配置項也是隻對運行期有效,而且是全局用戶都生效,須要root權限修改,修改完後須要使用命令ldconfig 將 /etc/ld.so.conf 加載到ld.so.cache中,避免重啓系統就能夠當即生效。
除了前面介紹的那些搜索路徑外,還有缺省搜索路徑/usr/lib/ /lib/ 目錄,能夠經過-z nodefaultlib編譯選項禁止搜索缺省路徑。
[stevenrao] $ g++ -o demo -z nodefaultlib -L/tmp -ltmp main.cpp
[stevenrao] $ ./demo
./demo: error while loading shared libraries: libstdc++.so.6: cannot open shared object file
這麼多搜索路徑,他們有個前後順序以下
一、RUMPATH 優先級最高
二、RPATH 其次
三、LD_LIBRARY_PATH
四、/etc/ld.so.cache
五、/usr/lib/ /lib/
查看一個程序搜索其各個動態庫另外一個簡單的辦法是使用 LD_DEBUG這個環境變量;
[stevenrao] $ export LD_DEBUG=libs
[stevenrao] $ ./demo
注:本文轉載自http://blog.csdn.net/suwei19870312/article/details/20045281