库技术 - Linux库详解参考模板_第1页
库技术 - Linux库详解参考模板_第2页
库技术 - Linux库详解参考模板_第3页
库技术 - Linux库详解参考模板_第4页
库技术 - Linux库详解参考模板_第5页
已阅读5页,还剩10页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、Linux库详解1 库基本概念1.1 库的分类   有两种说法,如果熟悉WIN平台下的DLL,相信不难理解:    库可以有三种使用的形式:静态、共享和动态。静态库的代码在编译时就已连接到开发人员开发的应用程序中,而共享库只是在程序开始运行时才载入,在编译时, 只是简单地指定需要使用的库函数。动态库则是共享库的另一种变化形式。动态库也是在程序运行时载入,但与共享库不同的是,使用的库函数不是在程序运行开 始,而是在程序中的语句需要使用该函数时才载入。动态库可以在程序运行期间释放动态库所占用的内存,腾出空间供其它程序使用。由于共享库和动态库并没有在

2、程序中包括库函数的内容,只是包含了对库函数的引用,因此代码的规模比较小。    Linux下的库文件分为共享库和静态库两大类,它们两者的差别仅在程序执行时所需的代码是在运行时动态加载的,还是在编译时静态加载的。区分库类型最好的方法是看它们的文件后缀,通常共享库以.so(Shared Object的缩写)结尾,静态链接库通常以.a结尾(Archive的缩写)。在终端缺省情况下,共享库通常为绿色,而静态库为黑色。  已经开发的大多数库都采取共享库的方式。ELF格式的可执行文件使得共享库能够比较容易地实现,当然使用旧的a.out模式也可以实现库的共享。Linu

3、x系统中目前可执行文件的标准格式为ELF格式。 .a的是为了支持较老的a.out格式的可执行文件的 .so的是支持elf格式的可执行文件的库。   .a是静态库文件,可以用ar 命令生成。 .so是动态库文件,编译时加上指定的选项即可生成,具体选项看相应的系统文档了。1 / 151.2 库的命名规则GNU库的使用必须遵守Library GNU Public License(LGPL许可协议)。该协议与GNU许可协议略有不同,开发人员可以免费使用GNU库进行软件开发,但必须保证向用户提供所用的库函数的源代码。库的命名和约定:(1)所以的库都以lib开头,许多开发工具都依赖这个约定,特别

4、是GCC,它会在-l选项所指定的文件名前自动地插入lib. (2)文件名以.a(代表存档,archive)结尾的库都是静态库,文件名以.so(代表共享目标文件,shared object)结尾的库都是共享库,例如,libdl.a是一个静态库,而libc.so是一个共享库。采用旧的a.out格式的共享库的后缀名为.sa。(3) 以_g和_p结尾的库,如libform_g.a和libfor_p.a,是基本库libform的特殊版本。通常以_g结尾的库是调试库,它们编入了特殊的符号和功能,能够增加对采用了这个库的应用程序进行调试的能力。类似地,代码剖析(profiling)库通常在名字后面附加_p,

5、他们包含地代码和符号能够进行复杂地代码剖析和性能分析。如果你使用了这些库中的某一个,那么一旦完成调试或剖析工作,需要使用基本库重新编译你的程序。库名的一般格式为:libname.major_num.minor_num.patch_num, 例如,libgdbm.so.2.0.0, libgdbm.so是库libname, major_num是2,minor_num和patch_num均为0.系统中可用的库都存放在/usr/lib和/lib目录中。这里的name可以是任何字符串,用来唯一标识某个库。该字符串可以是一个单字、几个字符、甚至一个字母。数学共享库的库名为libm.so.5,这里的标识字

6、符为m,版本号为5。libm.a则是静态数学库。X-Windows库名为libX11.so.6,这里使用X11作为库的标识,版本号为6。每个共享函数库都有个特殊的名字,称作“soname”。Soname名字命名必须以“lib”作为前缀,然后是函数库的名字,然后是“.so”,最后是版本号信息。不过有个特例,就是非常底层的C库函数都不是以lib开头这样命名的。每个共享函数库都有一个真正的名字(“real name”),它是包含真正库函数代码的文件。真名有一个主版本号,和一个发行版本号。最后一个发行版本号是可选的,可以没有。主版本号和发行版本号使你可以知道你到底是安装了什么版本的库函数。另外,还有一

7、个名字是编译器编译的时候需要的函数库的名字,这个名字就是简单的soname名字,而不包含任何版本号信息。管理共享函数库的关键是区分好这些名字。当可执行程序需要在自己的程序中列出这些他们需要的共享库函数的时候,它只要用soname就可以了;反过来,当你要创建一个新的共享函数库的时候,你要指定一个特定的文件名,其中包含很细节的版本信息。当你安装一个新版本的函数库的时候,你只要先将这些函数库文件拷贝到一些特定的目录中,运行ldconfig这个实用就可以。Ldconfig检查已经存在的库文件,然后创建soname的符号链接到真正的函数库,同时设置/etc/ld.so.cache这个缓冲文件。这个我们稍

8、后再讨论。Ldconfig并不设置链接的名字,通常的做法是在安装过程中完成这个链接名字的建立,一般来说这个符号链接就简单的指向最新的soname或者最新版本的函数库文件。最好把这个符号链接指向soname,因为通常当你升级你的库函数后,你就可以自动使用新版本的函数库。我们来举例看看:/usr/lib/libreadline.so.3 是一个完全的完整的soname,ldconfig可以设置一个符号链接到其他某个真正的函数库文件,例如是 /usr/lib/libreadline.so.3.0。同时还必须有一个链接名字,例如/usr/lib/libreadline.so 就是一个符号链接指向/us

9、r/lib/libreadline.so.3。2 库操作命令 Linux库操作可以使用命令完成,目前常用的命令是ldd和ldconfig。2.1 ldd ldd是Library Dependency Display缩写,它的作用是显示一个可执行程序必须使用的共享库。 $ ldd /usr/bin/mesg libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0xb7eaf000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0xb7feb000)2.2 ldconfig 库安装到系统以后,为了让动态链接库为系

10、统所认识及共享,就需要运行ldconfig。ldconfig命令的用途,主要是在默认搜寻目录(/lib和 /usr/lib)以及动态库配置文件/etc/ld.so.conf内所列的目录下,搜索出可共享的动态链接库(格式如lib*.so*),进而创建出动态装入程序(ld.so)所需的连接和缓存文件。缓存文件默认为/etc/ld.so.cache,此文件保存已排好序的动态链接库名字列表, ldconfig通常在系统启动时运行,而当用户安装了一个新的动态链接库时,就需要手工运行这个命令。(1)命令格式 ldconfig 选项 libs(2)主要选项 -v或-verbose ldconfig将显示正在

11、扫描的目录、搜索到的动态链接库,以及它所创建的连接的名字。 -f CONF 指定动态链接库的配置文件为CONF,系统默认为/etc/ld.so.conf。 -C CACHE 指定生成的缓存文件为CACHE,系统默认的是/etc/ld.so.cache,文件存放已排好序的可共享的动态链接库的列表。 -p或-print-cache 让ldconfig打印出当前缓存文件所保存的所有共享库的名字。 -r ROOT 改变应用程序的根目录为ROOT。 n ldconfig仅扫描命令行指定的目录,不扫描默认目录(/lib、/usr/lib),也不扫描配置文件/etc/ld.so.conf所列的目录。 运行没

12、有选项的ldconfig命令时,用于更新高速缓冲文件。这个命令主要用于高速缓冲DNS服务器(Caching DNS Server)。高速缓冲DNS服务器的原理是提供查询的历史记录,并且利用这些记录来提高查询的效率。当某个查询是第一次被发送到高速缓冲DNS服务器时,高速缓冲DNS服务器就将此查询的整个过程记录下来,在一定的时期内用它来回答所有相同的查询,从而减少整个DNS系统的负担并且提高查询速度。3 库的路径问题为了让执行程序顺利找到动态库,有三种方法:(1)把库拷贝到/usr/lib和/lib目录下。(2)在LD_LIBRARY_PATH环境变量中加上库所在路径。例如动态库libhello.

13、so在/home/ting/lib目录下,以bash为例,使用命令:$export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/ting/lib(3) 修改/etc/ld.so.conf文件,把库所在的路径加到文件末尾,并执行ldconfig刷新。这样,加入的目录下的所有库文件都可见4 查看库中的符号有时候可能需要查看一个库中到底有哪些函数,nm命令可以打印出库中的涉及到的所有符号。库既可以是静态的也可以是动态的。nm列出的符号有很多,常见的有三种,一种是在库中被调用,但并没有在库中定义(表明需要其他库支持),用U表示;一种是库中定义的函数,用T表示,这是最常

14、见的;另外一种是所谓的 “弱态”符号,它们虽然在库中被定义,但是可能被其他库中的同名符号覆盖,用W表示。例如,假设开发者希望知道上文提到的hello库中是否定义了 printf():$nm libhello.so |grep printfU printfU表示符号printf被引用,但是并没有在函数内定义,由此可以推断,要正常使用hello库,必须有其它库支持,再使用ldd命令查看hello依赖于哪些库:$ldd hellolibc.so.6=>/lib/libc.so.6(0x400la000)/lib/ld-linux.so.2=>/lib/ld-linux.so.2 (0x4

15、0000000)从上面的结果可以继续查看printf最终在哪里被定义,有兴趣可以go on5 库升级Linux系统软件更新很快,新的核心几乎每几个星期就公布一次,其它软件的更新也是非常频繁。多数情况下,盲目跟随潮流的升级并不必要,如果确实需要新版本的特性时再升级。换句话说,不要为了升级而升级。Linux系统中多数软件都是用共享库来编译的,其中包含了在不同程序之间共享的公用子例程。在运行某个程序时,如果看到如下信息:“Incompatible library version”则表明需要将该库升级到程序所需要的版本。库是向下兼容的,也就是说,用老版本库编译的程序可以在新安装的版本库上运行,反之则不

16、行。Linux库函数的升级是一项重要的工作,往往与其它软件包的升级有一定关联作用,所以操作前一定要备份文件。下面看一下如何把Glibc 2.2.4.13升级至2.3.2版本,其过程如下: 1.下载.gz压缩文件并解压在GUN C网站下载的四个.gz压缩文件,解压至一临时目录中:cd /usr/caolinuxtar xzvf glibc-2.3.2.tar.gzcd glibc-2.3.2tar xzvf ./glibc-linuxthreads-2.3.2.tar.gztar xzvf ./glibc-crypt-2.3.2.tar.gztar xzvf ./glibc-localedata

17、-2.3.2.tar.gz 2.建立库函数的安装目录mkdir /usr/higlibccd /usr/higlibc 3.建立编译目录mkdir caocd cao./configure -enable-add-ons=linuxthreads,crypt,localedata -prefix=/usr/higlibc 4.编译与安装makemake checkmake install 5.改变数据库的链接ln -s /usr/higlibc/lib/ld-linux.so.2 /lib/ld-linux.so.2然后,修改/etc/ld.so.conf,加入一行/usr/higlibc/l

18、ib,执行下面代码:ldconfig -v更新/etc/ld.so.cache的内容,列出每个库的版本号,扫描目录和所要创建及更新的链接。 6.更改GCC设置cd /usr/lib/gcc-libcp -r i386-redhat-linux higlibc 7.更新符号链接cd /usr/higlibc/includeln -s /usr/src/linux/include/linuxln -s /usr/src/linux/include/asmln -s /usr/X11R6/include/X118.测试并完成6 高级共享库特性6.1 soname为了在同一系统中使用不同版本的库,可以

19、在库文件名后加上版本号为后缀,例如: libhello.so.1.0,由于程序连接默认以.so为文件后缀名。所以为了使用这些库,通常使用建立符号连接的方式。ln -s libhello.so.1.0 libhello.so.1ln -s libhello.so.1 libhello.so共享库的一个非常重要的,也是非常难的概念是 soname简写共享目标名(short for shared object name)。这是一个为共享库(.so)文件而内嵌在控制数据中的名字。如前面提到的,每一个程序都有一个需要使用的库的清单。这个清单的内容是一系列库的 soname,如同 ldd 显示的那样,共享

20、库装载器必须找到这个清单。soname 的关键功能是它提供了兼容性的标准。当要升级系统中的一个库时,并且新库的 soname 和老的库的 soname 一样,用旧库连接生成的程序,使用新的库依然能正常运行。这个特性使得在 Linux 下,升级使用共享库的程序和定位错误变得十分容易。在 Linux 中,应用程序通过使用 soname,来指定所希望库的版本。库作者也可以通过保留或者改变 soname 来声明,哪些版本是相互兼容的,这使得程序员摆脱了共享库版本冲突问题的困扰。查看/usr/local/lib 目录,分析 MiniGUI 的共享库文件之间的关系6.2 共享库装载器当程序被调用的时候,L

21、inux 共享库装载器(也被称为动态连接器)也自动被调用。它的作用是保证程序所需要的所有适当版本的库都被调入内存。共享库装载器名字是 ld.so 或者是 ld-linux.so,这取决于 Linux libc 的版本,它必须使用一点外部交互,才能完成自己的工作。然而它接受在环境变量和配置文件中的配置信息。文件 /etc/ld.so.conf 定义了标准系统库的路径。共享库装载器把它作为搜索路径。为了改变这个设置,必须以 root 身份运行 ldconfig 工具。这将更新 /etc/ls.so.cache 文件,这个文件其实是装载器内部使用的文件之一。6.3 使用 dlopen另外一个强大的库

22、函数是 dlopen()。该函数将打开一个新库,并把它装入内存。该函数主要用来加载库中的符号,这些符号在编译的时候是不知道的。比如 Apache Web 服务器利用这个函数在运行过程中加载模块,这为它提供了额外的能力。一个配置文件控制了加载模块的过程。这种机制使得在系统中添加或者删除一个模块时,都不需要重新编译了。可以在自己的程序中使用 dlopen()。dlopen() 在 dlfcn.h 中定义,并在 dl 库中实现。它需要两个参数:一个文件名和一个标志。文件名可以是我们学习过的库中的 soname。标志指明是否立刻计算库的依赖性。如果设置为 RTLD_NOW 的话,则立刻计算;如果设置的

23、是 RTLD_LAZY,则在需要的时候才计算。另外,可以指定 RTLD_GLOBAL,它使得那些在以后才加载的库可以获得其中的符号。当库被装入后,可以把 dlopen() 返回的句柄作为给 dlsym() 的第一个参数,以获得符号在库中的地址。使用这个地址,就可以获得库中特定函数的指针,并且调用装载库中的相应函数。7 动态库的编写和使用现在假设有一个叫hello的程序开发包,它提供一个静态库libhello.a 一个动态库libhello.so,一个头文件hello.h,头文件中提供sayhello()这个函数/* hello.h */void sayhello();另外还有一些说明文档。这是

24、一个典型的程序开发包结构。7.1 生成库第一步要把源代码编绎成目标代码。以下面的代码为例,生成上面用到的hello库:/* hello.c */#include <stdio.h>void sayhello()printf("hello,worldn");用gcc编绎该文件,在编绎时可以使用任何合法的编绎参数,例如-g加入调试代码等:gcc -c hello.c -o hello.o1.连接成静态库连接成静态库使用ar命令,其实ar是archive的意思$ar cqs libhello.a hello.o2.连接成动态库生成动态库用gcc来完成,由于可能存在多个

25、版本,因此通常指定版本号:$gcc -shared -Wl, -soname, libhello.so.1 -o libhello.so.1.0 hello.o另外再建立两个符号连接:$ln -s libhello.so.1.0 libhello.so.1$ln -s libhello.so.1 libhello.so这样一个libhello的动态连接库就生成了。最重要的是传gcc -shared 参数使其生成是动态库而不是普通执行程序。-Wl 表示后面的参数也就是-soname,libhello.so.1直接传给连接器ld进行处理。实际上,每一个库都有一个soname,当连接器发现它正在查找

26、的程序库中有这样一个名称,连接器便会将soname嵌入连结中的二进制文件内,而不是它正在运行的实际文件名,在程序执行期间,程序会查找拥有 soname名字的文件,而不是库的文件名,换句话说,soname是库的区分标志。这样做的目的主要是允许系统中多个版本的库文件共存,习惯上在命名库文件的时候通常与soname相同:libxxxx.so.major.minor 其中,xxxx是库的名字,major是主版本号,minor 是次版本号。7.2 使用库当要使用静态的程序库时,连接器会找出程序所需的函数,然后将它们拷贝到执行文件,由于这种拷贝是完整的,所以一旦连接成功,静态程序库也就不再需要了。 然而,

27、对动态库而言,就不是这样。动态库会在执行程序内留下一个标记指明当程序执行时,首先必须载入这个库。由于动态库节省空间,linux下进行连接 的缺省操作是首先连接动态库,也就是说,如果同时存在静态和动态库,不特别指定的话,将与动态库相连接。1.与动态库连接linux默认的就是与动态库连接,下面这段程序testlib.c使用hello库中的sayhello()函数/*testlib.c*/#include <hello.h>#include <stdio.h>int main()sayhello();return 0;使用如下命令进行编译$gcc -c testlib.c -o testlib.o用如下命令连接:$gcc testlib.o -lhello -o testlib在连接时要注意,假设libhello.o 和libhello.a都在缺省的库搜索路径下/usr/lib,如果在其它位置要加上-L参数2. 与静态库连接与静态库连接麻烦一些,主要是参数问题。还是上面的例子:$gcc tes

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论