日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

從開發(fā)者面對(duì)的動(dòng)態(tài)庫(kù),對(duì)linux發(fā)行版兼容性差的缺點(diǎn)和痛點(diǎn)出發(fā),本文梳理問題、探討并分享3種解決思路。

Linux系統(tǒng)如何知道哪些路徑下有動(dòng)態(tài)鏈接庫(kù)可供鏈接加載?可借助ldconfig緩存的信息。

ldconfig 是一個(gè)工具程序,用于更新動(dòng)態(tài)鏈接器的緩存。動(dòng)態(tài)鏈接器在加載動(dòng)態(tài)庫(kù)時(shí),會(huì)先查找緩存,如果緩存中已經(jīng)存在對(duì)應(yīng)的動(dòng)態(tài)庫(kù)的記錄,則直接使用緩存中的信息,否則再根據(jù)環(huán)境變量LD_LIBRARY_PATH從對(duì)應(yīng)的目錄內(nèi)找動(dòng)態(tài)庫(kù)文件。

那么ldconfig的緩存,究竟存儲(chǔ)在哪里?在內(nèi)存嗎?還是在文件系統(tǒng)?

ldconfg 對(duì)動(dòng)態(tài)庫(kù)路徑信息的緩存,存儲(chǔ)在哪里?

可以通過命令查詢當(dāng)前系統(tǒng)已緩存了哪些動(dòng)態(tài)庫(kù):

1.命令  ldconfig -p  查詢當(dāng)前系統(tǒng)已緩存的動(dòng)態(tài)庫(kù)

以下通過命令  ldconfig -p  查詢當(dāng)前系統(tǒng)已緩存的動(dòng)態(tài)庫(kù),包含庫(kù)文件名稱、版本信息、體系結(jié)構(gòu)、庫(kù)文件所在路徑。以下查詢結(jié)果僅展示常用的動(dòng)態(tài)庫(kù),比如 libstdc++,libMySQLclient等動(dòng)態(tài)庫(kù),

user@linuxlibs:~$ ldconfig -p
785 libs found in cache `/etc/ld.so.cache'
…… # std-c++ 動(dòng)態(tài)庫(kù)
        libstdc++.so.6 (libc6,x86-64) => /lib/x86_64-linux-gnu/libstdc++.so.6
        libssl3.so (libc6,x86-64) => /lib/x86_64-linux-gnu/libssl3.so
        libssl.so.3 (libc6,x86-64) => /lib/x86_64-linux-gnu/libssl.so.3
        libssl.so (libc6,x86-64) => /lib/x86_64-linux-gnu/libssl.so
        libssh.so.4 (libc6,x86-64) => /lib/x86_64-linux-gnu/libssh.so.4
…… # Python/ target=_blank class=infotextkey>Python核心動(dòng)態(tài)庫(kù)
        libpython3.10.so.1.0 (libc6,x86-64) => /lib/x86_64-linux-gnu/libpython3.10.so.1.0
…… # 線程相關(guān)的動(dòng)態(tài)庫(kù)
        libpthread.so.0 (libc6,x86-64, OS ABI: Linux 3.2.0) => /lib/x86_64-linux-gnu/libpthread.so.0
……
        libodbc.so.2 (libc6,x86-64) => /lib/x86_64-linux-gnu/libodbc.so.2
        libodbc.so (libc6,x86-64) => /lib/x86_64-linux-gnu/libodbc.so
…… # mysql 客戶端動(dòng)態(tài)庫(kù)
        libmysqlclient.so.21 (libc6,x86-64) => /lib/x86_64-linux-gnu/libmysqlclient.so.21
        libmysqlclient.so (libc6,x86-64) => /lib/x86_64-linux-gnu/libmysqlclient.so
……  # 維護(hù)鏈接信息的動(dòng)態(tài)庫(kù)
        ld-linux-x86-64.so.2 (libc6,x86-64) => /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
Cache generated by: ldconfig (Ubuntu GLIBC 2.35-0ubuntu3.4) stable release version 2.35

2.介紹一個(gè)大多數(shù)可執(zhí)行文件都會(huì)鏈接的動(dòng)態(tài)庫(kù)ld-linux-x86-64.so.2

上面最后的 /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 是 Linux 系統(tǒng)的一部分。這個(gè)庫(kù)主要用于加載和運(yùn)行其他動(dòng)態(tài)鏈接庫(kù)。

當(dāng)一個(gè)程序需要使用其他動(dòng)態(tài)鏈接庫(kù)中的函數(shù)時(shí),它會(huì)通過調(diào)用 ld-linux-x86-64.so.2 來(lái)加載所需的動(dòng)態(tài)鏈接庫(kù),并解析其中的符號(hào)(函數(shù)、變量等)。這樣,程序就可以在運(yùn)行時(shí)動(dòng)態(tài)地使用其他庫(kù)中的功能,而不需要在編譯時(shí)將這些庫(kù)靜態(tài)鏈接到程序中。

3.ldconfig的緩存文件,在這里

我們注意到輸出信息第一行為785 libs found in cache /etc/ld.so.cache (在緩存文件中找到785個(gè)庫(kù)文件記錄),說(shuō)明 /etc/ld.so.cache 是 ldconfig 搜索動(dòng)態(tài)庫(kù)時(shí)依據(jù)的緩存文件,該緩存文件記錄了785個(gè)動(dòng)態(tài)庫(kù)文件的信息,每條信息記錄了 key=>value 的這樣的鍵值對(duì)形式,例如libmysqlclient.so.21 (libc6,x86-64) => /lib/x86_64-linux-gnu/libmysqlclient.so.21。用 ls -lht 命令查看該緩存文件的屬性:

user@linuxlibs:~$ ls -lht  /etc/ld.so.cache
  -rw-r--r-- 1 root root 48K Dec 11 10:45 /etc/ld.so.cache

user@linuxlibs:~$ file /etc/ld.so.cache
  /etc/ld.so.cache: data    #類型是二進(jìn)制數(shù)據(jù)文件,有內(nèi)部格式無(wú)法直接查看內(nèi)容

從中,我們第一可以明確的是,緩存文件存儲(chǔ)在磁盤。

第二可以推斷,磁盤的緩存文件,可能有通過mmap()方式映射內(nèi)到存中,以滿足系統(tǒng)各類軟件頻繁獲取動(dòng)態(tài)庫(kù)信息的效率要求。這個(gè)猜測(cè)后續(xù)會(huì)進(jìn)一步驗(yàn)證。

這些動(dòng)態(tài)庫(kù)的版本如何被linux系統(tǒng)安裝管理的呢?

首先,Ubuntu linux的動(dòng)態(tài)庫(kù)文件,需要通過apt安裝后才會(huì)出現(xiàn)在系統(tǒng)庫(kù)目錄內(nèi)。例如C++ 程序在運(yùn)行時(shí)需要鏈接的動(dòng)態(tài)庫(kù)libstdc++.so.6.0.30,可通過apt install 安裝包 libstdc++6 獲得。

#可通過 dpkg -L 查詢軟件包 libstdc++6 安裝后新增了哪些文件:
user@linuxlibs:~$ dpkg -L libstdc++6
 /.
 /usr
 /usr/lib
 /usr/lib/x86_64-linux-gnu
 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.30
 /usr/lib/x86_64-linux-gnu/libstdc++.so.6
 /usr/share/doc/libstdc++6
 …… 略

安裝后,允許存在多個(gè)不同版本的libstdc++動(dòng)態(tài)庫(kù)包,但系統(tǒng)啟用的只是其中一個(gè):

user@linuxlibs:~$ ls -l /usr/lib/x86_64-linux-gnu/libstdc++.so.6
  …… /usr/lib/x86_64-linux-gnu/libstdc++.so.6 -> libstdc++.so.6.0.30

我們看到這里的libstc++動(dòng)態(tài)庫(kù)版本號(hào)是6.0.30。但千萬(wàn)不要誤以為這是Linux采用的C++標(biāo)準(zhǔn)庫(kù)的代碼版本號(hào)。動(dòng)態(tài)庫(kù)版本號(hào) 6.0.30 表示該動(dòng)態(tài)庫(kù)的版本信息。版本號(hào)通常由三個(gè)部分組成,分別表示主版本號(hào)、次版本號(hào)和補(bǔ)丁版本號(hào)。

動(dòng)態(tài)庫(kù)的版本號(hào)通常用于標(biāo)識(shí)軟件或庫(kù)的不同版本,以便用戶和系統(tǒng)能夠識(shí)別和管理不同版本的軟件或庫(kù)。在使用動(dòng)態(tài)庫(kù)時(shí),程序會(huì)根據(jù)需要去加載相應(yīng)版本的動(dòng)態(tài)庫(kù)。

動(dòng)態(tài)庫(kù)的版本號(hào)的確定,通常是由庫(kù)的開發(fā)者或維護(hù)者根據(jù)庫(kù)的更新和發(fā)布情況分配的。因此,即使庫(kù)的代碼進(jìn)行了重大更新,版本號(hào)也可能只進(jìn)行了微小的變化。所以庫(kù)的版本號(hào)并不一定與庫(kù)的代碼版本直接相關(guān);即使同一個(gè)vim軟件,在不同的Linux發(fā)行版有不同的打包維護(hù)人,雖然都從軟件官方獲得同一源碼版本號(hào)的vim的代碼,但不同Linux發(fā)行版的軟件打包維護(hù)人根據(jù)自己發(fā)行版的情況決定編譯后打包的庫(kù)文件的版本。

當(dāng)然,也有些軟件采用兩個(gè)版本號(hào)相等的方式發(fā)布軟件版本和代碼,比如Ubuntu的維護(hù)者將OpenGL項(xiàng)目的動(dòng)態(tài)庫(kù)版本與代碼版本保持一致,動(dòng)態(tài)庫(kù)版本后面以-數(shù)字表示該版本代碼的第幾次正式打包,這個(gè)數(shù)字每次正式打包前都要+1,如圖中的2.2.0-4為Ubuntu 22.04系統(tǒng)的libglew2.2軟件包的2.2.0版本的第4次打包入庫(kù)。

雖然這個(gè)版本經(jīng)歷了4次打包發(fā)布,但libglew-2.2.0的4次生成的軟件包,在安裝后,路徑中的動(dòng)態(tài)庫(kù)的文件名仍保持.so.2.2.0結(jié)尾。

這是因?yàn)?次打包期間,庫(kù)代碼接口沒變,自然不應(yīng)該修改X.Y.Z中的任何一個(gè)數(shù)字。以免破壞 /usr/lib/x86_64-linux-gnu/libGLEW.so -> libGLEW.so.2.2.0 這種libGLEW.so軟鏈接對(duì)實(shí)際動(dòng)態(tài)庫(kù)文件libGLEW.so.2.2.0的鏈接效果:

開發(fā)者可以從哪里查詢,動(dòng)態(tài)庫(kù)文件的版本號(hào)與代碼版本號(hào)的對(duì)應(yīng)關(guān)系?

如果開發(fā)依賴了 OpenGL(v1,v2,v3都有,libGLEW、libGLut、libGL、libegl-mesa0等名稱繁多) 這種帶有較多版本歷史包袱的開發(fā)庫(kù),有時(shí)必須確定系統(tǒng)已安裝的OpenGL庫(kù)的版本號(hào)跟開發(fā)要求的庫(kù)的代碼版本號(hào)的是否匹配,才能確保代碼調(diào)用的函數(shù)跟實(shí)際運(yùn)行環(huán)境的庫(kù)的版本能兼容。

那么如何查詢呢?這個(gè)問題沒有為唯一答案,軟件的發(fā)布方式和維護(hù)形式太多了。但那些主流的Linux發(fā)行版的軟件源安裝的軟件包往往采用了近似的策略,方便了用戶查詢幫助信息。如果你的Linux是Ubuntu、centos,那么安裝后可以直接從命令中獲得大部分信息,包括動(dòng)態(tài)庫(kù)版本說(shuō)明:

(1) Ubuntu使用命令$ apt show libglew2.2 查詢軟件幫助信息

(2) Centos 使用命令$ yum info glew-devel 查詢軟件幫助信息

[root@device78969 ~]# yum info glew-devel
Loaded plugins: fastestmirror, ovl
Loading mirror speeds from cached hostfile
 * base: mirrors.ustc.edu.cn
 * extras: mirrors.aliyun.com
 * updates: mirrors.163.com
AvAIlable Packages
Name        : glew-devel
Arch        : i686
Version     : 1.10.0
Release     : 5.el7
Size        : 172 k
Repo        : base/7/x86_64
Summary     : Development files for glew
URL         : http://glew.sourceforge.NET
License     : BSD and MIT
Description : Development files for glew

Name        : glew-devel
Arch        : x86_64
Version     : 1.10.0
Release     : 5.el7
Size        : 172 k
Repo        : base/7/x86_64
Summary     : Development files for glew
URL         : http://glew.sourceforge.net
License     : BSD and MIT
Description : Development files for glew

最后,分享3個(gè)思路,解決運(yùn)行時(shí)動(dòng)態(tài)庫(kù)不兼容問題

這是很多開發(fā)者發(fā)布軟件包時(shí)最頭痛的,系統(tǒng)自帶的動(dòng)態(tài)庫(kù)版本,與軟件運(yùn)行所要求的動(dòng)態(tài)庫(kù)不兼容,直接影響了軟件在Linux當(dāng)前系統(tǒng)的正常功能。

評(píng)論區(qū)有網(wǎng)友指出,動(dòng)態(tài)庫(kù)最大的弊端是跨Linux發(fā)行版部署的時(shí)候,常因?yàn)橐蕾嚨膭?dòng)態(tài)庫(kù)版本在不同Linux上實(shí)際安裝的是不同版本,兩個(gè)版本的動(dòng)態(tài)庫(kù)未保持向低版本兼容,導(dǎo)致主程序找不到合適的依賴庫(kù)版本而無(wú)法運(yùn)行。

下面介紹針對(duì)這個(gè)問題的3種解法(實(shí)際本質(zhì)上是2種:被動(dòng)、主動(dòng))

思路1:由shell腳本幫助載入合適的動(dòng)態(tài)庫(kù):

通過局部環(huán)境變量設(shè)置 LD_LIBRARY_PATH 和 PRE_LOAD,讓軟件優(yōu)先使用受支持版本的動(dòng)態(tài)庫(kù)。

LD_PRELOAD 環(huán)境變量用于指定在加載動(dòng)態(tài)鏈接庫(kù)時(shí)優(yōu)先加載的庫(kù)文件。通過設(shè)置 LD_PRELOAD 環(huán)境變量,你可以在程序加載動(dòng)態(tài)鏈接庫(kù)之前加載你自己的庫(kù)文件,從而實(shí)現(xiàn)對(duì)程序行為的修改或調(diào)試。

想在局部生效 LD_PRELOAD 環(huán)境變量,可以使用以下內(nèi)容寫到一個(gè)statup.sh腳本內(nèi):

#!/usr/bin/bash
export LD_PRELOAD=/path/to/your/library
./my_dir/my_programe

或:

#!/usr/bin/bash
LD_PRELOAD=/path/to/your/library  ./my_dir/my_programe

其中,/path/to/your/library 是你要加載的庫(kù)文件的路徑,最好是當(dāng)前可執(zhí)行文件所在目錄下的動(dòng)態(tài)庫(kù)文件,以方便管理;./my_dir/my_programe為你要運(yùn)行的可執(zhí)行文件。

LD_PRELOAD 環(huán)境變量的使用需要謹(jǐn)慎,因?yàn)樗赡軙?huì)影響程序的正常運(yùn)行,所以只建議在shell腳本內(nèi)部使用,通過腳本運(yùn)行后只有你的my_program受這個(gè)環(huán)境變量加載的動(dòng)態(tài)庫(kù)的影響:而且會(huì)優(yōu)先加載你指定的動(dòng)態(tài)庫(kù)而不加載其他同名的動(dòng)態(tài)庫(kù),就避免了與系統(tǒng)自帶動(dòng)態(tài)庫(kù)的沖突。

這種設(shè)置環(huán)境腳本的思路,在Tomcat和Pycharm的安裝方式和啟動(dòng)方式中被采用。

 

!!! 提醒:

 

在使用 LD_PRELOAD 環(huán)境變量進(jìn)行調(diào)試或修改程序行為時(shí),建議在測(cè)試環(huán)境中進(jìn)行,并確保對(duì)可能的影響有充分的了解,以免影響系統(tǒng)正常運(yùn)行。

思路2:代碼編程實(shí)現(xiàn)本地主動(dòng)加載動(dòng)態(tài)庫(kù)文件,區(qū)別于思路1的被動(dòng)加載方式:

下面是一段偽代碼,演示了由C代碼控制,在運(yùn)行時(shí)才加載動(dòng)態(tài)庫(kù)文件到進(jìn)程中。一個(gè)好處是延遲了加載,而且由代碼負(fù)責(zé)檢測(cè)該動(dòng)態(tài)庫(kù)是否提供了所需功能,若未提供,則卸載動(dòng)態(tài)庫(kù),再去加載其他動(dòng)態(tài)庫(kù):

#include <dll_function_headers.h>

// 定義加載動(dòng)態(tài)庫(kù)的函數(shù)
void* load_library(const char* library_path, const char* symbol_name) {
    // 打開動(dòng)態(tài)庫(kù)
    void* handle = dlopen(library_path, RTLD_LAZY);
    if (handle == NULL) {
        printf("dlopen() failed: %sn", dlerror());
        return NULL;
    }

    // 查找符號(hào)
    void* symbol_address = dlsym(handle, symbol_name);
    if (symbol_address == NULL) {
        printf("dlsym() failed: %sn", dlerror());
        dlclose(handle);
        return NULL;
    }

    // 返回符號(hào)地址
    return symbol_address;
}

// 定義使用動(dòng)態(tài)庫(kù)符號(hào)的函數(shù)
int use_symbol(void* symbol_address) {
    // 定義符號(hào)的函數(shù)指針類型
    typedef int (*symbol_func_t)(void);
    symbol_func_t symbol_func = (symbol_func_t)symbol_address;

    // 調(diào)用符號(hào)對(duì)應(yīng)的函數(shù)
    int result = symbol_func();
    return result;
}

int main() {
    // 假設(shè)動(dòng)態(tài)庫(kù)路徑為 /path/to/library.so,符號(hào)名稱為 symbol
    const char* library_path = "/path/to/library.so";
    const char* symbol_name = "symbol";

    // 加載動(dòng)態(tài)庫(kù)
    void* symbol_address = load_library(library_path, symbol_name);
    if (symbol_address == NULL) {
        printf("加載動(dòng)態(tài)庫(kù)失敗n");
        return 1;
    }

    // 使用符號(hào)
    int result = use_symbol(symbol_address);
    if (result != 0) {
        printf("符號(hào)調(diào)用失敗n");
        return 1;
    }

    // 關(guān)閉動(dòng)態(tài)庫(kù)
    dlclose(handle);

    return 0;
}

但這種方式?jīng)]有解決所有本地動(dòng)態(tài)庫(kù)都無(wú)法支持當(dāng)前Linux系統(tǒng)的特殊場(chǎng)景。

思路3:代碼編程實(shí)現(xiàn)從網(wǎng)絡(luò)主動(dòng)加載動(dòng)態(tài)庫(kù)文件,區(qū)別于思路2的方式:

這種方式仍然由本地軟件負(fù)責(zé)加載。但加載的來(lái)源改為從服務(wù)端API交互,將本地系統(tǒng)的版本信息告訴服務(wù)端,由服務(wù)端的數(shù)據(jù)中心提供能匹配本地Linux的動(dòng)態(tài)庫(kù)文件,由本地軟件將服務(wù)端告知的動(dòng)態(tài)庫(kù)文件,下載到本地。然后加載運(yùn)行。

由于配備了后臺(tái)服務(wù),且對(duì)各種需要兼容的Linux系統(tǒng)做了測(cè)試,準(zhǔn)備了匹配的動(dòng)態(tài)庫(kù)下載使用,所以軟件安裝包可以很小。這種方式其實(shí)也是很多軟件采用的軟件自身新版本的更新機(jī)制。

缺點(diǎn)是需要網(wǎng)絡(luò),開發(fā)階段需要做多種Linux系統(tǒng)的動(dòng)態(tài)庫(kù)兼容性測(cè)試,在服務(wù)端需要維護(hù)匹配的動(dòng)態(tài)庫(kù)文件與信息。

適合于達(dá)到一定使用規(guī)模的軟件采用。

分享到:
標(biāo)簽:動(dòng)態(tài)
用戶無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定