【Linux】动态库和静态库

一、什么是动态库和静态库

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

二、如何生成静态库

// 生成静态库
ar -rc add.c -o add-o
// ar 是gnu归档工具,rc表示的是(replace and create)

// 查看静态库中的目录列表
ar -tv libmymath.a
// t : 列出静态库中的文件
// v : verbase 详细信息


gcc main.c -L. -lmymath
// -L : 指定库路径
// -l :  指定库名
// 测试目标文件生成后,静态库删除,程序照样可以运行

       ar命令是gnu的归档工具,常用语将目标文件打包为静态库,下面我们使用ar命令的 -r 选项和 -c 选项进行打包。

  • -r(replace) :若静态库文件当中的目标文件有更新,则使用新的目标文件替换旧的目标文件
  • -c(create) :建立静态库文件 

三、库的搜索路径

  • 从左到右搜索 -L 指定的目录
  • 由环境变量指定的目录(LIBRARY_PATH)
  • 由系统指定的目录(/usr/lib     /usr/local/lib)

四、生成动态库

第一步:使用 fPIC 选型生成对应的目标文件

  • fPIC :产生位置无关码(position independent code)
  • 库名规则:libxxx.so 
// 生成动态库的指令
gcc -fPIC -c sub.c add.c

说明一下:

  1. -fPIC作用于编译阶段,告诉编译器产生与位置无关的代码,此时产生的代码中没有绝对地址,全部都使用相对地址,从而代码可以被加载器加载到内存的任意位置都可以正确的执行。这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的。
  2. 如果不加-fPIC选项,则加载.so文件的代码段时,代码段引用的数据对象需要重定位,重定位会修改代码段的内容,这就造成每个使用这个.so文件代码段的进程在内核里都会生成这个.so文件代码段的拷贝,并且每个拷贝都不一样,取决于这个.so文件代码段和数据段内存映射的位置。
  3. 不加-fPIC编译出来的.so是要在加载时根据加载到的位置再次重定位的,因为它里面的代码BBS位置无关代码。如果该.so文件被多个应用程序共同使用,那么它们必须每个程序维护一份.so的代码副本(因为.so被每个程序加载的位置都不同,显然这些重定位后的代码也不同,当然不能共享)。
  4. 我们总是用-fPIC来生成.so,但从来不用-fPIC来生成.a。但是.so一样可以不用-fPIC选项进行编译,只是这样的.so必须要在加载到用户程序的地址空间时重定向所有表目。

第二步:使用-shared选项将所有目标文件打包为动态库

第三步:将头文件和生成的动态库组织起来

五、使用动态库

编译选项:

  • l : 连接动态库,只要库名即可(去掉lib以及版本号)
  • L : 连接库所在的路径

如果找不到动态库怎么办??

  • 直接将动态库拷贝到系统中
  • 将不在系统默认的搜索路径下的库路径,添加到LD_LIBRARY_PATH中
  • 通过软连接的方式
  • 在系统中创建一个配置文件,将库路径写入到配置文件中

六、运行动态库

有三种方式:

  1. 拷贝.so文件到系统共享库路径下,一般指的是 /usr/lib
  2. 更改 LD_LIBRARY_PATH
  3. ldconfig 配置 /etc/ld.so.conf.d/ ,ldconifg 更新

七、使用外部库

       系统中其实有很多库,他们通常由一组互相关联的用来完成某项常见工作的函数构成。比如用来处理屏幕显示情况的函数。(ncurses库)

-lm 表示要连接libm.so或者lib.a库文件。

八、库文件名称和引入库的名称

比如:libc.so -> c库,去掉前缀lib,去掉后缀.so, .a

如何在代码中使用动态库??(一共有四种方法)

当我们在编译可执行文件出错的时候,我们需要查看库有没有在当路径中,库有没有安装到系统中,下面的四种方法在使用动态库时,也是主要将库的路径找到。

我们在进行编译时,如果文件中的部分需要依赖动态库,我们需要链接动态库,但是在操作系统中,操作系统只认识存放在操作系统内部的地址,当我们链接外部的动态库时,我们需要让操作系统知道该动态库是否已经安装到系统中,操作系统是否认识到路径。

对于静态库来说,静态库只要编译成功之后,就不再需要了,即使将静态库进行删除,我们在执行程序也不会报错。

有四种方法来让操作系统查找我们所要链接的动态库:

动态库和静态库的区别:

如果我们同时提供动态库和静态库,gcc默认是动态库,如果我们非要静态连接,我们必须使用static选项,如果我们只提供静态库,那么我们的可执行程序也没有办法,只能对该库进行静态连接,但是程序不一定整体是静态连接的,如果我们只提供动态库,默认只能是动态链接,如果一定要使用静态链接会发生链接错误。

理解动态库加载

1.站在系统角度理解

我们在启动一个可执行文件时,我们会启动一个进程来执行文件,当一个进程启动时,操作系统会为该进程生成内部的数据结构:进程PCB,页表等,此时可执行文件和链接库文件都在磁盘中存储的,我们需要将可执行文件加载到内存中,当我们执行可执行文件的时候,遇到了动态链接的时候,我们需要将动态库的文件从磁盘中链接到内存中,在通过页表链接到虚拟地址空间中。

动态库也是叫做共享库,因为一个动态库只需要被加载一次,其他只要将映射建立成功即可。

2. 保留问题

谁来决定哪些库加载了那些库没有加载

我们在执行可执行文件的时候,有时不会只加载一个文件,有时会加载多个文件。那么这多个文件中,我们有时候会链接到同一个动态库中,那么这个动态库什么时候进行加载呢,这是由操作系统决定的。

系统可不可以同时存在非常多个已经记载的动态库??

可以在系统中存在非常多已经加载的动态库,操作系统需要将这些动态库先描述在组织,因为在操作系统中需要进行管理这些动态库。模拟一个结构体将动态库进行管理起来。

3. 谈谈编址(简单)谈谈可执行程序

可执行程序的本身是有自己的格式信息的,如果我们的可执行程序没有加载到内存中,我们的程序中有没有地址呢??答案是有的,在我们进行反汇编的代码中可以看到全是地址。其实我们的可执行程序在没有被加载之前,也已经基本被按照类别将可执行程序划分到各个区域中

我们进程地址空间里面的很多地址数据都是从可执行文件中来的,操作系统和编译器也是有关系的,虚拟地址空间本身不仅要遵守操作系统,编译器在编译程序的时候也需要遵守。

绝对编址

相对编址

4. 理解动态库动态链接和加载问题

    一般程序的加载

一些函数在磁盘中是分块存储,在没有加载到内存中,这些函数在磁盘中是以地址存放的。可执行文件中的构成是函数的地址以及一个ELF格式的表头。

地址空间既然是一个数据结构对象,谁来用什么数据初始化??我们的代码在没有加载到物理内存中的时候,这些代码已经有了地址编号,当加载到物理内存中的时候,我们可以将一批代码加载到物理内存中,这样就有了物理地址,这样就可以将物理地址和虚拟地址映射起来。这样,我们就可以填充到页表中。

CPU寄存器中有一个寄存器是PC指针寄存器——程序计数器,这个计数器保存正在执行指令的下一条指令的地址。在可执行文件的表头还存放着入口地址。

指令寄存器(不重要)

代码是数据吗,代码是要占内存空间的。

所谓的地址空间,本质是由操作系统 + 编译器 + 计算机体系结构(CPU)三者共同配合使用的

     动态库的加载

CPU是非常笨的,CPU只会按照我们给他的指令一行一行编译执行。

逻辑地址 = 虚拟地址 = 线性地址   物理地址

我们需要将程序和动态库都加载到内存中,操作系统会将

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

加油,旭杏

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

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

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

打赏作者

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

抵扣说明:

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

余额充值