Linux共享庫(kù)如何進(jìn)行版本控制

大家平時(shí)使用Linux系統(tǒng)過(guò)程中可能都見(jiàn)過(guò)文件系統(tǒng)里有好多帶版本號(hào)的共享庫(kù),如下:

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

大家平時(shí)關(guān)注過(guò)這些共享庫(kù)的版本號(hào)是以什么規(guī)則制定的呢?

以"libname.so.x.y.z"來(lái)說(shuō):

  • 最前面使用"lib"前綴,中間是庫(kù)的名字和后綴".so",后面跟著三個(gè)數(shù)字組成版本號(hào)
  • x為主版本號(hào),表示庫(kù)的重大升級(jí),不同主版本號(hào)的庫(kù)之間是不兼容的,依賴于舊版本號(hào)的程序要修改相應(yīng)的代碼來(lái)適應(yīng)新版本的庫(kù),并重新編譯才可以鏈接新版本庫(kù)運(yùn)行。
  • y為次版本號(hào),表示庫(kù)的增量升級(jí),相對(duì)于舊版本只是增加了一些新的接口,并保持原有的接口符號(hào)不變,完全兼容舊版本的庫(kù),即一個(gè)依賴于舊版本號(hào)的程序可以直接鏈接新版本庫(kù)來(lái)正常運(yùn)行。
  • z為發(fā)布版本號(hào),表示庫(kù)的一些錯(cuò)誤修正、性能優(yōu)化等,不會(huì)增加新的接口,只是在舊版本庫(kù)的基礎(chǔ)上做一些優(yōu)化。

如何創(chuàng)建共享庫(kù)

首先介紹一些SO-NAME,每一個(gè)共享庫(kù)都有一個(gè)SO-NAME,即共享庫(kù)的文件名去掉次版本號(hào)和發(fā)布版本號(hào),比如"libname.so.x.y.z"的SO_NAME就是"libname.so.x"。在Linux系統(tǒng)中,系統(tǒng)會(huì)為每個(gè)共享庫(kù)在所在的目錄中創(chuàng)建一個(gè)與SO-NAME同名并且指向?qū)嶋H共享庫(kù)的軟鏈接。例如"libc.so.1"會(huì)指向"libc.so.1.2.3",當(dāng)目錄中有"libc.so.1.2.4"時(shí),"libc.so.1"這個(gè)軟鏈就會(huì)指向"libc.so.1.2.4",達(dá)到升級(jí)的目的。那系統(tǒng)是如何更新這個(gè)軟鏈的呢,例如我們經(jīng)常使用apt-get install更新程序時(shí),動(dòng)態(tài)鏈接庫(kù)更換了一個(gè)新的版本,那同時(shí)也需要更新一些軟鏈指向的位置,有一個(gè)程序叫ldconfig,每次升級(jí)后執(zhí)行一下ldconfig,就會(huì)自動(dòng)遍歷所有的默認(rèn)共享庫(kù)目錄,更新軟鏈。

如下代碼:

// libc.c
#include <stdio.h>

void func(int i) {
    printf("func %d \n", i);
}

在gcc中通過(guò)“-Wl,-soname”參數(shù)告訴鏈接器,用于制定共享庫(kù)的SO-NAME。

gcc -fPIC -shared -Wl,-soname,libc.so.1 -o libc.so.1.2.3 lib.c

再看program.c

// program.c
#include <stdio.h>

int main() {
    func(1);
    return 0;
}

編譯鏈接運(yùn)行:

$ 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    

上面可見(jiàn),程序并沒(méi)有運(yùn)行成功,因?yàn)闆](méi)有創(chuàng)建SO-NAME的相應(yīng)軟鏈

$ ln -s libc.so.1.2.3 libc.so.1
再次運(yùn)行
$ ./ttt
./ttt: error while loading shared libraries: libc.so.1: cannot open shared object file: No such file or directory    

再次運(yùn)行發(fā)現(xiàn)還是沒(méi)有運(yùn)行成功,為什么呢?因?yàn)槌绦蜻\(yùn)行時(shí)不知道去哪里找這個(gè)動(dòng)態(tài)鏈接庫(kù),所有需要指定一下查找?guī)斓穆窂剑?/p>

$ LD_LIBRARY_PATH=. ./ttt
func 1

運(yùn)行成功。

為什么運(yùn)行"LD_LIBRARY_PATH=."后程序就可以運(yùn)行成功了呢?這里介紹下共享庫(kù)的路徑查找相關(guān)知識(shí)點(diǎn)。

共享庫(kù)系統(tǒng)路徑

一般有三個(gè):

  • /lib:主要存放系統(tǒng)最關(guān)鍵和基礎(chǔ)的共享庫(kù),比如動(dòng)態(tài)鏈接器、C語(yǔ)言運(yùn)行庫(kù)、數(shù)學(xué)庫(kù)等,/bin和/sbin下的程序需要的共享庫(kù)和系統(tǒng)啟動(dòng)需要的庫(kù)一般放在這里。
  • /usr/lib:主要存放一些非系統(tǒng)運(yùn)行時(shí)所需要的關(guān)鍵性的共享庫(kù),一般是用戶開(kāi)發(fā)過(guò)程中用到的共享庫(kù)。
  • /usr/local/lib:一般存放一些非系統(tǒng)所需要的第三方庫(kù),例如裝一個(gè)Python環(huán)境依賴的庫(kù)都可以放在這里。

總結(jié):/lib和/usr/lib存放一些常用成熟的系統(tǒng)本身需要的庫(kù),/usr/local/lib存放一些非系統(tǒng)所需要的第三方庫(kù)。

簡(jiǎn)單講:/lib是內(nèi)核級(jí)的,/usr/lib是系統(tǒng)級(jí)的:/usr/local/lib是用戶級(jí)的。

兩個(gè)在程序運(yùn)行時(shí)與共享庫(kù)搜索路徑相關(guān)的環(huán)境變量:

  • LD_LIBRARY_PATH:通過(guò)此環(huán)境變量,可以臨時(shí)改變某個(gè)程序的共享庫(kù)查找路徑,而不會(huì)影響系統(tǒng)中的其它程序,LD_LIBRARY_PATH默認(rèn)為空,如果某個(gè)進(jìn)程設(shè)置了此環(huán)境變量,動(dòng)態(tài)鏈接器在查找共享庫(kù)時(shí),會(huì)首先查找LD_LIBRARY_PATH的指定目錄,通過(guò)這個(gè)環(huán)境變量可以測(cè)試新的共享庫(kù),因?yàn)殒溄悠麈溄訒r(shí)會(huì)鏈接最先找到的共享庫(kù)。
  • LD_PRELOAD:與LD_LIBRARY_PATH類似,它比LD_LIBRARY_PATH里面的目錄優(yōu)先級(jí)還高,LD_PRELOAD里面指定的共享庫(kù)和目標(biāo)文件中的全局符號(hào)會(huì)覆蓋后面出現(xiàn)的同名全局符號(hào),使得我們可以很方便的改寫標(biāo)準(zhǔn)庫(kù)里的某個(gè)函數(shù)而不影響其它函數(shù),對(duì)于程序調(diào)試和測(cè)試非常有用。

為什么要extern C?

前面已經(jīng)介紹了共享庫(kù)的版本升級(jí)機(jī)制,在C語(yǔ)言中可能升級(jí)比較方便簡(jiǎn)單,不會(huì)遇到太多問(wèn)題,在C++中就比較繁瑣了,因?yàn)镃++為了支持重載和namespace等,編譯出來(lái)的函數(shù)符號(hào)相對(duì)于函數(shù)名字來(lái)說(shuō)有很多前后綴修飾,而且不同廠家的編譯器或者不同版本的編譯器可能符號(hào)修飾規(guī)則都不同,在更新時(shí)可能也會(huì)因?yàn)檫@種原因?qū)е虏患嫒?,所以函?shù)導(dǎo)出時(shí)需要使用extern C修飾,讓導(dǎo)出函數(shù)的符號(hào)遵守C語(yǔ)言的規(guī)范。

參考資料

《程序員的自我修養(yǎng):鏈接裝載與庫(kù)》

https://blog.csdn.net/weixin_35695879/article/details/90721384

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容