Linux — 浅析静态库和动态库

浅析静态库和动态库





我们编写的程序一般需要经过预处理,编译,汇编和链接这几个步骤才变成可执行的程序. 在实际的软件开发中,对于一些需要被许多模块反复使用的

共代码,我们将它们编译为库文件. 在windows平台和linux平台下都大量存在着库. 本质上来说库是一种可执行的二进制形式,可以被操作系统载入

执行. 由于windows和linux的本质不同,因此二者库的二进制是不兼容的. 所以呢,今天我们着重来看看Linux下的库.


库的种类


linux下的库有两种:静态库和共享库(动态库). 二者的不同点在于代码被载入的时刻不同:

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

共享库的代码是在可执行程序运行时才载入内存的,在编译过程中仅简单的引用,因此代码体积较小.


库的原理


我们的动态库和静态库都是链接的时候才会运行,回顾一下,将一个程序编译成可执行程序的步骤:



静态库在程序编译时会自动连接到目标代码中,程序运行时将不再需要该静态库. 不过呢,静态库在内存中存在多份拷贝会造成空间的浪费,例如

静态库占用1M内存,有2000个这样的程序,将占用近2GB的空间. 所以才会有动态库的出现. 静态库的特点有以下这几个部分:

1.静态库对函数库的链接是放在编译时期完成的.

2.程序在运行时与函数库再无瓜葛,移植方便.

3.浪费空间和资源,因为所有相关的目标文件与牵扯到的函数库被链接合成一个可执行文件.

4.是静态库对程序的更新,部署和发布页会带来麻烦.如果静态库liba.lib更新了,所以使用它的应用程序都需要重新编译,发布给用户.


接下来就是动态库啦,动态库就是静态库的一个升级版本,算是创新. 因为节约了空间.  动态库在程序编译时并不会被连接到目标代码中,而是程序

运行时才会被载入,因此在程序运行时还需要动态库的存在. 动态库就是一个对应的资源,所有要执行的程序来我这里拿,并不是我把我所有的拷贝




动态库的总结:

1.动态库把对一些库函数的链接载入推迟到程序运行的时期.

2.可以实现进程之间的资源共享 (动态库也叫共享库)

3.将一些程序升级变得简单

4.甚至可以真正做到链接载入完全由程序员在程序代码中控制(显示调用)


所以我们现在可以看到,静态库的优点是编译后的执行程序不需要外部的函数库支持,缺点是如果静态函数库改变了,那么你的程序就必须重新编译

;而动态库在多个应用程序都要使用同一个函数库的时候就非常合适啦,但是前提是程序的运行环境中必须提供相应的库.不管是静态库和动态库,都是

有*.o目标文件生产的.


静态库和动态库的运用


要接下来我们再linux中创建一个静态库 动态库 以及使用他们. 我们定义三个文件 test.c test.h main.c 把它们放到同一个目录下.

//test.h
#ifndef _TEST_H__
#define _TEST_H__

int print();

#endif //_TEST_H__
//test.c
#include<stdio.h>
int print()
{
   printf("hello world\n");
   return 1;
}
//main.c

#include"test.h"
int main()
{
   print();
   return 0;
}



再首先我们要将test.c通过编译,可惜它不可以. 它没有办法通过gcc -o编译,这个道理非常简单,test.c没有main函数. 因此不构成一个完整的程序

现在我们有三个办法来解决这个问题! 

1.通过编写一个makefile,然后直接将目标代码合成一个.o文件.

2.通过创建静态库libmytest.a,使得main函数调用,print函数可调用静态链接库.

3.通过创建动态库libmytest.so,使得main函数调用,print函数可调用动态链接库.

下面我们只关心静态库和动态库方法! 首先是创建静态库.然后使用main函数调用print函数时可调用静态链接库.

Linux中静态库命名规范,必须是"lib[名称].a" lib为前缀,中间为静态库名,扩展名为.a. 例如我们将创建静态库名为mytest。结果为libmytest.a

创建一个linux下的静态库一共分两步: 

第一步:将代码文件编译成目标文件.既将test.c文件编译成test.o文件. 这里使用指令 gcc -c test.c

第二步:利用ar工具将目标文件hello.o打包成静态库文件.a(注意命名规则,我的静态库文件名为:libnytest.a) 


 
接下来我们让main.c使用到这个静态库即可. 其实这也很简单,只需要在编译的时候,指定静态库的搜索路径(-L选项),指定静态库名(只是名字).



接下来我们需要通过创建动态链接库libmytest.so,使得main函数调用 print函数时可调用动态链接库 创建动态库也有两个步骤:

第一步:生成目标文件,此时要加编译器选项-fPIC.[ps:-fPIC的创建与地质无关的编译程序,是为了能够在多个应用程序间共享]
gcc -fPIC -c test.c

第二步:生产动态库,此时要加链接器选项 -shared[ps:-shard是指定生成动态链接库]
gcc -shared -o libmytest.so test.o

其实我骗了你们,他们两个也可以合成一个命令:
gcc -shared -fPIC -o libmytest.so test.c



果不其然,我们生成了一个动态库, 接下来我们使用这个动态库. 但是我们刚刚使用静态库的方法就不能够在使用了,因为会报错.这个时候首先我们

有三种选择方案:

(1)把库拷贝到/usr/lib和/lib目录下。

(2)在LD_LIBRARY_PATH环境变量中加上库所在路径。例如动态库libhello.so在/home/ting/lib目录下,以bash为例,使用命令:

$export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/ting/lib

(3) 修改/etc/ld.so.conf文件,把库所在的路径加到文件末尾,并执行ldconfig刷新。这样,加入的目录下的所有库文件都可见。


我们选择第二种方法, mv libmytest.so /usr/lib 然后执行下面代码我们看看效果:



当我们把动态库删除之后,我们再看看效果:



这个时候就不能运行了,因为动态库把对一些库函数的链接载入推迟到程序运行的时期. 所以因为我删了动态库可执行程序报错了. 现在总结一下,这

种方式是动态库的常用的方式,我们程序在链接过程的时候链接到动态库,然后生产可执行文件. 在可执行文件执行时,将动态库内容加载到内存当中

所以程序启动起来之后,就跟动态库已经没什么关系了,其实这样是有一点隐患的,接下来我们来看看更好的方法.




动态链接库的动态加载方式



首先我记着前几年的时候,那个时候我们热爱的一些网络游戏总是会有一些游戏更新,大部分都停服更新,然后给你发好多的更新包等你更新了以后就

可以进行愉快的玩游戏了,现在我突然想了一下,我们下载的难道不就是更换了静态库的可执行文件吗? 然后替换掉本地的可执行文件,最后就完成

了游戏的更新. 而现在很多的游戏有一个不停服更新,不知不觉的我们就更新完成了,究其原因还是因为使用了动态库替换了静态库. 我们程序中的大

型动态库都是利用动态链接器连接动态库,然后动态库映射到共享存储映射区,供进程使用. 所以我们如果能够让程序运行的时候寻找动态库,而不是

我们给他规定好路径,也就是说! 就算进程跑起来之后,他每一次需要数据的时候,他不去内存里面拿,他还是要去远程动态库中拿. 这样我们每次

更新的时候,只需要更改远程库的内容即可,动态库的动态加载方式就是这么的强大!


在main.c中增加头文件#include<dlfcn.h>引入dlopen,dlsym,dlclose,dlerroe这几个系统调用.



我们目前为止只关心dlopen就可以啦.

第一个参数:指定共享库的名称,将会在下面位置查找指定的共享库.

第二个参数:指定打开共享库. -RTLD_NOW:将共享库的所有函数加载到内存  -RTLD_LAZY:会退后共享库中的函数的加载操作.

返回值:返回动态库的句柄.


对于以上几个函数我们需要对它们进行一定的了解,因为动态库的动态加载方式靠的都是他们这些函数,dlopen尤为重要。


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值