Unix/Linux环境下创建和使用静/动态库
来源:互联网 发布:阿里云学生优惠停止了 编辑:程序博客网 时间:2024/06/04 17:50
库的作用
大体上库的存在,有两方面的原因,一是代码的复用,二是声明和实现的分离。将功能相近的使用模块封装成库,使代码的复用、管理和分发变得简单了许多,例如著名的开源图形库ncurses,你可以自行编译,更可以直接使用已经编译好的现成的库文件。另外,由于库是二进制文件,某种意义上讲,将功能的实现部分隐藏了起来,这就为商业代码的保护提供了一种方式。
库文件按照链接方式和时机,可以分为动态库和静态库,下面分别介绍它们在Linux环境中的创建和使用方法。
静态链接库
静态库是指在程序的链接阶段,其中被用到的代码会被直接链接到可执行文件中的库。静态链接的可执行程序包含了其所需的全部库函数:所有库函数都连接到程序中。 这类程序是完整的,其运行不需要外部库的支持。 静态链接程序的优点之一是其安装之前不需要做环境准备工作 。
Linux中,静态库的扩展名通常为.a,它仅仅是一些目标文件(.o)的归档(archive)或者说打包,另外,为了链接时能够快速地定位其中的符号(函数、变量名等),静态库还会包含一个对其中符号的索引。
创建静态库的过程十分简单,除编译所必须的工具之外,要用到的命令只有两个ar和ranlib。ar可以将各个目标文件进行归档,ranlib对ar生成的归档文件(即静态库文件)进行索引。假设现在有这样几个源文件:plus.c, sub.c及相应的头文件,另外还有一个用来调用库文件中函数的主文件main.c,它们的内容分别是:
plus.c,
sub.c,
main.c,
下面将plus.c和sub.c编译,制作成静态库libmath.a,依次执行:
$ cc -c plus.c sub.c
$ ar rc libmath.a plus.o sub.o
$ ranlib libmath.a
现在我们的libmath.a静态库已经制作完成,其中使用了ar命令的两个选项,c表示若库文件不存在则创建之,
r表示库文件中若已经存在某个目标文件,且较旧,则执行替换。使用这个静态库更加简单:
$ cc main.c -L. -lmath -o main
$ ./main
3
动态链接库
动态库和静态库相似,也是各个目标文件的集合。但相比静态链接的程序,动态链接可执行程序要小得多:这类程序运行时需要外部共享 函数库的支持,因此好像并不完整。除了程序体小之外,动态链接允许程序包指定必须的库,而不必将库装入程序包内。动态链接技术还允许多个运行中的程序共享一个库,这样就不会出现同一代码的多份拷贝共占内存的情况了。由于这些原因,当前多数程序采用动态链接技术。
在Linux中的扩展名通常为.so。但在链接时,并不会被链接到可执行文件中,而是在执行时(需要时)由操作系统的动态加载模块动态地加载到内存,并链接到可执行文件地址空间的相应位置。
动态链接库的创建也分为编译和”归档”两个阶段,但不同的是在这两个阶段需要使用一些不同的命令选项。首先,需要将源文件编译成一种成为位置无关码(PIC: Position Independent Code)的目标文件,这种代码可以被加入到内存的任何位置却不需要加载器对其进行重定位,关于这种格式可以参考《链接器与加载器》和《程序员的自我修养–链接装载与库》中较为详尽的描述。接下来需要将这些位置无关码“归档”为.so文件。整个过程只需一个工具即可,即gcc。还是上面的源文件,执行以下命令:
$ cc -c -fpic plus.c sub.c
$ cc -shared -o libmath.so *.o
这样,包含plus.o和sub.o的动态库文件libmath.so就创建完成了。其中cc(gcc的符号链接)命令的-fpic或-fPIC选项使之创建位置
无关的目标文件,使用-shared选项可以创建最终的动态库文件。使用动态库文件有两种方法,一种是让操作系统的动态加载模块
(如ld-linux.so.2)在执行时加载动态库。另一种是在代码中使用dl库动态加载库文件。
先介绍下第一种方法。使用这种方法需要在编译可执行文件时指明库名及其路径(对于自己的编写的动态库而言):
$ cc main.c -L. -lmath -o main
这时可执行文件main就被创建了,上面的命令并没有将libmath.so中相应的目标代码链接到main中(你可以对比一下这里的main和静态链接的main的大小),只是在库中查找和确认main.c中用到的符号而已。但通常这个main现在还无法正常执行,这涉及到系统查找动态库的路径问题,系统通常只在某些指定的目录(标准路径)下查找所需的库文件,若在标准路径中无法找到所需的库,则会到环境变量LD_LIBRARY_PATH(如果存在的话)指定的目录下查找,若仍无法找到就会报错。因为libmath.so在main的当前目录中,而当前目录通常并不在标准路径之列。为了使libmath.so能够被找到和加载,你可以把它放到标准路径中,但更好的方法是将其所在目录加入到LD_LIBRARY_PATH变量中。执行下面的命令:
$ LD_LIBRARY_PATH=$LD_LIBRARY_PATH #添加当前目录
$ export LD_LIBRARY_PATH # 将环境变量导出,使其在子shell中可用
$ ./main
3
下面介绍使用dl库加载动态库,dl库中函数很少很简练,看main.c代码:
main.c中,首先使用dlopen打开需要的动态库,其中参数RTLD_LAZY指明仅当需要调用该库时才进行加载。dlopen返回一个
句柄,dlsym使用该句柄和符号来取得相应函数的地址,这里使用int (*)(int,
int)函数指针来接收plus函数的地址。接下来使用得到的函数指针调用相应的函数,最后通过dlclose函数来关闭句柄。编译这个程序需要使用dl
动态链接库,因此需要使用gcc的-ldl选项:
$ cc main.c -ldl -o main
$ ./main
3
.so与.a文件的对比
最后附上libmath.a和libmath.so文件的内容,使用objdump的-d选项查看的:
libmath.a,
In archive libmath.a:
plus.o: file format elf32-i386
Disassembly of section .text:
00000000 <plus>:
0:55 push %ebp
1:89 e5 mov %esp,%ebp
3:8b 45 0c mov 0xc(%ebp),%eax
6:8b 55 08 mov 0x8(%ebp),%edx
9:8d 04 02 lea (%edx,%eax,1),%eax
c:5d pop %ebp
d:c3 ret
sub.o: file format elf32-i386
Disassembly of section .text:
00000000 <sub>:
0:55 push %ebp
1:89 e5 mov %esp,%ebp
3:8b 45 0c mov 0xc(%ebp),%eax
6:8b 55 08 mov 0x8(%ebp),%edx
9:89 d1 mov %edx,%ecx
b:29 c1 sub %eax,%ecx
d:89 c8 mov %ecx,%eax
f:5d pop %ebp
10:c3 ret
libmath.so:
libmath.so: file format elf32-i386
Disassembly of section .init:
00000324 <_init>:
324:55 push %ebp
325:89 e5 mov %esp,%ebp
327:53 push %ebx
328:83 ec 04 sub $0x4,%esp
......
0000044c <plus>:
44c:55 push %ebp
44d:89 e5 mov %esp,%ebp
44f:8b 45 0c mov 0xc(%ebp),%eax
452:8b 55 08 mov 0x8(%ebp),%edx
455:8d 04 02 lea (%edx,%eax,1),%eax
458:5d pop %ebp
459:c3 ret
45a:90 nop
45b:90 nop
0000045c <sub>:
45c:55 push %ebp
45d:89 e5 mov %esp,%ebp
45f:8b 45 0c mov 0xc(%ebp),%eax
462:8b 55 08 mov 0x8(%ebp),%edx
465:89 d1 mov %edx,%ecx
467:29 c1 sub %eax,%ecx
469:89 c8 mov %ecx,%eax
46b:5d pop %ebp
46c:c3 ret
46d:90 nop
46e:90 nop
46f:90 nop
......
Disassembly of section .fini:
000004a8 <_fini>:
4a8:55 push %ebp
4a9:89 e5 mov %esp,%ebp
4ab:53 push %ebx
4ac:83 ec 04 sub $0x4,%esp
4af:e8 00 00 00 00 call 4b4 <_fini+0xc>
4b4:5b pop %ebx
4b5:81 c3 40 1b 00 00 add $0x1b40,%ebx
4bb:e8 d0 fe ff ff call 390 <__do_global_dtors_aux>
4c0:59 pop %ecx
4c1:5b pop %ebx
4c2:c9 leave
4c3:c3 ret
可见,相对.a,.so有很多额外的代码段,其中比较重要的是<_init>段和<_fini>段。它们分别进行一些前期和后期处理工作,例如<_init>通常在dlopen返回之前执行一些.so库中的一些初始化工作(C++中就可能是全局构造或者静态对象的构造函数)。
- Unix/Linux环境下创建和使用静/动态库
- Unix/Linux环境下创建和使用静/动态库
- Linux环境下c语言静态链接库和动态链接库创建和使用
- Linux环境下c语言静态链接库和动态链接库创建和使用
- Linux环境下c语言静态链接库和动态链接库创建和使用
- Linux环境下创建并使用动态链接库
- Linux环境下创建静态库和动态库
- Windows和Linux下动态库的创建和使用
- [转载] linux下创建和使用静态和动态库
- Linux下创建和使用静态和动态库
- 编译链接------Linux环境下c语言静态链接库和动态链接库创建和使用
- 编译链接------Linux环境下c语言静态链接库和动态链接库创建和使用
- Linux下静态库、动态库的创建和使用
- Linux下静态库、动态库的创建和使用
- Libtool创建和使用linux下动态库
- Linux下动态链接库的创建和使用
- Libtool创建和使用linux下动态库
- linux下动态/静态库的创建和使用
- [js]学术搜索的背景更换
- [Linux][2010-12-31] STL list 使用
- MTK623510A 资源添加的
- 测试Sphinx
- C# 读写操作Excel
- Unix/Linux环境下创建和使用静/动态库
- Liferay学习笔记(一)——Liferay Portal5.2.3环境的初步搭建
- sinablog新浪博客API错误代码说明曝光!
- VIM使用总结
- Java课程设计---碰撞的小球
- 用PHP实现网页内容保存为excel表格
- Windows XP远程桌面的技巧
- c#中out与ref的用法与区别
- 加了醋的啤酒