设为首页 加入收藏

TOP

linux共享库的版本控制(一)
2017-10-12 18:07:35 】 浏览:4493
Tags:linux 共享 版本 控制

  前几天看到一篇介绍linux共享库版本控制及使用的文章,觉得不错,这里就与大家分享一下。

1. Linux约定

  经常看到Linux中,共享库的名字后面跟了一串数字,比如:libperl.so.5.18.2。其实就是版本号,作用是为了更加方便的管理动态库,比如升级。往往系统中存在一个库的多个版本,那么Linux 系统如何控制多个版本的问题?Window之前没有处理好,为此专门有个名词来形容这个问题:“Dll hell”,其严重影响软件的升级和维护。“Dll hell”是指windows上动态库的新版本覆盖了旧版本,但是却不兼容老版本,所以程序升级之后,动态库更新导致程序运行不起来。在Linux操作系统下也有同样的问题,那么它是怎么解决的呢?

Linux引入了一套机制,如果遵守这个机制就可以避免这个问题。 但是这只事一个约定,不是强制的。通常建议用户遵守这个约定,否则也会出现Linux版的“Dll hell”问题。 下面来介绍一个这个机制。 这个机制是通过文件名,来控制动态库(shared library)的版本。

Linux上的shared library有三个名字,分别是:

  • 共享库本身的文件名(real name)

    其通常包含完整的版本号,比如:libmath.so.1.1.1234 。lib是Linux库的约定前缀,math是共享库名字,so是共享库的后缀名,1.1.1234的是共享库的版本号,由主版本号+小版本号+build号组成。主版本号,代表当前动态库的版本,如果共享库的接口发生变化,那么这个版本号就要加1;后面的两个版本号(小版本号build号)是用来指示库的更新迭代号,表示在接口没有改变的情况下,由于需求发生变化等因素,开发的新代码。

  • 共享库的soname(Short for shared object name)

    用来告诉应用程序,在加载共享库的时候,应该使用的文件名。其格式为lib + math + .so + (major version number)
    其只包含主版本号,换句话说,也就是只要共享库的接口没有变,soname就能与real name保持一致,因为主版本号一样。所以在库的real name的小版本号build号发生改变时,应用程序仍然可以通过soname得知,要使用的是哪个real name。不明白?等会给个例子来说明。

  • 共享库的链接名(link name)

    是专门为应用程序在编译时的链接阶段而用的名字。这个名字就是lib + math +.so ,比如libmath.so。其是不带任何版本信息的。在共享库的编译过程中,编译器将生成一个共享库及real name,同时将共享库的soname写在共享库文件里的文件头里面。可以用命令readelf -d sharelibrary | grep soname查看。
    在应用程序引用共享库时,链接选项里面用的是共享库的link name。通过link名字找到对应的real name动态库,并且把其中的soname提取出来,写在应用程序自己的文件头的共享库字段里面。当应用程序运行时,就会通过soname,结合动态链接程序(ld.so),在给定的路径下加载real name的共享库。

2.使用示例

  这里我们写了一个简单的例子,包含了三个文件,分别是:

  2.1 清单1 test.h

1 #ifndef _TEST_H_
2 #define _TEST_H_
3 void print_hello();
4 #endif

  2.2 清单2 test.c

1 #include <stdio.h>
2 #include "test.h"
3 void print_hello()
4 {
5     printf("this is a test for shared lib");
6 }

  2.3 清单3 main.c

1 #include <stdio.h>
2 #include "test.h"
3 int main()
4 {
5     print_hello();  
6     return 0;
7 }

  首先编译共享库:

1 gcc -fPIC -o test.o -c test.c
2 gcc -shared -Wl,-soname,libtest.so.0 -o libtest.so.0.0.0 test.o

  然后就生成了libtest.so.0.0.0,这就是库的real name。另外,链接选项里面的-Wl,-soname,libtest.so.0告诉编译器,库的soname是libtest.so.0,我们可以看到real name的头文件里面,已经包含了这样的信息:

1 readelf -d libtest.so.0.0.0 | grep soname
2 0x0000000e (SONAME) Library soname: [libtest.so.0]

  如果没有指定soname,库的头文件里面是没有这个字段的。 有了库以后,下一步是链接到应用程序里面,我们需要这样写:

1 gcc -c -o main.o main.c
2 gcc -L. -o main main.o -ltest

  但是会报错“cannot find -ltest”。这里因为,链接选项制定的是link name,而根据linux的规则,此目录下面并没有libtest.so文件,所以需要先生成link name文件。

1 ln -s libtest.so.0.0.0  libtest.so.0
2 ln -s libtest.so.0 libtest.so

  或者一步到位:

1 ln -s libtest.so.0.0.0 libtest.so

  然后再进行应用程序的编译:gcc -L. -o main main.o -ltest。ok,生成了我们需要的main可执行程序。查看一下,其引用的共享库:

1 ldd main
2     linux-gate.so.1 =>  (0xb76fb000)
3     libtest.so.0 => not found
4     libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb752e000)
5     /lib/ld-linux.so.2 (0xb76fc000)

  没错,链接时,应用程序需要的库正是我们指定的soname,而不是link name或者real name。所以应用程序正是通过soname去寻找真正的real name库。这有什么好处吗?答案后面揭晓。另外,上面的输出中,我们发现,libtest.so.0是not found的状态。为什么呢?因为这个库的当前所在路径并不在链接程序(ld.so)的搜索路径之中,所以无法找到。如何解决?这篇文章就不多说了,这里提供几个方案:

  • 改变LD_LIBRARY_PATH

    export LD_LIBRARY_PATH=/home/bow/all/program/test/lib_version_test:$LD_LIBRARY_PATH

    这里/home/bow/all/program/test/lib_version_test是共享库的路径。虽然改变LD_LIBRARY_PATH能达到目的,但是不推荐使用,因为这是一个全局的变量,其他应用程序可能受此影响,导致各种库的覆盖问题。如果要清楚这个全局变量,使用命令unset LD_LIBRARY_PATH

首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇1048 石子归并 下一篇Win10或Win8下ObjectARX2015 Wiza..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目