【Linux】动静态库

1.动静态库的原理

源文件和头文件形成一个可执行文件需要经历以下的四个步骤

  • 预处理:展开头文件,宏替换,条件编译等,最后形成.i文件
  • 编译:完成词法分析、语法分析、词义分析、符号汇总等。将代码翻译成汇编指令,最后形成.s文件
  • 汇编:将汇编指令转换成二进制的机器码,最后形成.o文件
  • 链接:将各个.o文件链接,最后形成可执行文件。

比如下面需要用eg1.c,eg2.c,eg3.c,eg4.c和mian.c形成可执行文件,需要以下的步骤:

在这里插入图片描述

如果其他项目也需要用到eg1.c,eg2.c,eg3.c,eg4.c文件,那么形成可执行文件的步骤是相同的。
在这里插入图片描述

这里eg1.c,eg2.c,eg3.c,eg4.c被反复的使用,如果将这些.c文件打包在一起,就形成了库,所以库本质上就是一堆.o文件的集合,往往这些文件集合,提供了大量的可调用方法。

在这里插入图片描述

2.动态库和静态库基础

静态库(.a):**在编译链接的时候直接将整个库的代码整合复制到可执行文件中,**程序运行的时候不再需要静态库,所以使用静态库的形成的文件往往比较大。

动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。 动态库在编译的时候,在程序中只有一个指向的位置。

  • 一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码
  • 也就是说可执行文件在需要使用动态函数的时候,程序才会去读取函数库。
  • 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接(dynamic linking)
  • 动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间。

3.动静态库的实现

3.1设计一个静态库

准备下面的几个文件

mymath.h和mymath.c

在这里插入图片描述

myprint.c和myprint.h

在这里插入图片描述

将上面的mymath.c和myprint.c编译生成对应的xxx.o文件,下面编写对应的Makfile

在这里插入图片描述

编写Makefile

在这里插入图片描述

ar是gnu归档工具,rc表示(replace and create)

在这里插入图片描述

最后生成了对应的静态库。

发布静态库

在使用库的时候,需要什么东西?

库文件和头文件。

在这里插入图片描述

执行make static发布静态库

在这里插入图片描述

3.2设计一个动态库

shared: 表示生成共享库格式
fPIC:产生位置无关码(position independent code)
库名规则:libxxx.so

 gcc -fPIC -c sub.c add.c
 gcc -shared -o libmymath.so *.o
  • fpIC将对于的文件加载到物理内存中,允许加载到物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间。

  • shared:将动态库发布,形成共享库的格式

编写对应的Makefile

在这里插入图片描述

生成对应的动态库并发布

在这里插入图片描述

4.动静态库的使用

上面我们实现了动静态并发布,下面讲解如何使用动静态库;

我们将动态库和静态库放在一个test目录下进行测试。

cp lib-static test/ -rf
cp lib-dyl test/ -rf
4.1静态库的使用

在这里插入图片描述

在使用静态库前,需要知道库里面有那些方法。

然而在包头文件时,我们依然发现找不到静态库中的头文件。

在这里插入图片描述

解决方案一:

指定头文件的路径,这个用法在实际工程中重要不大。

在这里插入图片描述

解决方案二:

头文件的搜索路径:

  • 对于" "形式的头文件,是在当前(进程)路径查找。
  • 对应<>形式的头文件,是在系统头文件下查找。

所以:我们可以考虑将自己的头文件和库文件拷贝到系统路径下。

  • /usr/include这个路径下,存放了系统头文件
  • /lib64路径下,存放了系统的动静态库

在这里插入图片描述

cp lib-static/include/* /usr/include 	#将静态库下的所有头文件拷贝到系统头文件路径下
cp lib-static/lib/*  /lib64/				#将静态库下的所有库拷贝到系统路径下。

#要使用自己的库,-l需要指定第三方库的名字
gcc -o test main.c -lmymath

在这里插入图片描述

一个库的库名需要去掉前缀lib和后缀,静态库需要去掉lib和.a

在这里插入图片描述

注意:

方案二这种做法,容易污染操作系统的头文件和库,所以一般不推荐这种写法,除非是大佬写的库。

解决方案三:

指定路径:文件搜索路径

gcc ~~~ -I 头文件路径 -L 库文件路径 -l库名(去掉前缀lib和后缀)

在这里插入图片描述

4.2动态库的使用

方案一

将文件拷贝到系统路径,使用方法和静态库的使用方法相似。这种方法并不推荐

方案二:指定路径

使用方法:

gcc 依赖关系 -I 头文件路径 -L库文件理解 -l库文件名

在这里插入图片描述

为什么静态库生成的可执行程序可以成功执行,而动态库生成的无法执行?

程序和动态库是分开加载的。

在动静态库原理中我们提到过,链接静态库是直接拷贝静态库的内容,所以可执行程序不需要再依赖静态库

而动态库只是加一了一个指向,指向动态库的位置,而可执行程序执行是一个新的进程,所以需要重新链接动态库。

可执行程序如何链接动态库?

  • 将动态库拷贝到/lib64/中

在这里插入图片描述

这种方法会污染系统库文件,所以不推荐

  • 导入环境变量

程序在运行时,会在自己的环境变量**【LD_LIBRARY_PATH】**,中查找自己的动态库路径。

在这里插入图片描述

由于配置过Vim,所以与默认的有些不同。

接下来,可以将动态库的路径导入到LD_LIBRARY_PATH。

在这里插入图片描述

命令行式导入环境变量只在该shell中有效,重启后需要重新配置。

方案三:配置.conf文件

ldconfig 配置/etc/ld.so.conf.d/,ldconfig更新

在这里插入图片描述

动态库在运行时,还会扫描该路径【/etc/ld.so.conf.d/】下的配置文件。

在这里插入图片描述

所以我们可以创建一个动态库的配置文件,将动态库的路径加入配置文件中。

在这里插入图片描述

ldconfig 命令可以加载配置文件。然后程序就可以正常运行

在这里插入图片描述

ldd 文件:可以查看文件的链接信息。

在这里插入图片描述

方案四:在系统库中建立到动态库的软连接

由于动态库会自动搜索系统库文件,所以可以在系统库中建立导入的动态库的软连接

sudo ln -s 动态库绝对路径 /lib64/软连接名

在这里插入图片描述

4.3动态库的多进程共享原理

被加载到内存中的动态库,通过页表映射到了该进程地址空间中的共享区中,在运行时,则需要再次通过页表的映射,找到物理内存中的动态库。所以进程在运行时需要再次扫描物理内存。

而静态库在连接的过程中,代码则是被拷贝到可执行程序中,静态库则存放在每个进程的代码段,每个静态连接的进程都持有一份,所以静态库和进程可以分离。

在这里插入图片描述

如果有其他的进程动态链接了动态库:

在这里插入图片描述

所以动态链接有下面的特点:

  • 动态库(.so),程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
  • 动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间 。

一些好玩的库

比如ncurse,Linux下的图形化界面库;boost,很经典的C++库。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

影中人lx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值