- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章Linux下如何用GCC编译动态库由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
本文主要解决以下几个问题 。
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 。
如果程序比较大的话,应该效果会很明显的.
最后此篇关于Linux下如何用GCC编译动态库的文章就讲到这里了,如果你想了解更多关于Linux下如何用GCC编译动态库的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的,
Linux 管道可以缓冲多少数据?这是可配置的吗? 如果管道的两端在同一个进程中,但线程不同,这会有什么不同吗? 请注意:这个“同一个进程,两个线程”的问题是理论上的边栏,真正的问题是关于缓冲的。 最
我找到了here [最后一页] 一种有趣的通过 Linux 启动 Linux 的方法。不幸的是,它只是被提及,我在网上找不到任何有用的链接。那么有人听说过一种避免引导加载程序而使用 Linux 的方法
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
我试图了解 ld-linux.so 如何在 Linux 上解析对版本化符号的引用。我有以下文件: 测试.c: void f(); int main() { f(); } a.c 和 b.c:
与 RetroPie 的工作原理类似,我可以使用 Linux 应用程序作为我的桌面环境吗?我实际上并不需要像实际桌面和安装应用程序这样的东西。我只需要一种干净简单的方法来在 RaspberryPi 上
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 这个问题似乎不是关于 a specific programming problem, a softwar
关闭。这个问题是off-topic .它目前不接受答案。 想改进这个问题吗? Update the question所以它是on-topic用于堆栈溢出。 关闭 10 年前。 Improve thi
有什么方法可以覆盖现有的源代码,我应该用 PyQt、PyGTK、Java 等从头开始构建吗? 最佳答案 如果您指的是软件本身而不是它所连接的存储库,那么自定义应用程序的方法就是 fork 项目。据我所
我的情况是:我在一个磁盘上安装了两个 linux。我将第一个安装在/dev/sda1 中,然后在/dev/sda2 中安装第二个然后我运行第一个系统,我写了一个脚本来在第一个系统运行时更新它。
我在 i2c-0 总线上使用地址为 0x3f 的系统监视器设备。该设备在设备树中配置有 pmbus 驱动程序。 问题是,加载 linux 内核时,这个“Sysmon”设备没有供电。因此,当我在总线 0
关闭。这个问题是off-topic .它目前不接受答案。 想改进这个问题吗? Update the question所以它是on-topic用于堆栈溢出。 关闭 11 年前。 Improve thi
我正试图在 linux 模块中分配一大块内存,而 kalloc 做不到。 我知道唯一的方法是使用 alloc_bootmem(unsigned long size) 但我只能从 linux 内核而不是
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 这个问题似乎不是关于 a specific programming problem, a softwar
我有 .sh 文件来运行应用程序。在该文件中,我想动态设置服务器名称,而不是每次都配置。 我尝试了以下方法,它在 CentOS 中运行良好。 nohup /voip/java/jdk1.8.0_71/
我是在 Linux 上开发嵌入式 C++ 程序的新手。我有我的 Debian 操作系统,我在其中开发和编译了我的 C++ 项目(一个简单的控制台进程)。 我想将我的应用程序放到另一个 Debian 操
关闭。这个问题需要多问focused 。目前不接受答案。 想要改进此问题吗?更新问题,使其仅关注一个问题 editing this post . 已关闭 4 年前。 Improve this ques
我使用4.19.78版本的稳定内核,我想找到带有企鹅二进制数据的C数组。系统启动时显示。我需要在哪里搜索该内容? 我在 include/linux/linux_logo.h 文件中只找到了一些 Log
我知道可以使用 gdb 的服务器模式远程调试代码,我知道可以调试针对另一种架构交叉编译的代码,但是是否可以更进一步,从远程调试 Linux 应用程序OS X 使用 gdbserver? 最佳答案 当然
是否有任何可能的方法来运行在另一个 Linux 上编译的二进制文件?我知道当然最简单的是在另一台机器上重建它,但假设我们唯一能得到的是一个二进制文件,那么这可能与否? (我知道这可能并不容易,但我只是
我是一名优秀的程序员,十分优秀!