linux库文件

来源:互联网 发布:php程序员入门 编辑:程序博客网 时间:2024/05/17 23:18

Linux 的库文件
[日期:2012-04-10] 来源:Linux社区  作者:yangzhongxuan

 
1.什么是库

在windows平台和linux平台下都存在着大量的库。
 

本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。

由于windows和linux的本质不同,因此二者库的二进制是不兼容的。

本文仅限于介绍linux下的库。

 


2.库的种类及区别

•linux库有分类

静态库和共享库(动态库)

•区别:

后缀不同

通常共享库以.so(SharedObject的缩写)结尾,静态链接库通常以.a结尾(Archive的缩写)。在终端缺省情况下,共享库通常为绿色,而静态库为黑色。

 


代码被载入的时刻不同

静态库——编译时加载

动态库——语句调运时加载

 


静态库的代码在编译过程中已经被载入可执行程序,因此体积较大。

动态库,程序中只保留库文件的名字和函数名,在运行时去查找库文件和函数体并载入内存,程序的体积基本变化不大。

 


静态库的原则是“以空间换时间”,增加程序体积,减少运行时间;

动态库则是“以时间换空间”,增加运行时间,减少了程序本身的体积。

不同的应用程序如果调用相同的动态库,那么在内存里只需要有一份该动态库的实例。

 


3.库存在的意义

•便于管理。

你调用函数的时候,只是一个#include就搞定,操作系统就自己帮你去找你要用的东西,(前提是你的环境变量要正确,或者你的库在系统默认的地方),而且库函数是成熟稳定的。

 


•缩短开发周期。

你不用去编写所用的代码,你可以用牛人们已经用了千百遍的代码——标准库

这也是团队合作的必要。你有时候只需要了解函数的功能、入口、出口。

 


•有利于升级

如病毒库的升级,具体的我还得查查资料。哈哈见笑。

 

4.生成库文件

•概述

编写函数代码
 

编译生成各目标文件

用ar文件对目标文件归档,生成静态库文件。注意归档文件名必须以lib打头(可查看库文件命名规则)。

 


使用要点:

在gcc的-I参数后加上静态库头文件的路径。

在gcc的-L参数后加上库文件所在目录

在gcc的-l参数后加上库文件名,但是要去掉lib和.a扩展名。

比如库文件名是libtest.a那么参数就是-ltest

 


•编写最简单的源文件

编写如下两个文件,注意放在同一目录中

myalib.h //静态库头文件

myalib.c //静态库实现文件

1.//myalib.h 
2. 
3.voidtest(); 
4. 
5. 
6.//myalib.c 
7.#inlcude<stdio.h>  8. 
9.voidtest() 
10. 
11.{ 
12. 
13.printf("test\n"); 
14. 
15.} 
•制作静态库文件

生成目标文件

 

 

gcc-c myalib.c


执行完后会生成一个myalib.o文件

 


用ar命令归档,格式为ar-rc <生成的档案文件名><.o文件名列表>

再次提醒,归档文件名一定要以lib打头,.a结尾。


ar-rc libtest.a myalib.o

执行完后会生成一个libtest.a文件

 


•使用静态库文件

编写一个测试程序main.c,内容为

1.//main.c 
2. 
3. 
4.#include"myalib.h" 
5. 
6.intmain(int argc,char* argv[]) 
7. 
8.{ 
9. 
10.test(); 
11. 
12.return0; 
13. 
14.} 
编译目标文件,注意要把静态库头文件的路径加到-I参数里面

gcc-I ./ -o main.o -c main.c

现在生成了一个main.o文件

 


生成可执行文件,注意要把静态库文件的路径加到-L参数里面,

把库文件名(去掉打头的lib和结尾的.a)加到-l参数后面。如下面所示

 


gcc-o main -L ./ main.o -ltest

 


此时就会生成一个名为main的可执行文件

另外,注意-l参数好象应该加到输入文件名的后面,否则会报错。

 


比如gcc-o main -L./ -ltest main.o就会提示

main.o(.text+0x11):In function `main':

:undefined reference to `test'

collect2:ld returned 1 exit status

 


执行可执行文件查看效果

执行./main,输出

test

说明执行成功。

 


•生成动态库文件

gcc-fPIC --shared test.c -o libtest.so

生成libtest.so

 


•使用动态库文件

编译可执行文件

gccmain.c -o main -I./ -L./ -ltest

指定加载库的路径,后面会详细介绍。

exportLD_LIBRARY_PATH=./:$LD_LIBRARY_PATH

 


执行./main,输出

test

说明执行成功。

 


5.库文件命名

GNU库的使用必须遵守LibraryGNU PublicLicense(LGPL许可协议)。该协议与GNU许可协议略有不同,开发人员可以免费使用GNU库进行软件开发,但必须保证向用户提供所用的库函数的源代码。

系统中可用的库都存放在/usr/lib和/lib目录中。库文件名由前缀lib和库名以及后缀组成。根据库的类型不同,后缀名也不一样。共享库的后缀名由.so和版本号组成,静态库的后缀名为.a。

 


静态库的名字一般为libxxxx.a,其中xxxx是该lib的名称

动态库的名字一般为libxxxx.so.major.minor,xxxx是该lib的名称,major是主版本号,minor是副版本号

6.查看可执行文件的依赖库

ldd命令可以查看一个可执行程序依赖的共享库,

# ldd./main

       linux-gate.so.1=> (0x00ec1000)

       libtest.so=> ./libtest.so (0x00967000)
 

       libc.so.6=> /lib/tls/i686/cmov/libc.so.6 (0x0025b000)

       /lib/ld-linux.so.2(0x00471000)

7.可执行程序定位共享库

当系统加载可执行代码时候,能够知道其所依赖的库的名字,但是还需要知道绝对路径

此时就需要系统动态载入器(dynamiclinker/loader)对于elf格式的可执行程序,是由ld-linux.so*来完成的,它先后搜索

elf文件的DT_RPATH段,

LD_LIBRARY_PATH指定的路径,

/etc/ld.so.cache文件列表,

/lib/,/usr/lib,目录

找到库文件后将其载入内存。

8.指定库路径

如果安装在/lib或者/usr/lib下,那么ld默认能够找到。

方法1

如果安装在其他目录,需要将其添加到/etc/ld.so.cache文件中,步骤如下

编辑/etc/ld.so.conf文件,加入库文件所在目录的路径

运行ldconfig,该命令会重建/etc/ld.so.cache文件

方法2:(临时指定)

#export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/libpath

9.库的题外话

库文件在连接(静态库和共享库)和运行(仅限于使用共享库的程序)时被使用,其搜索路径是在系统中进行设置的。一般Linux系统把 /lib和 /usr/lib两个目录作为默认的库搜索路径,所以使用这两个目录中的库时不需要进行设置搜索路径即可直接使用。对于处于默认库搜索路径之外的库,需要将库的位置添加到库的搜索路径之中。设置库文件的搜索路径有下列两种方式:

在环境变量 LD_LIBRARY_PATH中指明库的搜索路径。

在 /etc/ld.so.conf文件中添加库的搜索路径。

将自己可能存放库文件的路径都加入到/etc/ld.so.conf

添加方法也极其简单,将库文件的绝对路径直接写进去就OK,一行一个。例如:

/usr/local/lib

/opt/lib

我的机是Ubuntu10.4系统/etc/ld.so.conf的内容如下:

#cat/etc/ld.so.conf

include/etc/ld.so.conf.d/*.conf

为了加快程序执行时对共享库的定位速度,避免使用搜索路径查找共享库的低效率,linux是直接读取库列表文件/etc/ld.so.cache从中进行搜索的。

/etc/ld.so.cache是一个非文本的数据文件,不能直接编辑,它是根据/etc/ld.so.conf中设置的搜索路径由/sbin/ldconfig命令将这些搜索路径下的共享库文件集中在一起而生成的。

因此,为了保证程序执行时对库的定位,在/etc/ld.so.conf中进行了库搜索路径的设置之后,还必须要运行/sbin/ldconfig命令更新/etc/ld.so.cache 文件之后才可以。

ldconfig,简单的说,它的作用就是将/etc/ld.so.conf列出的路径下的库文件缓存到/etc/ld.so.cache以供使用。因此当安装完一些库文件,(例如刚安装好glib),或者修改ld.so.conf增加新的库路径后,需要运行一下/sbin/ldconfig使所有的库文件都被缓存到ld.so.cache中,如果没做,即使库文件明明就在/usr/lib下的,也是不会被使用的,结果编译过程中抱错,缺少xxx库。

在程序连接时(不是运行),对于库文件(静态库和共享库)的搜索路径,除了上面的设置方式之外,还可以通过-L参数显式指定。因为用-L设置的路径将被优先搜索,所以在连接的时候通常都会以这种方式直接指定要连接的库的路径。

 

 

10.动态库的显式调用

库函数dlopen()将打开一个新库,并把它装入内存。该函数主要用来加载库中的符号,这些符号在编译的时候是不知道的。比如Apache Web服务器利用这个函数在运行过程中加载模块,这为它提供了额外的能力。一个配置文件控制了加载模块的过程。这种机制使得在系统中添加或者删除一个模块时,都不需要重新编译了。
 

dlopen()在dlfcn.h中定义,并在dl库中实现。它需要两个参数:一个文件名和一个标志。文件名可以是我们学习过的库名字。标志指明是否立刻计算库的依赖性。如果设置为RTLD_NOW的话,则立刻计算;如果设置的是RTLD_LAZY,则在需要的时候才计算。另外,可以指定RTLD_GLOBAL,它使得那些在以后才加载的库可以获得其中的符号。

当库被装入后,可以把 dlopen()返回的句柄作为给 dlsym()的第一个参数,以获得符号在库中的地址。使用这个地址,就可以获得库中特定函数的指针,并且调用装载库中的相应函数。

下面详细说明一下这些函数。

•dlerror

原型为:const char *dlerror(void);

当动态链接库操作函数执行失败时,dlerror可以返回出错信息,返回值为NULL时表示操作函数执行成功。

•dlopen

原型为:void *dlopen (const char *filename, int flag);

dlopen用于打开指定名字(filename)的动态链接库,并返回操作句柄。

filename:如果名字不以/开头,则非绝对路径名,将按下列先后顺序查找该文件。

(1)用户环境变量中的LD_LIBRARY值;

(2)动态链接缓冲文件/etc/ld.so.cache

(3)目录/lib,/usr/lib

flag表示在什么时候解决未定义的符号(调用)。取值有两个:

1)RTLD_LAZY :表明在动态链接库的函数代码执行时解决。

2)RTLD_NOW :表明在dlopen返回前就解决所有未定义的符号,一旦未解决,dlopen将返回错误。

dlopen调用失败时,将返回NULL值,否则返回的是操作句柄。

•dlsym

取函数执行地址

原型为:void *dlsym(void *handle, char *symbol);

dlsym根据动态链接库操作句柄(handle)与符号(symbol),返回符号对应的函数的执行代码地址。由此地址,可以带参数执行相应的函数。

如程序代码:void (*add)(int x,int y); /*说明一下要调用的动态函数add*/

add=dlsym("xxx.so","add");/*打开xxx.so共享库,取add函数地址*/

add(89,369);/*带两个参数89和369调用add函数*/

•dlclose:关闭动态链接库

原型为:int dlclose (void *handle);

dlclose用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0时,才会真正被系统卸载。

 

编写测试文件

1.#include<stdio.h> 
2. 
3.#include<dlfcn.h> //用于动态库管理的系统头文件  4. 
5.#include"test.h" //要把函数的头文件包含进来,否则编译时会报错 
6. 
7.intmain(int argc,char* argv[]) 
8. 
9.{ 
10. 
11.//声明对应的函数的函数指针 
12. 
13.void(*pTest)(); 
14. 
15.//加载动态库 
16. 
17.void*pdlHandle = dlopen("libtest.so", RTLD_LAZY);  18. 
19.//错误处理 
20. 
21.if(pdlHandle== NULL ) {  22. 
23.printf("Failedload library\n"); 
24. 
25.return-1; 
26. 
27.} 
28. 
29.char*pszErr = dlerror();  30. 
31.if(pszErr!= NULL) 
32. 
33.{ 
34. 
35.printf("%s\n",pszErr); 
36. 
37.return-1; 
38. 
39.} 
40. 
41.//获取函数的地址 
42. 
43.pTest= dlsym(pdlHandle, "test");  44. 
45.pszErr= dlerror();  46. 
47.if(pszErr!= NULL) 
48. 
49.{ 
50. 
51.printf("%s\n",pszErr); 
52. 
53.dlclose(pdlHandle); 
54. 
55.return-1; 
56. 
57.} 
58. 
59.//实现函数调用 
60. 
61.(*pTest)(); 
62. 
63. 
64.//程序结束时关闭动态库 
65. 
66.dlclose(pdlHandle); 
67. 
68.return0; 
69. 
70.}  
2、编译测试文件使用-ldl选项指明生成的对象模块需要使用共享库

gcc -omain -ldl main.c

执行完后就生成了一个main文件

 


3、执行测试程序

执行 ./main


本篇文章来源于 Linux公社网站(www.linuxidc.com)  原文链接:http://www.linuxidc.com/Linux/2012-04/58352p2.htm

 

原创粉丝点击