Linux下如何用GCC编译动态库
2010年07月21日
1 为什么要使用库?
2 库的分类
3 创建自己的库
或许大家对自己初学 Linux时的情形仍记忆尤新吧。如果没有一个能较好的解决依赖关系的包管理器,在Linux下安装软件将是一件及其痛苦的工作。你装a包时,可能会提示你要先装b包,当你费尽心力找到b包时,可能又会提示你要先安装c包。我就曾被这样的事搞的焦头烂额,至今一提起rpm仍心有余悸,头皮发麻。说是一朝被蛇咬,十年怕井绳怕也不为过。
Linux下之所以有这许多的依赖关系,其中一个开发原则真是功不可没。这个原则就是:尽量不重复做别人已经做过的事。换句话说就是尽量充分利用别人的劳动成果。
这就涉及到如何有效的进行代码复用。
1 为什么要使用库?
关于代码复用的途径,一般有两种。
粘贴复制
这是最没有技术含量的一种方案。如果代码小,则工作量还可以忍受,如果代码很庞大,则此法不可取。即便有人原意这样做,但谁又能保证所有的代码都可得到呢?
而库的出现很好的解决了这个问题。
库,是一种封装机制,简单说把所有的源代码编译成目标代码后打成的包。
那么用户怎么能知道这个库提供什么样的接口呢?难道要用nm等工具逐个扫描?
不用担心,库的开发者早以把一切都做好了。除了包含目标代码的库外,www.Linuxidc.com一般还会提供一系列的头文件,头文件中就包含了库的接口。为了让方便用户,再加上一个使用说明就差不多完美了。
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的输出:
[leo@leo test]$ ldd a.out
Linux-gate.so.1 => (0xffffe000)
libhello.so => not found
libc.so.6 => /lib/libc.so.6 (0x40034000)
/lib/ld-Linux.so.2 (0x40000000)
果然如此,看到没有,libhello.so => not found。
Linux为我们提供了两种解决方法:
1.可以把当前路径加入 /etc/ld.so.conf中然后运行ldconfig,或者以当前路径为参数运行ldconfig(要有root权限才行)。
2.把当前路径加入环境变量LD_LIBRARY_PATH中
当然,如果你觉得不会引起混乱的话,可以直接把该库拷入/lib,/usr/lib/等位置(无可避免,这样做也要有权限),这样链接器和加载器就都可以准确的找到该库了。
我们采用第二种方法:
[leo@leo test]$ export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
[leo@leo test]$ ldd a.out
Linux-gate.so.1 => (0xffffe000)
libhello.so => ./libhello.so (0x4001f000)
libc.so.6 => /lib/libc.so.6 (0x40036000)
/lib/ld-Linux.so.2 (0x40000000)
哈哈,这下ld-Linux.so.2就可以找到libhello.so这个库了。
现在可以直接运行了:
[leo@leo test]$ ./a.out
Hello World
3.2 创建静态库
仍使用刚才的hello.c和test.c。
第一步,生成目标文件。
[leo@leo test]$ gcc -c hello.c
[leo@leo test]$ ls hello.o -l
-rw-r--r-- 1 leo users 840 5月 6 12:48 hello.o
第二步,把目标文件归档。
[leo@leo test]$ ar r libhello.a hello.o
ar: creating libhello.a
OK,libhello.a就是我们所创建的静态库了,简单吧:)
[leo@leo test]$ file libhello.a
libhello.a: current ar archive
下面一行命令就是教你如何在程序中链接静态库的:
[leo@leo test]$ gcc test.c -lhello -L. -static -o hello.static
我们来用file命令比较一下用动态库和静态库链接的程序的区别:
[leo@leo test]$ gcc test.c -lhello -L. -o hello.dynamic
正如前面所说,链接器默认会链接动态库(这里是libhello.so),所以只要把上个命令中的 -static参数去掉就可以了。
用file实用程序验证一下是否按我们的要求生成了可执行文件:
[leo@leo test]$ file hello.static hello.dynamic
hello.static: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.6.6, statically linked, not stripped
hello.dynamic: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.6.6, dynamically linked (uses shared libs), not stripped
不妨顺便练习一下ldd的用法:
[leo@leo test]$ ldd hello.static hello.dynamic
hello.static:
not a dynamic executable
hello.dynamic:
Linux-gate.so.1 => (0xffffe000)
libhello.so => ./libhello.so (0x4001f000)
libc.so.6 => /lib/libc.so.6 (0x40034000)
/lib/ld-Linux.so.2 (0x40000000)
OK,看来没有问题,那就比较一下大小先:
[leo@leo test]$ ls -l hello.[ds]*
-rwxr-xr-x 1 leo users 5911 5月 6 12:54 hello.dynamic
-rwxr-xr-x 1 leo users 628182 5月 6 12:54 hello.static
看到区别了吧,链接静态库的目标程序和链接动态库的程序比起来简直就是一个庞然大物!
这么小的程序,很难看出执行时间的差别,不过为了完整起见,还是看一下time的输出吧:
[leo@leo test]$ time ./hello.static
Hello World
real 0m0.001s
user 0m0.000s
sys 0m0.001s
[leo@leo test]$ time ./hello.dynamic
Hello World
real 0m0.001s
user 0m0.000s
sys 0m0.001s
如果程序比较大的话,应该效果会很明显的。
发表评论
-
windows下的android删除模拟器中的apk文件
2012-01-20 01:50 812windows下的android删除模拟器中的apk文件 2 ... -
Windows Mobile SDK安装失败解决方法
2012-01-20 01:50 1060Windows Mobile SDK安装失败解决方法 201 ... -
Bitmap简单操作-平移旋转缩放(VC++/Windows SDK)
2012-01-20 01:50 3057Bitmap简单操作-平移旋转缩放(VC++/Windows ... -
Windows下离线安装Android SDK的简单方法
2012-01-20 01:49 1339Windows下离线安装Android SDK的简单方法 2 ... -
Android源代码下载与编译
2012-01-20 01:49 778Android源代码下载与编译 2011年03月17日 ... -
小学生期末评语经典大全
2012-01-19 09:36 541小学生期末评语经典大全 2011年12月15日 1.你是 ... -
激发休眠的想象空间,引领小学生快乐作文
2012-01-19 09:36 603激发休眠的想象空间,引领小学生快乐作文 2011年05月21 ... -
改革作文评估方法,解放小学语文教师
2012-01-19 09:36 585改革作文评估方法,解放小学语文教师 2010年07月13日 ... -
小学生作文教学生活化
2012-01-19 09:36 560小学生作文教学生活化 2010年07月22日 小学 ... -
小学作文教学中的四个“重视”
2012-01-19 09:36 646小学作文教学中的四个 ... -
Java HotSpot性能引擎的体系结构
2012-01-17 02:11 694Java HotSpot性能引擎的体 ... -
Google Chromium开发文档-初级入门指南
2012-01-17 02:11 1058Google Chromium开发文档-初级入门指南 201 ... -
VC编译器过时与否?
2012-01-17 02:11 977VC编译器过时与否? 2011 ... -
Qt 学习笔记 --Qt SDK 的下载安装与配置
2012-01-17 02:11 1445Qt 学习笔记 --Qt SDK 的下 ... -
liunx编译android源码参考
2012-01-17 02:11 582liunx编译android源码参考 2011年10月20日 ... -
Linux系统管理员手册
2012-01-15 21:45 600Linux系统管理员手册 201 ... -
linux书籍推荐
2012-01-15 21:45 735linux书籍推荐 2009年11月23日 linux书 ... -
第4集 C++的异常处理和面向对象的紧密关系(转)
2012-01-15 21:45 663第4集 C++的异常处理和面向对象的紧密关系(转) 2010 ... -
c3链表队列二叉树
2012-01-15 21:45 700c3链表队列二叉树 2010年12月23日 结构和其他数 ...
相关推荐
Linux下如何用GCC编译动态库.docx
默认编译是静态库,但考虑到 linux 上动态库使用较多,所以使用 -DBUILD_SHARED_LIBS=ON 参数编译为动态库。 在 centos 7 下使用 gcc 4.8.5 + cmake 3.16.9 编译,包含 bin, include, lib, lib64, share 五个目录,...
Linux 如何使用gcc生成静态库和动态库,使用GCC编译生成静态库和动态库的方法
Linux系统下使用gcc 5.3编译器编译的boost库1.68版本的动态库和静态库,多线程参数编译,经测试可用
在使用GCC编译程序时,只需加上-shared选项即可,这样生成的执行程序即为动态链接库。 其中-fPIC选项的作用是:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的, 所以动态载入时是通过代码拷贝...
GCC编译动态和静态链接库.pdf 学习资料 复习资料 教学资源
网上大多都是Windows下的QT MQTT编译安装方法,Linux下有些不同,此文件是关于UBuntu和ARM Linux下QT MQTT库的编译安装详细步骤 -------------------------------------------------------- Linux版本:UBuntu 16.04...
下面就分别总结下linux下生成并使用静态库与动态库的方法:(由于是C++项目,所以编译器用的g++,但是与gcc的使用是相通的) 首先是准备工作,把我们需要封装成库文件的函数的头文件与源文件写好,如下: //myAPI.h...
一、Linux 平台 gcc 和动态共享库的基础知识 1)GNU gcc 的编译工具用法 2)动态共享库怎么使用 3)关于 Linux 的动态共享库的设置 二、GCC——C 程序是如何编译成的 三、GCC 使用详解
arm版本的openssl交叉编译文件
我们通常把一些公用函数制作成函数库,供其它程序使用。函数库分为静态库和动态库两种。...动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在。
默认编译是静态库,但考虑到 linux 上动态库使用较多,所以使用 -DBUILD_SHARED_LIBS=ON 参数编译为动态库。 在 ubuntu 下使用 gcc 4.8.5 + cmake 3.28 编译,包含 bin, include, lib, lib64, share 五个目录,可以...
介绍常用GCC常用编译选项,让你轻松在linux环境下开发程序.
交叉工具链arm-linux-gcc 4.5.1,以及制造根文件系统时需要的动态链接库也在该工具的目录下。
linux下编译/交叉编译openssl-1.1.1g生成动态库和静态库方法: 一、编译linux版本 1. cp openssl-1.1.1g.tar.gz /opt/ 2. tar -zxvf openssl-1.1.1g.tar.gz 3. mkdir build 4. ./config --prefix=/opt/openssl-1.1.1...
叉编译系统ZLTCG的开发为背景,详细分析了交叉编译系统GCC的体系结构,对其高度可移植性所依赖的中间表示 和机器描述技术进行了深入研究,并在此基础上阐述了如何基于GCC构造用于嵌入式系统开发的交又编译器。介绍了...
我的工作最近需要用到linux下构建多目录下Makefile产生so动态库样例的知识,我将最新的学习心得,做一个记录分享,以便以后需要使用时可以做参考。
ubuntu12.04 32位下gcc4.6.3编译glibc-2.15版本静态库文件,包内一个源码,一个编译后
2. 支持vs2017 & linux环境下gcc编译(linuxx使用的gcc版本为4.8.5,系统为centos7.8)。 3. faad2库使用的2_9_1版本,源码放在depend目录下,打开depends\faad\faad2.sln可直接使用vs2017编译库。 4. lib目录包含了...
根据链接时期的不同,库又有静态库和动态库之分。 静态库是在链接阶段被链接的(好像是废话,但事实就是这样),所以生成的可执行文件就不受库的影响了,即使库被删除了,程序依然可以成功运行。 有别于静态库,动态...