Linux共享庫如何進(jìn)行版本控制
掃描二維碼
隨時隨地手機(jī)看文章
大家平時使用Linux系統(tǒng)過程中可能都見過文件系統(tǒng)里有好多帶版本號的共享庫,如下:
lrwxrwxrwx 1 root root 21 Mar 25 18:33 libDeployPkg.so.0 -> libDeployPkg.so.0.0.0
-rw-r--r-- 1 root root 31304 Mar 25 18:33 libDeployPkg.so.0.0.0
lrwxrwxrwx 1 root root 20 Mar 25 18:33 libguestlib.so.0 -> libguestlib.so.0.0.0
-rw-r--r-- 1 root root 26752 Mar 25 18:33 libguestlib.so.0.0.0
lrwxrwxrwx 1 root root 16 Mar 25 18:33 libhgfs.so.0 -> libhgfs.so.0.0.0
-rw-r--r-- 1 root root 167248 Mar 25 18:33 libhgfs.so.0.0.0
lrwxrwxrwx 1 root root 18 Mar 25 18:33 libvgauth.so.0 -> libvgauth.so.0.0.0
-rw-r--r-- 1 root root 85144 Mar 25 18:33 libvgauth.so.0.0.0
lrwxrwxrwx 1 root root 19 Mar 25 18:33 libvmtools.so.0 -> libvmtools.so.0.0.0
-rw-r--r-- 1 root root 631480 Mar 25 18:33 libvmtools.so.0.0.0
大家平時關(guān)注過這些共享庫的版本號是以什么規(guī)則制定的呢?
以"libname.so.x.y.z"來說:
最前面使用"lib"前綴,中間是庫的名字和后綴".so",后面跟著三個數(shù)字組成版本號
x為主版本號,表示庫的重大升級,不同主版本號的庫之間是不兼容的,依賴于舊版本號的程序要修改相應(yīng)的代碼來適應(yīng)新版本的庫,并重新編譯才可以鏈接新版本庫運行。
y為次版本號,表示庫的增量升級,相對于舊版本只是增加了一些新的接口,并保持原有的接口符號不變,完全兼容舊版本的庫,即一個依賴于舊版本號的程序可以直接鏈接新版本庫來正常運行。
z為發(fā)布版本號,表示庫的一些錯誤修正、性能優(yōu)化等,不會增加新的接口,只是在舊版本庫的基礎(chǔ)上做一些優(yōu)化。
如何創(chuàng)建共享庫?
首先介紹一些SO-NAME,每一個共享庫都有一個SO-NAME,即共享庫的文件名去掉次版本號和發(fā)布版本號,比如"libname.so.x.y.z"的SO_NAME就是"libname.so.x"。在Linux系統(tǒng)中,系統(tǒng)會為每個共享庫在所在的目錄中創(chuàng)建一個與SO-NAME同名并且指向?qū)嶋H共享庫的軟鏈接。例如"libc.so.1"會指向"libc.so.1.2.3",當(dāng)目錄中有"libc.so.1.2.4"時,"libc.so.1"這個軟鏈就會指向"libc.so.1.2.4",達(dá)到升級的目的。那系統(tǒng)是如何更新這個軟鏈的呢,例如我們經(jīng)常使用apt-get install更新程序時,動態(tài)鏈接庫更換了一個新的版本,那同時也需要更新一些軟鏈指向的位置,有一個程序叫l(wèi)dconfig,每次升級后執(zhí)行一下ldconfig,就會自動遍歷所有的默認(rèn)共享庫目錄,更新軟鏈。
如下代碼:
// libc.c
void func(int i) {
printf("func %d \n", i);
}
在gcc中通過“-Wl,-soname”參數(shù)告訴鏈接器,用于指定共享庫的SO-NAME。
gcc -fPIC -shared -Wl,-soname,libc.so.1 -o libc.so.1.2.3 lib.c
再看program.c
// program.c
int main() {
func(1);
return 0;
}
編譯鏈接運行:
$ gcc -o ttt program.c ./libc.so.1.2.3
$ ./ttt
./ttt: error while loading shared libraries: libc.so.1: cannot open shared object file: No such file or directory
上面可見,程序并沒有運行成功,因為沒有創(chuàng)建SO-NAME的相應(yīng)軟鏈
$ ln -s libc.so.1.2.3 libc.so.1
再次運行
$ ./ttt
./ttt: error while loading shared libraries: libc.so.1: cannot open shared object file: No such file or directory
再次運行發(fā)現(xiàn)還是沒有運行成功,為什么呢?因為程序運行時不知道去哪里找這個動態(tài)鏈接庫,所以需要指定一下查找?guī)斓穆窂剑?br>
LD_LIBRARY_PATH=. ./ttt
func 1
運行成功。
為什么運行"LD_LIBRARY_PATH=."后程序就可以運行成功了呢?這里介紹下共享庫的路徑查找相關(guān)知識點。
共享庫系統(tǒng)路徑
一般有三個:
/lib:主要存放系統(tǒng)最關(guān)鍵和基礎(chǔ)的共享庫,比如動態(tài)鏈接器、C語言運行庫、數(shù)學(xué)庫等,/bin和/sbin下的程序需要的共享庫和系統(tǒng)啟動需要的庫一般放在這里。
/usr/lib:主要存放一些非系統(tǒng)運行時所需要的關(guān)鍵性的共享庫,一般是用戶開發(fā)過程中用到的共享庫。
/usr/local/lib:一般存放一些非系統(tǒng)所需要的第三方庫,例如裝一個Python環(huán)境依賴的庫都可以放在這里。
總結(jié):/lib和/usr/lib存放一些常用成熟的系統(tǒng)本身需要的庫,/usr/local/lib存放一些非系統(tǒng)所需要的第三方庫。
簡單講:/lib是內(nèi)核級的,/usr/lib是系統(tǒng)級的:/usr/local/lib是用戶級的。
兩個在程序運行時與共享庫搜索路徑相關(guān)的環(huán)境變量:
LD_LIBRARY_PATH:通過此環(huán)境變量,可以臨時改變某個程序的共享庫查找路徑,而不會影響系統(tǒng)中的其它程序,LD_LIBRARY_PATH默認(rèn)為空,如果某個進(jìn)程設(shè)置了此環(huán)境變量,動態(tài)鏈接器在查找共享庫時,會首先查找LD_LIBRARY_PATH的指定目錄,通過這個環(huán)境變量可以測試新的共享庫,因為鏈接器鏈接時會鏈接最先找到的共享庫。
LD_PRELOAD:與LD_LIBRARY_PATH類似,它比LD_LIBRARY_PATH里面的目錄優(yōu)先級還高,LD_PRELOAD里面指定的共享庫和目標(biāo)文件中的全局符號會覆蓋后面出現(xiàn)的同名全局符號,使得我們可以很方便的改寫標(biāo)準(zhǔn)庫里的某個函數(shù)而不影響其它函數(shù),對于程序調(diào)試和測試非常有用。
為什么要extern C?
前面已經(jīng)介紹了共享庫的版本升級機(jī)制,在C語言中可能升級比較方便簡單,不會遇到太多問題,在C++中就比較繁瑣了,因為C++為了支持重載和namespace等,編譯出來的函數(shù)符號相對于函數(shù)名字來說有很多前后綴修飾,而且不同廠家的編譯器或者不同版本的編譯器可能符號修飾規(guī)則都不同,在更新時可能也會因為這種原因?qū)е虏患嫒荩院瘮?shù)導(dǎo)出時需要使用extern C修飾,讓導(dǎo)出函數(shù)的符號遵守C語言的規(guī)范。
參考資料
《程序員的自我修養(yǎng):鏈接裝載與庫》
https://blog.csdn.net/weixin_35695879/article/details/90721384
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!