`
kongweile
  • 浏览: 521460 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

Linux 如何解决共享库的版本控制(避免Dll Hell)

 
阅读更多

Linux 系统,也同样面临和Window一样的问题,如何控制动态库的多个版本问题。Window之前没有处理好,为此专门有个名词来形容这个问题 “Dll hell”,其严重影响软件的升级和维护。 Dll hell 是指windows 上动态库新版本覆盖旧版本,但是却不兼容老版本。常常发生在程序升级之后,动态库更新,原有程序运行不起来;或者装新软件,但是已有的软件运行不起来。 同样Linux操作系统,也有同样的问题,那么它是怎么解决的呢?

Linux 为解决这个问题,引入了一套机制,如果遵守这个机制来做,就可以避免这个问题。 但是这只事一个约定,不是强制的。但是建议遵守这个约定,否则同样也会出现 Linux 版的Dll hell 问题。 下面来介绍一个这个机制。 这个机制是通过文件名,来控制dll (shared library) 的版本。

Linux 上的Dll ,叫shared library,其有三个名字,分别又不同的目的。

第一个是共享库本身的文件名(real name),其通常包含版本号,常常是是这样: libmath.so.1.1.1234 。 lib是Linux 上的库的约定前缀,math 是共享库名子,so 是共享库的后缀名,1.1.1234的是共享库的版本号,其主版本号+小版本号+build号。主板号,代表当前动态库的版本,如果动态库的接口有变化,那么这个版本号就要加1;后面的两个版本号(小版本号 和 build 号)是告诉你详细的信息,比如为一个hot-fix 而生成的一个版本,其小版本号加1,build号也应有变化。 这个文件名包含共享库的代码。

第二个是动态库的soname( Short for shared object name),其是应用程序加载dll 时候,其寻找共享库用的文件名。其格式为

                           lib + math+.so + ( major version number)

其只包含major version number,换句话说,也就是只要其接口没有变,应用程序都可以用,不管你其后minor build version or build version。

问题来了,程序运行时怎么通过soname 找个real name? Soname 存在哪里?如果与real name 关联起来?什么时候存的?

这就是接下来要介绍的第三个共享库的名字,link name,顾名思义,就是在编译过程,link 阶段用的文件名。 其将sonmae 和real name 关联起来。

第三个名字,共享库的连接名(link name),是专门为build 阶段连接而用的名字。这个名字就是lib + math +.so ,比如libmath.so。其是不带任何版本信息的。在共享库编译过程中,连接(link) 阶段,编译器将生成一个共享库及real name,同时将共享库的soname,写在共享库文件里的文件头里面。可以用命令 readelf -d sharelibrary 去查看。

在应用程序引用共享库时,其会用到共享库的link name。在应用程序的link阶段,其通过link名字找到动态库,并且把共享库的soname 提取出来,写在自己的共享库的头里面。当应用程序加载时候就会通过soname 去在给定的路径下寻找该共享库。

下面通过这个代码来说明一下系统是如何做的,并且介绍系统的一些设施和工具:

代码:        

1. File libhello.c

/* hello.c - demonstrate library use. */
#include <stdio.h>
void hello(void) 
{  printf("Hello, library world.\n");}

2. File libhello.h

/* libhello.h - demonstrate library use. */
void hello(void);

3. File main.c

/* main.c -- demonstrate direct use of the "hello" routine */
#include "hello.h"
int main(void) 
{
 hello(); 
return 0;
}

1.生成共享库,关联real name 和soname 。

     gcc -g -Wall -fPIC -c hello.c -o hello.o

     gcc -shared -W,soname,-libhello.so.0 -o libhello.so.0.0.0 hello.o

     将会生成共享库libhello.so.0.0.0.

     可以用系统提供的工具查看共享库的头:

      readelf -d libhello.so.0.0.0 | grep libhello

ox00000000000e(SONAME)    library soname: [libhello.so.0]

2.应用程序,引用共享库。

      先手动生成link 名字,以被后面的程序链接时用

      ln -s libhello.so.0.0.0 libhello.so.0

      gcc -g -Wall -c main.c -o main.o -I.

      gcc  -o main main.o -lhello -L.

      查看编译出来的程序:

      readelf -d main | grep libhello

ox000000000001(NEEDED)    shared library: [libhello.so.0]

      运行该程序,需要指定共享库的路径。 有两种办法,第一种使用环境变量“LD_LIBRARY_PATH”. 两外一种办法就是将共享库拷贝到系统目录(path 环境变量指定的其中一个目录)。

      暂停! 我们还没有解决一个问题是,程序只知道soname,怎么从soname 找到共享库,即real name 文件呢? 这需要我们定义一个link文件,连接到共享库本身。

      ln -s libhello.so.0.0.0 libhello.so.0

      当然这个路径需要放到LD_LIBRARY_PATH环境变量中。

     这样就可以运行该程序。

[Note]Linux 系统提供一个命令 ldconifg 专门为生成共享库的soname 文件,以便程序在加载时后通过soname 找到共享库。 同时该命令也为加速加载共享库,把系统的共享库放到一个缓存文件中,这样可以提高查找速度。可以用下面命令看一下系统已有的被缓存起来的共享库。

     ld -p

3.共享库,小版本升级,即接口不变.

   当升级小版本时,共享库的soname 是不变的,所以需要重新把soname 的那个连接文件指定新版本就可以。 调用ldconfig命令,系统会帮你做修改那个soname link文件,并把它指向新的版本呢。这时候你的应用程序就自动升级了。

4.共享库,主版本升级,即接口发生变化。

  当升级主版本时,共享库的soname 就会加1.比如libhello.so.0.0.0 变为 libhello.so.1.0.0. 这时候再运行ldconfig 文件,就会发现生成两个连接 文件。

    ln -s libhello.so.0---->libhello.so.0.0.0

    ln -s libhello.so.1----->libhello.so.1.0.0

尽管共享库升级,但是你的程序依旧用的是旧的共享库,并且两个之间不会相互影响。

    问题是如果更新的共享库只是增加一些接口,并没有修改已有的接口,也就是向前兼容。但是这时候它的主版本号却增加1. 如果你的应用程序想调用新的共享库,该怎么办? 简单,只要手工把soname 文件修改,使其指向新的版本就可以。(这时候ldconfig 文件不会帮你做这样的事,因为这时候soname 和real name 的版本号主板本号不一致,只能手动修改)。

  比如: ln -s libhello.so.0 ---> libhello.so.1.0.0

  但是有时候,主版本号增加,接口发生变化,可能向前不兼容。这时候再这样子修改,就会报错,“xx”方法找不到之类的错误。

总结一下,Linux 系统是通过共享库的三个不同名字,来管理共享库的多个版本。 real name 就是共享库的实际文件名字,soname 就是共享库加载时的用的文件名。在生成共享库的时候,编译器将soname 绑定到共享库的文件头里,二者关联起来。 在应用程序引用共享库时,其通过link name 来完成,link时将按照系统指定的目录去搜索link名字找到共享库,并将共享库的soname写在应用程序的头文件里。当应用程序加载共享库时,就会通过soname在系统指定的目录(path or LD_LIBRARY)去寻找共享库。

当共享库升级时,分为两种。一种是主板本不变,升级小版本和build 号。在这种情况下,系统会通过更新soname( ldconfig 来维护),来使用新的版本号。这中情况下,旧版本就没有用,可以删掉。

另外一种是主版本升级,其意味着库的接口发生变化,当然,这时候不能覆盖已有的soname。系统通过增加一个soname(ldconfig -p 里面增加一项),使得新旧版本同时存在。原有的应用程序在加载时,还是根据自己头文件的旧soname 去寻找老的库文件。

5.如果编译的时候没有指定,共享库的soname,会怎么样?

  这是一个trick 的地方。第一系统将会在生成库的时候,就没有soname放到库的头里面。从而应用程序连接时候,就把linkname 放到应用程序依赖库里面。或者换句话说就是,soname这时候不带版本号。 有时候有人直接利用这点来升级应用程序,比如,新版本的库,直接拷贝到系统目录下,就会覆盖掉已经存在的旧的库文件,直接升级。 这个给程序员很大程度的便利性,如果一步小心,就会调到类似windows的Dll hell 陷阱里面。建议不要这样做。

【Note】

  1. 指定共享库加载的路径。LD_LIBRARY_PATH 优先与 path 环境变量。

  2. ldd 可以查看程序,或者共享库依赖的库的路径

  3. nm 查看共享库暴露的接口

  4. ldconfig 可以自动生成soname 的连接文件。并提供catch 加速查找。

  5.readelf 可以查看动态库的信息,比如依赖的库,本身的somae。

  6. objdump 与readelf 类似。

  7 ld The GUN linker

  8. ld.so  dynamic linker or loader

  9. as the portable GNU assembley

【Reference】

http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html

分享到:
评论

相关推荐

    Dll_Hell_Solution.7z

    DLL是Windows系统中的一种共享代码库,多个应用程序可以引用同一个DLL,以减少内存占用和提高代码复用。然而,这种设计在带来便利的同时,也引发了一系列的问题。 在DLL Hell中,主要会出现以下几种情况: 1. 版本...

    别再掉进DLL地狱的陷阱里(DLL_Hell

    别再掉进DLL地狱的陷阱里(DLL_Hell)~.NET解决之道

    Delphi多个DLL共享全局数据Demo

    在Delphi编程环境中,开发人员经常需要在多个动态链接库(DLL)之间共享数据,以实现模块化设计和代码重用。"Delphi多个DLL共享全局数据Demo"是一个实例,展示了如何在不同的DLL中有效地管理和交换信息。在这个场景中...

    cximage静态库 动态库 64位linux版本

    本篇文章将深入探讨cximage库的静态库和动态库版本,以及如何在64位Linux系统中编译和使用它们。 首先,让我们理解什么是cximage库。cximage是一个强大的C++类库,用于处理图像文件,支持多种格式如BMP, JPEG, PNG,...

    Linux和Window x64 Opencv 3.4.3所有动态库so和dll文件,以及jar包

    在Linux系统中,`.so`文件是共享对象库,相当于Windows系统中的`.dll`动态链接库。它们包含了运行时程序需要调用的函数和数据。在OpenCV 3.4.3版本中,这些`.so`文件提供了对图像处理、特征检测、机器学习等多种功能...

    opencv库(windows+linux版本)

    对于Linux系统,OpenCV通常以共享库(.so文件)的形式存在。`libopencv_java480.so`是针对Java的Linux版本,它与Windows上的DLL类似,但格式适应于Linux的动态链接机制。这里的版本号480表明这是OpenCV的一个较早...

    Dll Hell Solution Demo

    DLL地狱及其解决方案 ,Dll Hell Solution,此为测试Demo 原文地址:https://www.codeproject.com/Articles/4896/The-DLL-Hell-Problems-and-Solutions#_articleTop

    nilvaiu.dll

    运行labview时,会丢失的dll库文件。

    lazarus调用动态库DLL的例子

    在编程领域,动态链接库(DLL)是一种共享代码的方式,它可以被多个应用程序同时使用,以减少内存占用和提高代码重用。Lazarus是Free Pascal编译器的集成开发环境(IDE),它提供了类似Delphi的界面和组件,用于创建...

    delphi DLL及数据共享

    在使用DLL进行数据共享时,可能会遇到版本兼容性、线程安全、资源管理等问题。为了保证稳定性,应尽量避免在DLL中直接修改全局状态,而是采用参数传递和返回值来交换数据。同时,对于多线程环境,需要确保数据访问...

    windows和Linux中DLL简谈

    动态链接库(DLL,Dynamic Link Library)是一种在Windows和Linux操作系统中广泛使用的共享库,它包含可被多个程序共享的代码和数据。动态链接库允许程序在运行时加载和使用所需的函数,而不是在编译时就将所有依赖...

    dll动态链接库

    DLL(Dynamic Link Library)动态链接库是Windows操作系统中一个核心的特性,它允许多个程序共享同一块内存空间中的代码和数据,从而节省系统资源、提高软件的可移植性和可维护性。DLL文件实质上是一系列函数和数据...

    S7.Net动态链接库DLL.rar

    DLL是一种共享库,它包含了一组可供多个应用程序同时调用的函数或资源。在S7.Net DLL的情况下,这些函数主要用于处理与西门子PLC的通信协议,如MPI、Profibus、Profinet、Ethernet/IP等,以及相关的数据读写操作。...

    dll编写教程.pdf

    DLL 编程技术 ...* 使用版本控制,以避免 DLL 版本问题。 DLL 编程技术是一种非常有用的技术,可以帮助我们编写高效、可靠的软件系统。但是,需要注意 DLL 的一些常见问题和-best Practice,以避免出现问题。

    Win32-dll.rar_DLL库_win32 dll

    DLL(Dynamic Link Library)是Windows操作系统中的一个重要组成部分,它是一种共享库,包含了一组可执行代码和数据,可供多个程序同时使用。Win32 DLL是专为32位Windows环境设计的动态链接库。这些库提供函数、类和...

    动态链接库dll

    动态链接库(DLL)是Windows操作系统中的一个重要概念,它是一种可执行代码的共享库,用于在多个应用程序之间共享函数和资源。DLL文件可以减少内存占用,优化系统性能,因为它们只在内存中加载一次,即可被多个进程...

    欧姆龙PLC与上位机FINS HOSTLINK通讯共享库DLL合集

    资源名:欧姆龙PLC与上位机FINS HOSTLINK通讯共享库DLL合集 资源类型:程序源代码 源码介绍: 欧姆龙PLC与上位机通讯共享库.DLL超全合集,包含串口和网口通讯 适合人群:新手及有一定经验的开发人员

    eclipse_无法定位序数于动态链接库libeay32.dll

    ### Eclipse 无法定位序数于动态链接库 libeay32.dll 的问题解析与解决方案 在使用 Eclipse 这款流行的集成开发环境(IDE)进行 Java 应用程序开发时,可能会遇到一个错误提示:“Eclipse 无法定位序数于动态链接库...

    动态链接库及静态链接库(windows下的.dll .lib和linux下的.so .a).docx

    动态链接库及静态链接库(Windows下的.dll .lib和Linux下的.so .a) 动态链接库和静态链接库是两种不同的库文件类型,它们在编译和链接过程中扮演着重要的角色。下面我们将详细探讨这两种库文件的特点和使用方法。 ...

    cjson编译好的动态链接库,so和dll,32位

    动态链接库是一种将函数库的代码在运行时动态加载到进程中的方式,相比于静态链接库,它节省了程序的存储空间,因为多个程序可以共享同一份库代码。这里的".so"是Linux下的动态链接库扩展名,代表"Shared Object",...

Global site tag (gtag.js) - Google Analytics