linux 共享库查找顺序,第8章 Linux共享库的组织

本文介绍了共享库的管理和版本控制方法,包括共享库的基本概念、命名规则、ABI兼容性问题及解决办法、符号版本控制等内容。同时,还探讨了共享库在Linux系统中的存放位置及其依赖关系的管理。

大量的共享对象需要组织,于是形成了共享库,本章介绍这个库。

共享库和程序可以分别独立更新和开发,但是这可能导致接口的更改和删除,致使程序无法正常使用。

共享库的更新分两类——兼容更新和不兼容更新。前者不更改接口,后者更改。

ABI(Application Binary Interface):二进制接口,就是软件层面上的接口。

表8-1列出了几种常见的共享库更改方式。

P256还列举了可能导致C语言ABI不兼容的几种情况。

P257还说明了防止C++ABI不兼容的几种措施。

Linux下的共享库命名形式如下:

libname.so.x.y.z

前面必须有lib表示它是共享库。

name是这个共享库的名字。

x是主版本号,表示重大升级,不同主版本号的共享库之间不兼容。

y是次版本号,表示增量升级,即只添加新功能不改变原有功能,它实现了共享库的向下兼容。

z是发布版本号,表示只是做了些修正,相同主版本号和此版本号的共享库之间完全兼容。

SO-NAME程序需要记录什么

so-name命名机制用于记录共享库之间的依赖关系。

so-name规定了共享库的接口,因为它的名字中间没有次版本号和发布版本号只保留了主版本号。

模块在编译、链接和运行时应该只使用so-name,而忽略此版本号和发布版本号,如若不然只要共享库一更新,原有的程序就无法使用了。

so-name自动指向最新版本的共享库,它存在于ELF中的dynamic段中,这样在动态链接时动态链接器就可以自动查找最新的共享库了。

某些程序依赖于高次版本号的共享库,却运行在低此版本号的共享库,那么低次版本号的共享库会缺少高次版本号中的独有符号,这就是所谓的版本交会问题。

很简单,就是在主版本号维持不变的情况下,给每次增加的全局符号分配一个相应的版本号,这样每次添加的东西都有版本了。

范围机制,它类似于高级语言中的访问控制,就是把以前的共享库都设为当前共享库的私有成员,外界无法访问。

也可以理解成当前的共享库继承了以前的共享库。

程序里面记录的是最新的共享库版本号,有点像虚函数机制,它是在程序实际使用的时候调用实际版本的共享库。

动态链接器会在链接时候判断程序中记录的最新版本号,然后再与系统中的共享库版本号比对,如果后者次版本号小于等于前者,那就没有任何问题,否则系统阻止程序运行。

Linux中的符号版本

GCC允许不同版本符号的重载,这是为了在共享库发生比较小的变化时不至于影响主版本号。

Linux还允许不同版本的函数符号的重载。

FHS(File Hierarchy Standard)规定了文件的组织形式,目录的结构和作用。共享库也是按照这个标准存放的。

它规定系统有两个存放共享库的位置。

1、/lib,存放系统关键和基础的共享库。

2、/usr/lib,(usr:Unix System Resource),它存放着非关键性的系统运行时所需要的共享库。

3、/usr/local/lib,它存放的是第三方应用程序的共享库。

动态链接所依赖的模块都记录在ELF中的dynamic段中的DT_NEED类型的项表示。

如果是绝对路径,那就直接按照绝对路径来查找。

如果是相对路径,那么动态链接器会在/lib、/usr/lib、/etc/ld.so.conf中指定的目录中查找。

ld.so.conf是一个文本配置文件,可能包含其他配置信息。

为了加快查找速度和统一管理,Linux下有一个叫ldconfig的程序能创建、更新和删除so-name,而且还能缓存so-name。这样在查找所需的共享库的时候先在缓存中查,然后再到系统中查,这样就起到了加快查找速度的作用了。

LD_LIBRARY_PATH,用于临时性地改变共享库的路径,而不影响其他程序。它主要用于共享库的调试、测试和应用程序级别的虚拟等。

一般来讲LD_LIBRARY_PATH可能包含若干个路径,它们之间用分号间隔。它提供了用户自定义的查找共享库的路径,其优先级高于系统默认的路径。

动态链接器是按照下列顺序查找共享目标文件的:

1、首先在LD_LIBRARY_PATH指定的路径下查找,如果找不着就转到2。

2、有缓存/etc/ld.so.cache指定的路径,如果找不到就转到3。

3、系统默认的共享库目录,如果找不到就报错。

LD_PRELOAD,动态链接的预装载,也是环境变量,它可以指定一些共享库和目标文件预先装载。LD_PRELOAD所指定的文件比LD_LIBRARY_PATH所指定的文件的优先级还要高,而且无论程序是否依赖它们,都会被装载。

LD_DEBUG,也是环境变量,用于打开动态链接器的调试功能。

就是如何用gcc命令创建共享库。

三个重要的参数:

1、shared,指定这是共享对象。

2、fPIC,指定按照代码无关技术生产输出文件。

3、WL,将指定参数传给编译器。

-rpath可以指定共享库的查找路径,这有点像LD_LIBRARY_PATH,它俩的好处在于你可以测试新的共享库而不影响原来的共享库的使用。因为你可以在不更改整体共享库路径的情况下,更改个别共享库路径,单独对该共享库进行调试。

安装共享库的方法:

1、直接复制粘贴到指定目录下,比如/lib,/usr/lib等,然后运行ldconfig,它需要root权限。

2、建立相应so-name软连接,也是使用ldconfig。

共享库居然也有构造和析构函数。

构造函数是可以指定的,只要在某函数声明上加上__attribute__((constructor))属性即可。毫无疑问它完成的也是一些初始化的工作,并且在main之前执行。

同样,析构函数需要早函数声明上加上__attribute__((destructor)),它会在main之后执行,做一些收尾工作。

这俩函数需要系统默认的标准运行库和启动文件。

与高级语言中构造函数和析构函数不同的是,这里的析构函数可以有多个。当然构造函数在这一点上与高级语言是相同的,也是允许有多个的。各构造和析构函数都有优先级,并且按照这个优先级执行。

共享库还可以是链接脚本文件。

这个共享库脚本也叫动态链接脚本。

<think> 我们正在讨论跨平台开发中Qt DLL和Linux共享库的区别。根据引用资料和Qt开发经验,我将从文件格式、命名规则、链接方式、版本管理、部署方式以及跨平台兼容性等多个维度进行系统对比。 --- ### 1. **文件格式与命名规则** - **Qt DLL (Windows)** - 文件扩展名:`.dll` - 导入库文件:`.lib`(用于隐式链接) - 命名示例:`MyLibrary.dll` - 引用说明:Windows动态库无强制命名规范,但Qt遵循`<库名>.dll`格式[^1]。 - **Linux共享库** - 文件扩展名:`.so`(Shared Object) - 遵循命名规范:`lib<库名>.so.x.y.z` - `x`:主版本号(不兼容变更) - `y`:次版本号(向后兼容的新功能) - `z`:发布版本号(Bug修复) - 引用说明:如引用[3]所述,Linux共享库通过版本号管理兼容性[^3]。 **关键区别**: - Linux强制`lib`前缀+版本号,Windows无此限制。 --- ### 2. **构建工具链差异** - **Windows (MSVC)** - 编译器:MSVC - 链接器参数:`/DLL` - 导出符号:需显式声明(`__declspec(dllexport)`或`.def`文件) - **Linux (GCC)** - 编译器:GCC/Clang - 链接器参数:`-shared`(引用[4]提到) - 导出控制:默认导出所有符号,可通过`__attribute__((visibility("hidden")))`隐藏 **Qt的跨平台封装**: Qt宏`Q_DECL_EXPORT/Q_DECL_IMPORT`自动适配平台: ```cpp #if defined(Q_OS_WIN) #define MY_EXPORT __declspec(dllexport) #else #define MY_EXPORT __attribute__((visibility("default"))) #endif ``` --- ### 3. **运行时加载机制** - **Windows** - 隐式加载:通过`.lib`导入库在编译时绑定 - 显式加载:`LoadLibrary()` + `GetProcAddress()` - **Linux** - 隐式加载:编译时指定`-l<库名>`(如`-lMyLibrary`) - 显式加载:`dlopen()` + `dlsym()` **Qt统一封装**: 使用`QLibrary`类实现跨平台加载: ```cpp QLibrary lib("MyLibrary"); if (lib.load()) { auto func = (AddFunc)lib.resolve("add"); // ... } ``` --- ### 4. **依赖管理** | **平台** | **依赖查找逻辑** | **部署工具** | |---------------|----------------------------------|--------------------| | Windows | 顺序:EXE目录 → PATH环境变量 | `windeployqt` | | Linux | `/usr/lib`,`LD_LIBRARY_PATH`等 | `linuxdeployqt`或手动 | **兼容性问题**: - Windows需确保VC++运行时库存在(引用[1]强调运行时库版本匹配)[^1] - Linux需处理`LD_LIBRARY_PATH`或使用`rpath`(如`-Wl,-rpath,$ORIGIN`) --- ### 5. **版本管理策略** - **Windows DLL** - 无内置版本控制,依赖文件名或元数据(易引发"DLL Hell") - 解决方案:并行程序集(Side-by-Side Assembly) - **Linux .so** - 符号链接管理多版本: ```bash libMyLib.so.1 → libMyLib.so.1.0.3 libMyLib.so → libMyLib.so.1 ``` - 兼容性规则(引用[3]): - 主版本号不同 ⇒ 不兼容 - 次版本号更高 ⇒ 向后兼容 --- ### 6. **跨平台开发建议** 1. **头文件设计**:使用预处理器隔离平台代码 ```cpp #if defined(Q_OS_LINUX) // Linux特定声明 #elif defined(Q_OS_WIN) // Windows特定声明 #endif ``` 2. **统一构建系统**:推荐使用CMake(引用[2]提到跨平台环境差异)[^2] ```cmake add_library(MyLibrary SHARED ${SOURCES}) target_link_libraries(MyLibrary Qt::Core) ``` 3. **字符编码**:始终使用UTF-8(引用[5]强调编码统一)[^5] 4. **路径处理**:用`QDir`和`QFileInfo`代替直接字符串拼接。 --- ### 总结:核心差异对比表 | **特性** | **Qt DLL (Windows)** | **Linux共享库** | |--------------------|-------------------------------|-------------------------------| | 文件格式 | `.dll` + `.lib` | `lib<名>.so.x.y.z` | | 版本管理 | 弱(依赖文件名) | 强(符号链接+语义版本) | | 导出符号控制 | 显式声明 | 默认全导出,需手动隐藏 | | 运行时依赖解析 | EXE目录 → PATH | `/usr/lib`,`LD_LIBRARY_PATH` | | 跨平台兼容关键 | VC++运行时库[^1] | GLIBC版本 | > 通过Qt的抽象层(如`QLibrary`)和CMake构建系统,可显著降低跨平台差异带来的复杂度[^5]。 §§相关问题§§ 1. Qt项目如何通过CMake实现Windows/Linux双平台动态库构建? 2. 如何解决Linux共享库的GLIBC版本兼容性问题? 3. Windows下Qt DLL如何实现版本控制以避免"DLL Hell"? 4. 在Qt中如何编写同时支持显式和隐式加载的动态库接口? 5. 部署Qt跨平台动态库时如何处理第三方依赖? </think> ### 跨平台开发中Qt DLL与Linux共享库的区别 #### 1. **文件格式与命名规范** | 特性 | Qt DLL (Windows) | Linux共享库 | |------------------|------------------------------------------|--------------------------------------| | **文件扩展名** | `.dll` | `.so` | | **命名规则** | 自由命名<br>(如`MyLib.dll`) | 强制`lib`前缀+版本号<br>(如`libmylib.so.1.2.3`)[^3] | | **导入库文件** | 需配套`.lib`文件 | 不需要单独导入库 | | **版本管理** | 无内置版本控制 | 语义化版本控制<br>`主.次.发布`三字段[^3] | #### 2. **构建与链接机制** **Windows DLL构建:** ```cmake add_library(MyDLL SHARED ${SOURCES}) target_link_libraries(MyDLL Qt::Core) # 链接Qt模块 ``` - 依赖VC++运行时库(需匹配VS版本)[^1] - 导出符号需显式声明:`__declspec(dllexport)` **Linux共享库构建:** ```bash g++ -shared -fPIC -o libmylib.so source.cpp $(pkg-config --cflags --libs Qt5Core) ``` - 使用`-shared`编译标志[^4] - 默认导出所有符号,可通过`__attribute__((visibility("default")))`控制 #### 3. **版本兼容性机制** | **兼容类型** | Windows DLL | Linux共享库 | |------------------|--------------------------------------|------------------------------------------------| | **主版本变更** | 无强制处理 | 必须保留旧版库(不兼容)[^3] | | **次版本升级** | 可能引发冲突 | 向后兼容(高版本兼容低版本)[^3] | | **发布版本更新** | 直接替换风险高 | 完全兼容(仅错误修复)[^3] | #### 4. **运行时加载差异** **Windows动态加载示例:** ```cpp HINSTANCE hDll = LoadLibrary(L"MyDLL.dll"); auto func = (AddFunc)GetProcAddress(hDll, "add"); ``` **Linux动态加载示例:** ```cpp void* handle = dlopen("libmylib.so", RTLD_LAZY); auto func = (AddFunc)dlsym(handle, "add"); ``` - Linux需显式链接`libdl`:编译时加`-ldl`参数 #### 5. **部署与依赖管理** | **方面** | Windows | Linux | |------------------|-----------------------------------|-----------------------------------| | **依赖解析** | 搜索路径:<br>1. EXE目录<br>2. PATH环境变量 | 搜索路径:<br>1. `/usr/lib`<br>2. `LD_LIBRARY_PATH` | | **Qt库部署** | `windeployqt`工具自动收集依赖 | `linuxdeployqt`或手动部署 | | **运行时依赖** | VC++ Redistributable[^1] | GLIBC版本匹配 | #### 6. **跨平台开发注意事项 1. **路径处理**: ```cpp // 跨平台路径分隔符处理 QString path = QDir::toNativeSeparators("plugins/config.ini"); ``` 2. **符号导出宏**: ```cpp #ifdef Q_OS_WIN #define API __declspec(dllexport) #else #define API __attribute__((visibility("default"))) #endif ``` 3. **文件系统差异**: - 换行符:Windows(`\r\n`),Linux(`\n`) - 字符编码:统一使用UTF-8[^2] > **关键设计原则**:通过Qt的抽象层(如`QLibrary`)封装平台差异,核心业务逻辑保持平台无关[^5]。典型跨平台项目结构: > ``` > /src > /common # 平台无关代码 > /windows # DLL特定实现 > /linux # .so特定实现 > ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值