linux 下有動態庫和靜態庫,動態庫以.so爲擴展名,靜態庫以.a爲擴展名。兩者都使用普遍。本文主要講動態庫方面知識。linux
基本上每個linux 程序都至少會有一個動態庫,查看某個程序使用了那些動態庫,使用ldd命令查看 ios
# ldd /bin/ls
c++
linux-vdso.so.1 => (0x00007fff597ff000)
spa
libselinux.so.1 => /lib64/libselinux.so.1 (0x00000036c2e00000)
線程
librt.so.1 => /lib64/librt.so.1 (0x00000036c2200000)
code
libcap.so.2 => /lib64/libcap.so.2 (0x00000036c4a00000)
對象
libacl.so.1 => /lib64/libacl.so.1 (0x00000036d0600000)
進程
libc.so.6 => /lib64/libc.so.6 (0x00000036c1200000)
ci
libdl.so.2 => /lib64/libdl.so.2 (0x00000036c1600000)
資源
/lib64/ld-linux-x86-64.so.2 (0x00000036c0e00000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00000036c1a00000)
libattr.so.1 => /lib64/libattr.so.1 (0x00000036cf600000)
這麼多so,是的。使用ldd顯示的so,並非全部so都是須要使用的,下面舉個例子
main.cpp
#include <stdio.h> #include <iostream> #include <string> using namespace std; int main () { cout << "test" << endl; return 0; }
使用缺省參數編譯結果
# g++ -o demo main.cpp
# ldd demo
linux-vdso.so.1 => (0x00007fffcd1ff000)
libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x00007f4d02f69000)
libm.so.6 => /lib64/libm.so.6 (0x00000036c1e00000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00000036c7e00000)
libc.so.6 => /lib64/libc.so.6 (0x00000036c1200000)
/lib64/ld-linux-x86-64.so.2 (0x00000036c0e00000)
若是我連接一些so,可是程序並不用到這些so,又是什麼狀況呢,下面我加入連接壓縮庫,數學庫,線程庫
# g++ -o demo -lz -lm -lrt main.cpp
# ldd demo
linux-vdso.so.1 => (0x00007fff0f7fc000)
libz.so.1 => /lib64/libz.so.1 (0x00000036c2600000)
librt.so.1 => /lib64/librt.so.1 (0x00000036c2200000)
libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x00007ff6ab70d000)
libm.so.6 => /lib64/libm.so.6 (0x00000036c1e00000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00000036c7e00000)
libc.so.6 => /lib64/libc.so.6 (0x00000036c1200000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00000036c1a00000)
/lib64/ld-linux-x86-64.so.2 (0x00000036c0e00000)
看看,雖然沒有用到,可是同樣有連接進來,那看看程序啓動時候有沒有去加載它們呢
# strace ./demo
execve("./demo", ["./demo"], [/* 30 vars */]) = 0
... = 0
open("/lib64/libz.so.1", O_RDONLY) = 3
...
close(3) = 0
open("/lib64/librt.so.1", O_RDONLY) = 3
...
close(3) = 0
open("/usr/lib64/libstdc++.so.6", O_RDONLY) = 3
...
close(3) = 0
open("/lib64/libm.so.6", O_RDONLY) = 3
...
close(3) = 0
open("/lib64/libgcc_s.so.1", O_RDONLY) = 3
...
close(3) = 0
open("/lib64/libc.so.6", O_RDONLY) = 3
...
close(3) = 0
open("/lib64/libpthread.so.0", O_RDONLY) = 3
...
close(3) = 0
...
看,有加載,因此一定會影響進程啓動速度,因此咱們最後不要把無用的so編譯進來,這裏會有什麼影響呢?
你們知不知道linux從程序(program或對象)變成進程(process或進程),要通過哪些步驟呢,這裏若是詳細的說,估計要另開一篇文章。簡單的說分三步:
一、fork進程,在內核建立進程相關內核項,加載進程可執行文件;
二、查找依賴的so,一一加載映射虛擬地址
三、初始化程序變量。
能夠看到,第二步中dll依賴越多,進程啓動越慢,而且發佈程序的時候,這些連接但沒有使用的so,一樣要一塊兒跟着發佈,不然進程啓動時候,會失敗,找不到對應的so。因此咱們不能像上面那樣,把一些毫無心義的so連接進來,浪費資源。可是開發人員寫makefile 通常有沒有那麼細心,圖省事方便,那麼有什麼好的辦法呢。繼續看下去,下面會給你解決方法。
先使用 ldd -u demo 查看不須要連接的so,看下面,一面瞭然,無用的so所有暴露出來了吧
# ldd -u demo
Unused direct dependencies:
/lib64/libz.so.1
/lib64/librt.so.1
/lib64/libm.so.6
/lib64/libgcc_s.so.1
使用 -Wl,--as-needed 編譯選項
# g++ -Wl,--as-needed -o demo -lz -lm -lrt main.cpp
# ldd demo
linux-vdso.so.1 => (0x00007fffebfff000)
libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x00007ff665c05000)
libc.so.6 => /lib64/libc.so.6 (0x00000036c1200000)
libm.so.6 => /lib64/libm.so.6 (0x00000036c1e00000)
/lib64/ld-linux-x86-64.so.2 (0x00000036c0e00000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00000036c7e00000)
# ldd -u demo
Unused direct dependencies:
呵呵,辦法很簡單省事吧,本文主要講so依賴的一些問題,下一篇將介紹so的路徑方面一些鮮爲人知的小祕密