在windows平台和linux编译静态库平台下都大量存在着库
本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行
由于windows和linux编译静态库的本质不同,因此二者库嘚二进制是不兼容的
本文仅限于介绍linux编译静态库下的库。
inux下的库有两种:静态库和共享库(动态库)
二者的不同点在于代码被载入的時刻不同。
静态库的代码在编译过程中已经被载入可执行程序因此体积较大。
共享库的代码是在可执行程序运行时才载入内存的在编譯过程中仅简单的引用,因此代码体积较小
库是别人写好的现有的,成熟的可以复用的代码,你可以使用但要记得遵守许可协议
现實中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始因此库的存在意义非同寻常。
共享库的好处是不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例
4、库文件是如何产生的在linux编译静态库下
静态库的后缀是.a,它的产生汾两步
Step 1.由源文件编译生成一堆.o每个.o里都包含这个编译单元的符号表
Step 2.ar命令将很多.o转换成.a,成文静态库
动态库的后缀是.so它由gcc加特定参数编譯产生。
5、库文件是如何命名的有没有什么规范
静态库的名字一般为libxxxx.a,其中xxxx是该lib的名称
6、如何知道一个可执行程序依赖哪些库
ldd命令可以查看一个可执行程序依赖的共享库
7、可执行程序在执行的时候如何定位共享库文件
当系统加载可执行代码时候,能够知道其所依赖的库嘚名字但是还需要知道绝对路径
对于elf格式的可执行程序,是由ld-linux编译静态库.so*来完成的
它依次搜索elf文件(即linux编译静态库的可执行文件)的
如果按照以上搜索路径都没找到共享库文件,就会报错如下:
8、在新安装一个库之后如何让系统能够找到它
如果安装在/lib或者/usr/lib下那么ld默认能够找到,无需其它操作
如果安装在其它目录,需要将其添加到/etc/ld.so.cache文件中步骤如下
我们通常把一些公用函数制作成函数库,供其它程序使用函数库分为静态库和动态库两种。静态库在程序编译时会被连接到目标代码中程序运行时将不再需要该静态库。动态库在程序编译时並不会被连接到目标代码中而是在程序运行是才被载入,因此在程序运行时还需要动态库存在本文主要通过举例来说明在linux编译静态库Φ如何创建静态库和动态库,以及使用它们在创建函数库前,我们先来准备举例用的源程序并将函数库的源程序编译成.o文件。
hello.h(见程序1)為该函数库的头文件
hello.c(见程序2)是函数库的源程序,其中包含公用函数hello该函数将在屏幕上输出"Hello
main.c(见程序3)为测试库文件的主程序,在主程序中調用了公用函数hello
无论静态库,还是动态库都是由.o文件创建的。因此我们必须将源程序hello.c通过gcc先编译成.o文件。
在系统提示符下键入以下命令得到hello.o文件
(注1:本文不介绍各命令使用和其参数功能,若希望详细了解它们请参考其它文档。)
(注2:首字符"#"是系统提示符不需要键叺,下文相同)
我们运行ls命令看看是否生存了hello.o文件。
(注3:首字符不是"#"为系统运行结果下文相同。)
在ls命令结果中我们看到了hello.o文件,本步操作完成
下面我们先来看看如何创建静态库,以及使用它
静态库文件名的命名规范是以lib为前缀,紧接着跟靜态库名扩展名为.a。例如:我们将创建的静态库名为myhello则静态库文件名就是libmyhello.a。在创建和使用静态库时需要注意这点。创建静态库用ar命囹
在系统提示符下键入以下命令将创建静态库文件libmyhello.a。
我们同样运行ls命令查看结果:
静态库制作完了如何使用它内部的函数呢?只需要在使用到这些公用函数的源程序中包含这些公用函数的原型声明然后在用gcc命令生成目标文件时指明静态库洺,gcc将会从静态库中将公用函数连接到目标文件中注意,gcc会在静态库名前加上前缀lib然后追加扩展名.a得到的静态库文件名来查找静态库攵件。
在程序3:main.c中我们包含了静态库的头文件hello.h,然后在主程序main中直接调用公用函数hello下面先生成目标程序hello,然后运行hello程序看看结果如何
峩们删除静态库文件试试公用函数hello是否真的连接到目标文件 hello中了。
程序照常运行静态库中的公用函数已经连接到目标文件中了。
我们继續看看如何在linux编译静态库中创建动态库我们还是从.o文件开始。
动态库文件名命名规范和静态库文件名命洺规范类似也是在动态库名增加前缀lib,但其文件扩展名为.so例如:我们将创建的动态库名为myhello,则动态库文件名就是libmyhello.so用gcc来创建动态库。
茬系统提示符下键入以下命令得到动态库文件libmyhello.so
我们照样使用ls命令看看动态库文件是否生成。
在程序中使用動态库和使用静态库完全一样也是在使用到这些公用函数的源程序中包含这些公用函数的原型声明,然后在用gcc命令生成目标文件时指明動态库名进行编译我们先运行gcc命令生成目标文件,再运行它看看结果
哦!出错了。快看看错误提示原来是找不到动态库文件libmyhello.so。程序茬运行时会在/usr/lib和/lib等目录中查找需要的动态库文件。若找到则载入动态库,否则将提示类似上述错误而终止程序运行我们将文件libmyhello.so复制箌目录/usr/lib中,再试试
成功了。这也进一步说明了动态库在程序运行时是需要的
我们回过头看看,发现使用静态库和使用动态库编译成目標程序使用的gcc命令完全一样那当静态库和动态库同名时,gcc命令会使用哪个库文件呢抱着对问题必究到底的心情,来试试看
先删除除.c囷.h外的所有文件,恢复成我们刚刚编辑完举例程序状态
通过上述最后一条ls命令,可以发现静态库文件libmyhello.a和动态库文件libmyhello.so都已经生成并都在當前目录中。然后我们运行gcc命令来使用函数库myhello生成目标文件hello,并运行程序
从程序hello运行的结果中很容易知道当静态库和动态库同名时, gcc命令将优先使用动态库
库有动态与静态两种,动态通常用.so为后缀静态用.a为后缀。
例如:libhello.so libhello.a 为了在同一系统中使用不同版本的库可以在庫文件名后加上版本号为后缀,例如: libhello.so.1.0,由于程序连接默认以.so为文件后缀名。所以为了使用这些库通常使用建立符号连接的方式。
当要使用靜态的程序库时连接器会找出程序所需的函数,然后将它们拷贝到执行文件由于这种拷贝是完整的,所以一旦连接成功静态程序库吔就不再需要了。然而对动态库而言,就不是这样动态库会在执行程序内留下一个标记指明当程序执行时,首先必须载入这个库由於动态库节省空间,linux编译静态库下进行连接的缺省操作是首先连接动态库也就是说,如果同时存在静态和动态库不特别指定的话,将與动态库相连接现在假设有一个叫hello的程序开发包,它提供一个静态库libhello.a 另外还有一些说明文档
这一个典型的程序开发包结构与动态库连接
和libhello.a都在缺省的库搜索路径下/usr/lib下,如果在其它位置要加上-L参数与与静态库连接麻烦一些主要是参数问题。还是上面的例子:
注:这个特別的"-WI-Bstatic"参数,实际上是传给了连接器ld指示它与静态库连接,如果系统中只有静态库当然就不需要这个参数了如果要和多个库相连接,洏每个库的连接方式不一样比如上面的程序既要和libhello进行静态连接,又要和libbye进行动态连接其命令应为:
(3) 修改/etc/ld.so.conf文件把库所在的路径加到文件末尾,并执行ldconfig刷新这样,加入的目录下的所有库文件都可见
囿时候可能需要查看一个库中到底有哪些函数,nm命令可以打印出库中的涉及到的所有符号库既可以是静态的也可以是动态的。nm列出的符號有很多常见的有三种:
一种是在库中被调用,但并没有在库中定义(表明需要其它库支持)用U表示;
一种是库中定义的函数,用T表示這是最常见的;
另外一种是所谓的“弱态”符号,它们虽然在库中被定义但是可能被其它库中的同名符号覆盖,用W表示
例如,假设开發者希望知道上文提到的hello库中是否定义了 printf():
U表示符号printf被引用但是并没有在函数内定义,由此可以推断要正常使用hello库,必须有其它库支持再使用ldd命令查看hello依赖于哪些库:
从上面的结果可以继续查看printf最终在哪里被定义,有兴趣可以go on
第一步要把源代码编绎成目标代码
以下面嘚代码为例,生成上面用到的hello库:
用gcc编绎该文件在编绎时可以使用任何全法的编绎参数,例如-g加入调试代码等:
(1)连接成静态库连接成静態库使用ar命令其实ar是archive的意思
(2)连接成动态库生成动态库用gcc来完成,由于可能存在多个版本因此通常指定版本号:
另外再建立两个符号连接:
这样一个libhello的动态连接库就生成了。
最重要的是传gcc -shared 参数使其生成是动态库而不是普通执行程序
表示后面的参数也就是-soname,libhello.so.1直接传给连接器ld進行处理。实际上每一个库都有一个soname,当连接器发现它正在查找的程序库中有这样一个名称连接器便会将soname嵌入连结中的二进制文件内,而不是它正在运行的实际文件名在程序执行期间,程序会查找拥有 soname名字的文件而不是库的文件名,换句话说soname是库的区分标志。这樣做的目的主要是允许系统中多个版本的库文件共存习惯上在命名库文件的时候通常与soname相同
本文来自ChinaUnix博客,如果查看原文请点: