一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

Linux|Centos|Ubuntu|系統進程|Fedora|注冊表|Bios|Solaris|Windows7|Windows10|Windows11|windows server|

服務器之家 - 服務器系統 - Linux - 讓軟件支持多個Linux發行版,動態庫不兼容?三種解法!

讓軟件支持多個Linux發行版,動態庫不兼容?三種解法!

2024-01-09 17:02未知服務器之家 Linux

圖片 從開發者面對的動態庫,對Linux發行版兼容性差的缺點和痛點出發,本文梳理問題、探討并分享3種解決思路。 Linux系統如何知道哪些路徑下有動態鏈接庫可供鏈接加載?可借助ldconfig緩存的信息。 ldconfig 是一個工具程序,用于

讓軟件支持多個Linux發行版,動態庫不兼容?三種解法!圖片

從開發者面對的動態庫,對Linux發行版兼容性差的缺點和痛點出發,本文梳理問題、探討并分享3種解決思路。

Linux系統如何知道哪些路徑下有動態鏈接庫可供鏈接加載?可借助ldconfig緩存的信息。

ldconfig 是一個工具程序,用于更新動態鏈接器的緩存。動態鏈接器在加載動態庫時,會先查找緩存,如果緩存中已經存在對應的動態庫的記錄,則直接使用緩存中的信息,否則再根據環境變量LD_LIBRARY_PATH從對應的目錄內找動態庫文件。

那么ldconfig的緩存,究竟存儲在哪里?在內存嗎?還是在文件系統?

ldconfg 對動態庫路徑信息的緩存,存儲在哪里?

可以通過命令查詢當前系統已緩存了哪些動態庫:

1.命令 ldconfig -p查詢當前系統已緩存的動態庫

以下通過命令 ldconfig -p查詢當前系統已緩存的動態庫,包含庫文件名稱、版本信息、體系結構、庫文件所在路徑。以下查詢結果僅展示常用的動態庫,比如 libstdc++,libmysqlclient等動態庫,

user@linuxlibs:~$ ldconfig -p
785 libs found in cache `/etc/ld.so.cache'
…… # std-c++ 動態庫
        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核心動態庫
        libpython3.10.so.1.0 (libc6,x86-64) => /lib/x86_64-linux-gnu/libpython3.10.so.1.0
…… # 線程相關的動態庫
        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 客戶端動態庫
        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
……  # 維護鏈接信息的動態庫
        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.介紹一個大多數可執行文件都會鏈接的動態庫ld-linux-x86-64.so.2

上面最后的/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2是 Linux 系統的一部分。這個庫主要用于加載和運行其他動態鏈接庫。

當一個程序需要使用其他動態鏈接庫中的函數時,它會通過調用 ld-linux-x86-64.so.2 來加載所需的動態鏈接庫,并解析其中的符號(函數、變量等)。這樣,程序就可以在運行時動態地使用其他庫中的功能,而不需要在編譯時將這些庫靜態鏈接到程序中。

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

我們注意到輸出信息第一行為785 libs found in cache /etc/ld.so.cache(在緩存文件中找到785個庫文件記錄),說明/etc/ld.so.cache是 ldconfig 搜索動態庫時依據的緩存文件,該緩存文件記錄了785個動態庫文件的信息,每條信息記錄了key=>value的這樣的鍵值對形式,例如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    #類型是二進制數據文件,有內部格式無法直接查看內容

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

第二可以推斷,磁盤的緩存文件,可能有通過mmap()方式映射內到存中,以滿足系統各類軟件頻繁獲取動態庫信息的效率要求。這個猜測后續會進一步驗證。

這些動態庫的版本如何被linux系統安裝管理的呢?

首先,Ubuntu linux的動態庫文件,需要通過apt安裝后才會出現在系統庫目錄內。例如C++ 程序在運行時需要鏈接的動態庫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
 …… 略

安裝后,允許存在多個不同版本的libstdc++動態庫包,但系統啟用的只是其中一個:

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++動態庫版本號是6.0.30。但千萬不要誤以為這是Linux采用的C++標準庫的代碼版本號。動態庫版本號6.0.30表示該動態庫的版本信息。版本號通常由三個部分組成,分別表示主版本號、次版本號和補丁版本號。

動態庫的版本號通常用于標識軟件或庫的不同版本,以便用戶和系統能夠識別和管理不同版本的軟件或庫。在使用動態庫時,程序會根據需要去加載相應版本的動態庫。

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

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

讓軟件支持多個Linux發行版,動態庫不兼容?三種解法!

雖然這個版本經歷了4次打包發布,但libglew-2.2.0的4次生成的軟件包,在安裝后,路徑中的動態庫的文件名仍保持.so.2.2.0結尾。

讓軟件支持多個Linux發行版,動態庫不兼容?三種解法!

這是因為4次打包期間,庫代碼接口沒變,自然不應該修改X.Y.Z中的任何一個數字。以免破壞/usr/lib/x86_64-linux-gnu/libGLEW.so -> libGLEW.so.2.2.0這種libGLEW.so軟鏈接對實際動態庫文件libGLEW.so.2.2.0的鏈接效果:

讓軟件支持多個Linux發行版,動態庫不兼容?三種解法!

開發者可以從哪里查詢,動態庫文件的版本號與代碼版本號的對應關系?

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

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

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

讓軟件支持多個Linux發行版,動態庫不兼容?三種解法!

(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://www.ythuaji.com.cn/uploads/allimg/s2vxa3t1m5e
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://www.ythuaji.com.cn/uploads/allimg/s2vxa3t1m5e
License     : BSD and MIT
Description : Development files for glew

最后,分享3個思路,解決運行時動態庫不兼容問題

這是很多開發者發布軟件包時最頭痛的,系統自帶的動態庫版本,與軟件運行所要求的動態庫不兼容,直接影響了軟件在Linux當前系統的正常功能。

讓軟件支持多個Linux發行版,動態庫不兼容?三種解法!

評論區有網友指出,動態庫最大的弊端是跨Linux發行版部署的時候,常因為依賴的動態庫版本在不同Linux上實際安裝的是不同版本,兩個版本的動態庫未保持向低版本兼容,導致主程序找不到合適的依賴庫版本而無法運行。

下面介紹針對這個問題的3種解法(實際本質上是2種:被動、主動)

思路1:由shell腳本幫助載入合適的動態庫:

通過局部環境變量設置LD_LIBRARY_PATH和PRE_LOAD,讓軟件優先使用受支持版本的動態庫。

LD_PRELOAD環境變量用于指定在加載動態鏈接庫時優先加載的庫文件。通過設置LD_PRELOAD環境變量,你可以在程序加載動態鏈接庫之前加載你自己的庫文件,從而實現對程序行為的修改或調試。

想在局部生效LD_PRELOAD環境變量,可以使用以下內容寫到一個statup.sh腳本內:

#!/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是你要加載的庫文件的路徑,最好是當前可執行文件所在目錄下的動態庫文件,以方便管理;./my_dir/my_programe為你要運行的可執行文件。

LD_PRELOAD環境變量的使用需要謹慎,因為它可能會影響程序的正常運行,所以只建議在shell腳本內部使用,通過腳本運行后只有你的my_program受這個環境變量加載的動態庫的影響:而且會優先加載你指定的動態庫而不加載其他同名的動態庫,就避免了與系統自帶動態庫的沖突。

這種設置環境腳本的思路,在Tomcat和Pycharm的安裝方式和啟動方式中被采用。

!!! 提醒:

在使用 LD_PRELOAD 環境變量進行調試或修改程序行為時,建議在測試環境中進行,并確保對可能的影響有充分的了解,以免影響系統正常運行。

思路2:代碼編程實現本地主動加載動態庫文件,區別于思路1的被動加載方式:

下面是一段偽代碼,演示了由C代碼控制,在運行時才加載動態庫文件到進程中。一個好處是延遲了加載,而且由代碼負責檢測該動態庫是否提供了所需功能,若未提供,則卸載動態庫,再去加載其他動態庫:

#include <dll_function_headers.h>

// 定義加載動態庫的函數
void* load_library(const char* library_path, const char* symbol_name) {
    // 打開動態庫
    void* handle = dlopen(library_path, RTLD_LAZY);
    if (handle == NULL) {
        printf("dlopen() failed: %s\n", dlerror());
        return NULL;
    }

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

    // 返回符號地址
    return symbol_address;
}

// 定義使用動態庫符號的函數
int use_symbol(void* symbol_address) {
    // 定義符號的函數指針類型
    typedef int (*symbol_func_t)(void);
    symbol_func_t symbol_func = (symbol_func_t)symbol_address;

    // 調用符號對應的函數
    int result = symbol_func();
    return result;
}

int main() {
    // 假設動態庫路徑為 /path/to/library.so,符號名稱為 symbol
    const char* library_path = "/path/to/library.so";
    const char* symbol_name = "symbol";

    // 加載動態庫
    void* symbol_address = load_library(library_path, symbol_name);
    if (symbol_address == NULL) {
        printf("加載動態庫失敗\n");
        return 1;
    }

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

    // 關閉動態庫
    dlclose(handle);

    return 0;
}

但這種方式沒有解決所有本地動態庫都無法支持當前Linux系統的特殊場景。

思路3:代碼編程實現從網絡主動加載動態庫文件,區別于思路2的方式:

這種方式仍然由本地軟件負責加載。但加載的來源改為從服務端API交互,將本地系統的版本信息告訴服務端,由服務端的數據中心提供能匹配本地Linux的動態庫文件,由本地軟件將服務端告知的動態庫文件,下載到本地。然后加載運行。

由于配備了后臺服務,且對各種需要兼容的Linux系統做了測試,準備了匹配的動態庫下載使用,所以軟件安裝包可以很小。這種方式其實也是很多軟件采用的軟件自身新版本的更新機制。

缺點是需要網絡,開發階段需要做多種Linux系統的動態庫兼容性測試,在服務端需要維護匹配的動態庫文件與信息。

適合于達到一定使用規模的軟件采用。

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 精品国产一区二区 | 4455永久在线观免费看片 | 91欧美秘密入口 | 男人天堂日韩 | 久久精品一卡二卡三卡四卡视频版 | 国产一区二区精品久久 | 国产有码在线 | 久久天天躁狠狠躁夜夜躁 | 99精品国产美女福到在线不卡 | 动漫美女羞羞视频 | 白丝校花好湿好紧 | 亚洲 另类 欧美 变态屎尿 | 经典三级四虎在线观看 | 国产精品 色 | 极品美女aⅴ高清在线观看 极品ts赵恩静和直男激战啪啪 | 2023最新伦理片 | 亚洲国产情侣一区二区三区 | 大乳孕妇一级毛片 | 高清一区二区 | 91精品国产91久久久久久 | 草莓视频幸福宝 | 久久精品人人做人人爽97 | 男人猛进猛出女人下面视频 | 日本在线亚州精品视频在线 | 操mm | 含羞草传媒一天免费看下 | 狠狠躁夜夜躁人人爽天天miya | chinesespanking调教| 国产成人精品一区二区阿娇陈冠希 | 国内精品91东航翘臀女神在线 | 日本大学jalapsikix | 91国产在线视频 | 亚洲国产成人久久综合一区 | 四虎新网站 | 国产精品亚洲综合久久 | 青青视频国产依人在线 | 236zz宅宅最新伦理 | 日韩欧美在线看 | 动漫美女人物被黄漫小说 | 欧美国产在线视频 | 成人快插 |