1. 程式人生 > >linux下so動態庫一些不為人知的祕密(上)

linux下so動態庫一些不為人知的祕密(上)

相關文章

linux 下有動態庫和靜態庫,動態庫以.so為副檔名,靜態庫以.a為副檔名。二者都使用廣泛。本文主要講動態庫方面知識。

   基本上每一個linux 程式都至少會有一個動態庫,檢視某個程式使用了那些動態庫,使用ldd命令檢視 
  1. # ldd /bin/ls
  2. linux-vdso.so.=> (0x00007fff597ff000)
  3. libselinux.so.=> /lib64/libselinux.so.(0x00000036c2e00000)
  4. librt.so.=> /lib64/librt.so.(0x00000036c2200000)
  5. libcap.so.=> /lib64/libcap.
    so.(0x00000036c4a00000)
  6. libacl.so.=> /lib64/libacl.so.(0x00000036d0600000)
  7. libc.so.=> /lib64/libc.so.(0x00000036c1200000)
  8. libdl.so.=> /lib64/libdl.so.(0x00000036c1600000)
  9. /lib64/ld-linux-x86-64.so.(0x00000036c0e00000)
  10. libpthread.so.=> /lib64/libpthread.so.(0x00000036c1a00000)
  11. libattr.so.=> /lib64/libattr.so.(0x00000036cf600000)
   這麼多so,是的。使用ldd顯示的so,並不是所有so都是需要使用的,下面舉個例子
main.cpp
  1. #include <stdio.h>
  2. #include <iostream>
  3. #include <string>
  4. using namespace std;
  5. int main ()
  6. {
  7.    cout << "test" << endl;
  8.    return 0;
  9. }
   使用預設引數編譯結果
  1. # g++ -o demo main.cpp
  2. # ldd demo
  3.     linux-vdso.so.=> (0x00007fffcd1ff000)
  4.         libstdc++.so.=> /usr/
    lib64/libstdc++.so.(0x00007f4d02f69000)
  5.         libm.so.=> /lib64/libm.so.(0x00000036c1e00000)
  6.         libgcc_s.so.=> /lib64/libgcc_s.so.(0x00000036c7e00000)
  7.         libc.so.=> /lib64/libc.so.(0x00000036c1200000)
  8.         /lib64/ld-linux-x86-64.so.(0x00000036c0e00000)
   如果我連結一些so,但是程式並不用到這些so,又是什麼情況呢,下面我加入連結壓縮庫,數學庫,執行緒庫
  1. # g++ -o demo -lz -lm -lrt main.cpp
  2. # ldd demo
  3.         linux-vdso.so.=> (0x00007fff0f7fc000)
  4.         libz.so.1 => /lib64/libz.so.1 (0x00000036c2600000)
  5.         librt.so.1 => /lib64/librt.so.1 (0x00000036c2200000)
  6.         libstdc++.so.=> /usr/lib64/libstdc++.so.(0x00007ff6ab70d000)
  7.         libm.so.6 => /lib64/libm.so.6 (0x00000036c1e00000)
  8.         libgcc_s.so.=> /lib64/libgcc_s.so.(0x00000036c7e00000)
  9.         libc.so.=> /lib64/libc.so.(0x00000036c1200000)
  10.         libpthread.so.=> /lib64/libpthread.so.(0x00000036c1a00000)
  11.         /lib64/ld-linux-x86-64.so.(0x00000036c0e00000)
  看看,雖然沒有用到,但是一樣有連結進來,那看看程式啟動時候有沒有去載入它們呢
  1. # strace ./demo
  2.     execve("./demo", ["./demo"], [/* 30 vars */]) = 0
  3.     ... = 0
  4.     open("/lib64/libz.so.1", O_RDONLY) = 3
  5.     ...
  6.     close(3) = 0
  7.     open("/lib64/librt.so.1", O_RDONLY) = 3
  8.     ...
  9.     close(3) = 0
  10.     open("/usr/lib64/libstdc++.so.6", O_RDONLY) = 3
  11.     ...
  12.     close(3) = 0
  13.     open("/lib64/libm.so.6", O_RDONLY) = 3
  14.     ...
  15.     close(3) = 0
  16.     open("/lib64/libgcc_s.so.1", O_RDONLY) = 3
  17.     ...
  18.     close(3) = 0
  19.     open("/lib64/libc.so.6", O_RDONLY) = 3
  20.     ...
  21.     close(3) = 0
  22.     open("/lib64/libpthread.so.0", O_RDONLY) = 3
  23.     ...
  24.     close(3) = 0
  25.     ...
  看,有載入,所以必定會影響程序啟動速度,所以我們最後不要把無用的so編譯進來,這裡會有什麼影響呢?    大家知不知道linux從程式(program或物件)變成程序(process或程序),要經過哪些步驟呢,這裡如果詳細的說,估計要另開一篇文章。簡單的說分三步:     1、fork程序,在核心建立程序相關核心項,載入程序可執行檔案;     2、查詢依賴的so,一一載入對映虛擬地址     3、初始化程式變數。   可以看到,第二步中dll依賴越多,程序啟動越慢,並且釋出程式的時候,這些連結但沒有使用的so,同樣要一起跟著釋出,否則程序啟動時候,會失敗,找不到對應的so。所以我們不能像上面那樣,把一些毫無意義的so連結進來,浪費資源。但是開發人員寫makefile 一般有沒有那麼細心,圖省事方便,那麼有什麼好的辦法呢。繼續看下去,下面會給你解決方法。   先使用 ldd -u demo 檢視不需要連結的so,看下面,一面瞭然,無用的so全部暴露出來了吧
  1. # ldd -u demo
  2. Unused direct dependencies:
  3.         /lib64/libz.so.1
  4.         /lib64/librt.so.1
  5.         /lib64/libm.so.6
  6.         /lib64/libgcc_s.so.1
  使用 -Wl,--as-needed 編譯選項
  1. # g++ -Wl,--as-needed -o demo -lz -lm -lrt main.cpp
  2. # ldd demo
  3.         linux-vdso.so.=> (0x00007fffebfff000)
  4.         libstdc++.so.=> /usr/lib64/libstdc++.so.(0x00007ff665c05000)
  5.         libc.so.=> /lib64/libc.so.(0x00000036c1200000)
  6.         libm.so.=> /lib64/libm.so.(0x00000036c1e00000)
  7.         /lib64/ld-linux-x86-64.so.(0x00000036c0e00000)
  8.         libgcc_s.so.=> /lib64/libgcc_s.so.(0x00000036c7e00000)
  9. # ldd -u demo
  10. Unused direct dependencies:
  呵呵,辦法很簡單省事吧,本文主要講so依賴的一些問題,下一篇將介紹so的路徑方面一些不為人知的小祕密