Linux下共享库的理解

来源:互联网 发布:淘宝有仿大牌吗 编辑:程序博客网 时间:2024/05/23 00:07

 

Linux下的共享库类似windows下的dll,共命令约定如下:

静态库一般由字母 lib 开头,并有 .a 的扩展名,而共享对象有两个不同的名称:soname 和 real name。

soname 包含前缀 "lib",然后紧跟库名,其次是 ".so"(后面紧跟另一个圆点),以及表明主版本号的数字。

soname 可以由前缀的路径信息来限定。real name 是包含库的已编译代码的真正文件名。

real name 在 soname 后添加一个圆点、小的数字、另外一个圆点和发布号。格式如下: 

libxxxx.so.major.minor 

其中,xxxx是库的名字,major是主版本号,minor 是次版本号或叫发布号,次版本号和其相应的圆点是可选的。

soname是记录在共享库中的,其它库使用这个共享库时,实际上只需要的提供soname,动态链接器会找到名称是soname的动态库给程序使用。

这种带版本号的共享库主要是为了你可以很方便的升级你的函数库,如果某个API改变了,创建库的程序会改变主版本号,然而,如果一个函数升级了某个函数库,而功能没有发生变化,这时只需要改变次版本号,由于只改变了次版本号,所以soname没有发生改变,这样就可以做到与旧的共享库保持兼容。

 

 

下面简要说明动态库的编写过程:

/* file libhello.h - for example use! */ 

void printhello(); 

库的代码很基本,在下一个清单中显示。 

 

/* file libprint.c */ 

#include "stdio.h" 

void printhello() 

{

    printf("hello opendba/n"); 

}

 

$ gcc -fPIC -c libhello.c 

$ ld -shared -soname libhello.so.1 -o libhello.so.1.0 -lc libhello.o

 

-soname也可以用-h代替。 

 

注意,gcc 命令行中的 -fPIC 选项。这是生成 Position-Independent Code 所必须要的。把这个命令翻译出来就是:生成可以在进程的进程空间的任何地方载入的代码。这对于共享对象是非常重要的。使用这个选项,使得必须执行重定位的数量降低到最少。一旦载入可执行程序使用的共享对象,就必须给它分配一些空间。必须给文本和数据部分配一些位置。如果它们不是以“位置独立”方式来构建,那么载入共享对象时,程序要做大量的重定位,这会影响到性能。 

 

现在我们分析一下传给 ld 的选项。-shared 选项表明输出的文件被认为是共享的库。通过 -soname name 选项,可以指定 soname 是什么。-o name 指定了共享对象的real name,也就是实际生成的动态库的文件名称。

 

为了让动态链接库为系统所共享,还需运行动态链接库的管理命令--ldconfig
ldconfig  命令的用途,主要是在默认搜寻目录(/lib和/usr/lib)以及动态库配置文件/etc/ld.so.conf内所列的目录下,搜索出可共享的动态 链接库(格式如前介绍,lib*.so*),进而创建出动态装入程序(ld.so)所需的连接和缓存文件.缓存文件默认为  /etc/ld.so.cache,此文件保存已排好序的动态链接库名字列表.

 

 

ldconfig -p 输出共享库soname实际对应的共享库的文件名称。

 

  一个程序/shared库一般都要依赖其他的一些库,这可以用ldd来查看,它列出了依赖的库的soname,因为实际依赖是库的接口,而soname正是反映了库的接口信息。linux使用ELF作为可执行程序和库的格式,这些依赖的库的soname保存在ELF的某个fileld里。当一个可执行程序执行时,ld.so负责把它所依赖的shared库加载到内存并链接,它按照以下顺序寻找shared库:

      1. 在LD_LIBRARY_PATH环境变量指定的目录下
  2. ld.so.cache文件该shared库对应的文件
  3. /usr/lib和/lib目录下

 

     环境变量:

 

  • LD_BIND_NOW --- 正常来讲,函数在呼叫之前是不会让程式寻找(looked up)的.设定这个旗号会使得程式库一载入,所有的寻找(lookups)便会发生,同时也造成起始的时间(startup time)较慢.当你想测试程式,确定所有的连结都没有问题时,这项旗号就变得很有用.
  • LD_PRELOAD 可以设定一个档案,使其具有*覆盖*(overriding)函数定义的能力.例如,如果你要测试记忆体分配的方略(strategies),而且还想置换*malloc*,那麽你可以写好准备替换的副程式(routine),并把它编译成mallolc.,然後:
    $ LD_PRELOAD=malloc.o; export LD_PRELOAD$ some_test_program
    LD_ELF_PRELOAD 与 LD_AOUT_PRELOAD 很类似,但是仅适用於正确的二进位型态.如果设定了 LD_something_PRELOAD 与 LD_PRELOAD ,比较明确的那一个会被用到.
  • LD_LIBRARY_PATH 是一连串以分号隔离的目录名称,用来搜寻共享程式库.对ld而言,并没有任何的影响;这项只有在执行期间才有影响.另外,对执行setuid与setgid的程式而言,这一项是无效的.而LD_ELF_LIBRARY_PATHLD_AOUT_LIBRARY_PATH这两种旗号可根据各别的二进位型式分别导向不同的搜寻路径.一般正常的运作下,不应该会用到LD_LIBRARY_PATH;把需要搜寻的目录加到/etc/ld.so.conf/里;然後重新执行ldconfig.
  • LD_NOWARN 仅适用於a.out.一旦设定了这一项(LD_NOWARN=true; export LD_NOWARN),它会告诉载入器必须处理fatal-warnings(像是次要版本不相容等)的警告讯息.
  • LD_WARN 仅适用於ELF.设定这一项时,它会将通常是致命讯息的"Can*t find library"转换成警告讯息.对正常的操作而言,这并没有多大的用处,可是对ldd就很重要了.
  • LD_TRACE_LOADED_OBJECTS 仅适用於ELF.而且会使得程式以为它们是由ldd所执行的:
    $ LD_TRACE_LOADED_OBJECTS=true /usr/bin/lynx        libncurses.so.1 => /usr/lib/libncurses.so.1.9.6        libc.so.5 => /lib/libc.so.5.2.18

 

 

原创粉丝点击