1 Linux應用程序可能會使用到兩種函數庫,一種靜態庫、一種動態庫,靜態庫以.a為擴展名,動態庫以.so為擴展名。二者都使用廣泛。
2 動態庫和靜態庫的基本概念?
靜態庫,是在可執行程序連接時就已經加入到執行碼中,在物理上成為執行程序的一部分;使用靜態庫編譯的程序運行時無需該庫文件支持,哪里都可以用,但是生成的可執行文件較大。動態庫,是在可執行程序啟動時加載到執行程序中,可以被多個可執行程序共享使用。使用動態庫編譯生成的程序相對較小,但運行時需要庫文件支持,如果機器里沒有這些庫文件就不能運行。
3 如何使用動態庫?
動態庫也叫共享庫,如果在程序連接時使用共享庫,就必須在運行時找到共享庫的位置。Linux的可執行程序在執行的時候默認是先搜索/lib和/usr/lib這兩個目錄,然后按照/etc/ld.so.conf里面的配置搜索絕對路徑。同時,linux也提供了環境變量LD_LIBRARY_PATH供用戶選擇使用,用戶可以通過它來查找默認路徑之外的其他路徑,如要查找/work/lib路徑,你可以在/etc/rc.d/rc.local或其他系統啟動后即可執行到的腳本文件中添加如下語句:LD_LIBRARY_PATH=/work/lib:$(LD_LIBRARY_PATH)。并且LD_LIBRARY_PATH路徑優先于系統默認路徑之前查找。
不過LD_LIBRARY_PATH的設定作用是全局的,過多的使用可能會影響到其他應用程序的運行,所以多用在調試。
4 庫的連接路經和運行路經
現代連接器在處理動態庫時將鏈接時路徑(Link-time path)和運行時路徑(Run-time path)分開,用戶可以通過-L指定連接時庫的路徑,通過-R(或-rpath)指定程序運行時庫的路徑,大大提高了庫應用的靈活性。比如我們做嵌入式移植時#arm-linux-gcc $(CFLAGS) –o target –L/work/lib/zlib/ -llibz-1.2.3 (work/lib/zlib下是交叉編譯好的zlib庫),將target編譯好后我們只要把zlib庫拷貝到開發板的系統默認路徑下即可。或者通過- rpath(或-R )、LD_LIBRARY_PATH指定查找路徑
5 動態庫的加載使用?
基本上每個linux程序都至少會用一個動態庫,查看某個程序使用了那些動態庫,使用命令ldd查看
如:#ldd /bin/ls (查看系統中ls用到的動態庫)
#ldd main (查看自定義main用到的動態庫)
#ldd -u main(查看自定義main中無用的動態庫)
#strace ./main(查看程序啟動時加載的所有動態庫)
Linux程序啟動時加載的庫有默認的庫也有顯式手動連接的庫
1 默認:編譯代碼時不手動連接額外庫,那么在代碼中使用到庫函數時會在運行時自動加載連接需要的庫。
#gcc -o main main.c
#ldd main
linux-vdso.so.1 => (0x00007fffa1b6d000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff4fde52000)
/lib64/ld-linux-x86-64.so.2 (0x00007ff4fe230000)
2 手動:編譯代碼時手動添加額外的庫,那么在程序運行時,既要加載默認的庫,還要加載手動添加的庫。加載的庫多了,會影響程序啟動的速度。
#gcc -o main1 -lm -lrt main.c (編譯時手動連接數學庫、線程庫)
#ldd main1
linux-vdso.so.1 => (0x00007fff88770000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f993a0cc000)
/lib64/ld-linux-x86-64.so.2 (0x00007f993a4aa000)
#strace ./main1
從程序運行中可以看出main1運行時即加載了默認庫,也加載了手動指定的庫(在此手動指定的數學庫、線程庫是代碼不需要的,但在程序運行時也加載了),會影響整個程序的加載速度。
大家知不知道linux從程序(program或對象)變成進程(process或進程),要經過哪些步驟呢,簡單的說分三步:
1、fork進程,在內核創建進程相關內核項,加載進程可執行文件;
2、查找依賴的so,一一加載映射虛擬地址
3、初始化程序變量。
可以看到,第二步中dll依賴越多,進程啟動越慢,并且發布程序的時候,有這些鏈接但沒有使用的so,同樣要一起跟著發布,否則進程啟動時候,會失敗,找不到對應的so。所以我們不能像上面那樣,把一些毫無意義的so鏈接進來,浪費資源。
6 關于Linux程序連接so有兩種方式:隱式和顯示
所謂顯示就是程序主動調用dlopen打開相關so;首先,dlopen的so使用ldd是查看不到的。其次,使用dlopen打開的so并不是在進程啟動時候加載映射的,而是當進程運行到調用dlopen代碼地方才加載該so,也就是說,如果每個進程顯示鏈接a.so;但是如果發布該程序時候忘記附帶發布該a.so,程序仍然能夠正常啟動,甚至如果運行邏輯沒有觸發運行到調用dlopen函數代碼地方。該程序還能正常運行,即使沒有a.so. 既然顯示加載這么多優點,那么為什么實際生產中很少碼農使用它呢, 主要原因還是起使用不是很方便,需要開發人員多寫不少代碼。所以不被大多數碼農使用,還有一個重要原因應該是能提前發現錯誤,在部署的時候就能發現缺少哪些so,而不是等到實際上限運行的時候才發現缺東少西。
华清图书馆
0元电子书,限时免费申领10本华清图书PDF版
扫码关注华清远见公众号