您当前的位置:首页 > 电脑百科 > 软件技术 > 软件技术

让软件支持多个Linux发行版,动态库不兼容?3种解法!

时间:2024-01-09 14:22:20  来源:微信公众号  作者:深入理解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/ target=_blank class=infotextkey>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次打包入库。

虽然这个版本经历了4次打包发布,但libglew-2.2.0的4次生成的软件包,在安装后,路径中的动态库的文件名仍保持.so.2.2.0结尾。

这是因为4次打包期间,库代码接口没变,自然不应该修改X.Y.Z中的任何一个数字。以免破坏 /usr/lib/x86_64-linux-gnu/libGLEW.so -> libGLEW.so.2.2.0 这种libGLEW.so软链接对实际动态库文件libGLEW.so.2.2.0的链接效果:

开发者可以从哪里查询,动态库文件的版本号与代码版本号的对应关系?

如果开发依赖了 OpenGL(v1,v2,v3都有,libGLEW、libGLut、libGL、libegl-mesa0等名称繁多) 这种带有较多版本历史包袱的开发库,有时必须确定系统已安装的OpenGL库的版本号跟开发要求的库的代码版本号的是否匹配,才能确保代码调用的函数跟实际运行环境的库的版本能兼容。

那么如何查询呢?这个问题没有为唯一答案,软件的发布方式和维护形式太多了。但那些主流的Linux发行版的软件源安装的软件包往往采用了近似的策略,方便了用户查询帮助信息。如果你的Linux是Ubuntu、centos,那么安装后可以直接从命令中获得大部分信息,包括动态库版本说明:

(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个思路,解决运行时动态库不兼容问题

这是很多开发者发布软件包时最头痛的,系统自带的动态库版本,与软件运行所要求的动态库不兼容,直接影响了软件在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: %sn", dlerror());
        return NULL;
    }

    // 查找符号
    void* symbol_address = dlsym(handle, symbol_name);
    if (symbol_address == NULL) {
        printf("dlsym() failed: %sn", 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系统的动态库兼容性测试,在服务端需要维护匹配的动态库文件与信息。

适合于达到一定使用规模的软件采用。



Tags:动态库   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
让软件支持多个Linux发行版,动态库不兼容?3种解法!
从开发者面对的动态库,对Linux发行版兼容性差的缺点和痛点出发,本文梳理问题、探讨并分享3种解决思路。Linux系统如何知道哪些路径下有动态链接库可供链接加载?可借助ldconfig...【详细内容】
2024-01-09  Search: 动态库  点击:(62)  评论:(0)  加入收藏
如何在C#中调用C++编写的动态库?
场景和优点在以下场景下,可能会使用C#调用C++编写的dll: C++库已经存在并且经过了充分测试和验证,需要被C#项目重复使用时; C++编写的库中包含高性能计算、海量数据处理等需要...【详细内容】
2023-06-08  Search: 动态库  点击:(349)  评论:(0)  加入收藏
Linux下动态库的显性调用(dlopen)和隐性调用区别
在linux环境下编程,我们如果想要使用第三方的库,基本上有以下几种方式。1、将第三方库的源码合并到我们的工程项目代码中,一起编译。2、将第三方库编译成静态库(xxx.a),我们在...【详细内容】
2023-03-22  Search: 动态库  点击:(181)  评论:(0)  加入收藏
C++调用动态库两种方式和Python调用C++动态库
创建动态库动态库是在程序运行时加载的库文件,并不占用程序本身大小。选择动态库项目:新建.h和.cpp文件:# cat.h#pragma onceextern "C" _declspec(dllexport) int sum(int a,...【详细内容】
2023-03-16  Search: 动态库  点击:(248)  评论:(0)  加入收藏
Linux下动态库(.so)和静态库(.a) 的区别
linux下有两种库:动态库和静态库(共享库)二者的不同点在于代码被载入的时刻不同。静态库的代码在编译过程中已经被载入可执行程序,因此体积比较大。动态库(共享库)的代码在...【详细内容】
2020-10-12  Search: 动态库  点击:(330)  评论:(0)  加入收藏
▌简易百科推荐
Win10/Win11和 macOS用户反馈:谷歌云服务“捆绑”系统 DNS 设置
IT之家 4 月 6 日消息,谷歌公司承认旗下的 Google One 订阅服务中存在问题,在 Windows 10、Windows 11 以及 macOS 系统上会更改系统 DNS 设置,变更为 8.8.8.8 地址。Google On...【详细内容】
2024-04-08    IT之家  Tags:Win10   点击:(2)  评论:(0)  加入收藏
微软 Edge 浏览器将迎来“内存限制器”功能,用户可自主控制 Edge 内存占用
IT之家 3 月 28 日消息,微软即将为其 Edge 浏览器带来一项实用新功能,据悉该公司正在测试一项内置的内存限制器,这项功能可以让用户限制 Edge 所占用的内存,防止浏览器超出内存...【详细内容】
2024-03-29    IT之家  Tags:Edge   点击:(13)  评论:(0)  加入收藏
一寸照片的大小如何压缩?四个实测效果很好的方法
一寸照片作为生活中常见的尺寸之一,常用于各类证件照与证明文件的制作。然而,受限于其较为狭小的尺寸,上传及打印过程中很容易出现尺寸超限的情况。所以,这个时候就需要对其体积...【详细内容】
2024-03-18  宠物小阿涛    Tags:压缩   点击:(12)  评论:(0)  加入收藏
手机投屏到电脑/电视的方法
方法一:Win10自带的投影功能1、将手机和电脑连接同一个无线网络。2、选择【开始】>【设置】>【系统】>【投影到此电脑】3、将默认的始终关闭的选项更改为所有位置都可用。4、...【详细内容】
2024-03-18    老吴讲I  Tags:投屏   点击:(13)  评论:(0)  加入收藏
微软商店怎么卸载应用 一分钟快速看懂!
微软商店怎么卸载应用 一分钟快速看懂!微软公司(Microsoft Corporation)是一家全球领先的科技企业,总部位于美国华盛顿州的雷德蒙德。成立于1975年,由比尔&middot;盖茨和保罗&mid...【详细内容】
2024-02-27  婷婷说体育    Tags:微软商店   点击:(35)  评论:(0)  加入收藏
微软Edge浏览器新功能:手机上传 配对设备直接传文件
2月21日,微软最新的稳定版本Edge浏览器在Windows 11/10端加入了“手机上传”功能。这一功能允许用户直接从移动设备上上传文件,适用于所有网站,并且没有文件格式限制。要使用这...【详细内容】
2024-02-21    中关村在线  Tags:Edge   点击:(127)  评论:(0)  加入收藏
什么是虚拟机?你知道吗?
谁都没想到 Sun 公司技术如此的强大,却在之后的岁月里逐渐走向陨落,因为不懂销售和运营,导致公司财务逐渐出现亏损,在 2009 年,Oracle 公司以现金方式收购 Sun 公司,交易价格达 74...【详细内容】
2024-02-19  Java极客技术  微信公众号  Tags:虚拟机   点击:(43)  评论:(0)  加入收藏
怎么查看电脑使用记录
查看电脑使用记录是一项常见的操作,可以帮助用户了解自己或其他人在电脑上进行了哪些操作。下面是一个详细的解释,包括查看浏览历史、文件访问记录、应用程序使用记录以及其他...【详细内容】
2024-02-06  编程资料站    Tags:使用记录   点击:(81)  评论:(0)  加入收藏
电脑虚拟内存怎么设置?1分钟快速增加内存!
“我电脑里的内存好像不太够用,因此,我想在电脑里增加一些虚拟内存。不知道我应该怎么操作呢?有什么比较简单的此操作方法吗?” 虚拟内存是计算机系统内存管理的一种技术,它为程...【详细内容】
2024-02-06  数据蛙恢复专家    Tags:虚拟内存   点击:(61)  评论:(0)  加入收藏
新手制作ai写真都是用哪些工具?
春节即将来临,你的朋友圈是否已经被各种春节主题的个人写真刷屏了?看到那么多美照,你是否也心动了?其实,制作个人写真并不需要专门去拍摄,现在有很多AI写真软件可以轻松制作出自己...【详细内容】
2024-01-31  雨后海棠    Tags:ai写真   点击:(60)  评论:(0)  加入收藏
站内最新
站内热门
站内头条