本文主要解决以下几个问题
1 为什么要使用库?
2 库的分类
3 创建自己的库
或许大家对自己初学linux时的情形仍记忆尤新吧。假如没有一个能较好的解决依赖关系的包管理器,在linux下安装软件将是一件及其痛苦的工作。您装a包时,可能会提示您要先装b包,当您费尽心力找到b包时,可能又会提示您要先安装c包。我就曾被这样的事搞的焦头烂额,至今一提起rpm仍心有余悸,头皮发麻。说是一朝被蛇咬,十年怕井绳怕也不为过。
文章整理:西部数码--专业提供域名注册、虚拟主机服务
linux下之所以有这许多的依赖关系,其中一个研发原则真是功不可没。这个原则就是:尽量不重复做别人已做过的事。换句话说就是尽量充分利用别人的劳动成果。
这就涉及到如何有效的进行代码复用。
1 为什么要使用库?
关于代码复用的途径,一般有两种。
粘贴复制
这是最没有技术含量的一种方案。假如代码小,则工作量还能够忍受,假如代码很庞大,则此法不可取。即便有人原意这样做,但谁又能确保任何的代码都可得到呢?
而库的出现很好的解决了这个问题。
库,是一种封装机制,简单说把任何的源代码编译成目标代码后打成的包。
那么用户怎么能知道这个库提供什么样的接口呢?难道要用nm等工具逐个扫描?
不用担心,库的研发者早以把一切都做好了。除了包含目标代码的库外,一般还会提供一系列的头文档,头文档中就包含了库的接口。为了让方便用户,再加上一个使用说明就差不多完美了。
2 库的分类
2.1 库的分类
根据链接时期的不同,库又有静态库和动态库之分。
静态库是在链接阶段被链接的(似乎是废话,但事实就是这样),所以生成的可执行文档就不受库的影响了,即使库被删除了,程式依然能够成功运行。
有别于静态库,动态库的链接是在程式执行的时候被链接的。所以,即使程式编译完,库仍须保留在系统上,以供程式运行时调用。(TODO:链接动态库时链接阶段到底做了什么)
2.2 静态库和动态库的比较
链接静态库其实从某种意义上来说也是一种粘贴复制,只但是他操作的对象是目标代码而不是源码而已。因为静态库被链接后库就直接嵌入可执行文档中了,这样就带来了两个问题。
首先就是系统空间被浪费了。这是显而易见的,想象一下,假如多个程式链接了同一个库,则每一个生成的可执行文档就都会有一个库的副本,必然会浪费系统空间。
再者,人非圣贤,即使是精心调试的库,也难免会有错。一旦发现了库中有bug,挽救起来就比较麻烦了。必须一一把链接该库的程式找出来,然后重新编译。
而动态库的出现正弥补了静态库的以上弊端。因为动态库是在程式运行时被链接的,所以磁盘上只须保留一份副本,因此节约了磁盘空间。假如发现了bug或要升级也很简单,只要用新的库把原来的替换掉就行了。
那么,是不是静态库就一无是处了呢?
答曰:非也非也。不是有句话么:存在即是合理。静态库既然没有湮没在滔滔的历史长河中,就必然有他的用武之地。想象一下这样的情况:假如您用libpcap库编了一个程式,要给被人运行,而他的系统上没有装pcap库,该怎么解决呢?最简单的办法就是编译该程式时把任何要链接的库都链接他们的静态库,这样,就能够在别人的系统上直接运行该程式了。
所谓有得必有失,正因为动态库在程式运行时被链接,故程式的运行速度和链接静态库的版本相比必然会打折扣。然而瑕不掩瑜,动态库的不足相对于他带来的好处在现今硬件下简直是微不足道的,所以链接程式在链接时一般是优先链接动态库的,除非用-static参数指定链接静态库。
2.3 如何判断一个程式有没有链接动态库?
答案是用file实用程式。
file程式是用来判断文档类型的,在file命令下,任何文档都会原形毕露的。
顺便说一个技巧。有时在windows下用浏览器下载tar.gz或tar.bz2文档,后缀名会变成奇怪的tar.tar,到linux有些新手就不知怎么解压了。但linux下的文档类型并不受文档后缀名的影响,所以我们能够先用命令file xxx.tar.tar看一下文档类型,然后用tar加适当的参数解压。
另外,还能够借助程式ldd实用程式来判断。
ldd是用来打印目标程式(由命令行参数指定)所链接的任何动态库的信息的,假如目标程式没有链接动态库,则打印“not a dynamic executable”,ldd的用法请参考manpage。
3 创建自己的库
3.1 创建动态库
创建文档hello.c,内容如下:
#include
void hello(void)
{
printf("Hello World\n");
}
用命令gcc -shared hello.c -o libhello.so编译为动态库。能够看到,当前目录下多了一个文档libhello.so。
[leo@leo test]$ file libhello.so
libhello.so: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), not stripped
看到了吧,文档类型是shared object了。
再编辑一个测试文档test.c,内容如下:
int
main()
{
hello();
return 0;
}
这下能够编译了:)
[leo@leo test]$ gcc test.c
/tmp/ccm7w6Mn.o: In function `main':
test.c:(.text 0x1d): undefined reference to `hello'
collect2: ld returned 1 exit status
链接时gcc很难找到hello函数,编译失败:(。原因是hello在我们自己创建的库中,假如gcc能找到那才教见鬼呢!ok,再接再厉。
[leo@leo test]$ gcc test.c -lhello
/usr/lib/gcc/i686-pc-linux-gnu/4.0.0/../../../../i686-pc-linux-gnu/bin/ld: cannot find -lhello
collect2: ld returned 1 exit status
[leo@leo test]$ gcc test.c -lhello -L.
[leo@leo test]$
第一次编译直接编译,gcc默认会链接标准c库,但符号名hello解析不出来,故连接阶段通但是了。
现在用gcc test.c -lhello -L.已编译成功了,默认输出为a.out。现在来试着运行一下:
[leo@leo test]$ ./a.out
./a.out: error while loading shared libraries: libhello.so: cannot open shared object file: No such file or directory
咦,怎么回事?原来虽然链接时链接器(dynamic linker)找到了动态库libhello.so,但动态加载器(dynamic loader, 一般是/lib/ld-linux.so.2)却没找到。再来看看ldd的输出:
http://www.west263.com
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!



