[Linux]:动静态库
✨✨ 欢迎大家来到贝蒂大讲堂✨✨
🎈🎈养成好习惯,先赞后看哦~🎈🎈
所属专栏:Linux学习
贝蒂的主页:Betty’s blog
1. 动静态库的介绍
一般而言,库分为动态库和静态库。
- 在
Linux
当中,以.so
为后缀的是动态库,以.a
为后缀的是静态库。- 在
Windows
当中,以.dll
为后缀的是动态库,以.lib
为后缀的是静态库。
静态库在程序编译时被链接到目标代码中。一旦链接完成,静态库的代码就成为目标程序的一部分。这意味着如果多个程序都使用了同一个静态库,那么每个程序都会包含一份该库的副本,从而导致程序体积较大。
优点:
- 独立性强,不依赖外部环境,因为库代码已经被包含在程序中。
- 运行时加载速度相对较快,因为不需要在运行时进行库的加载操作。
缺点:
- 生成的程序体积较大。
- 如果静态库有更新,需要重新编译链接所有使用该库的程序。
动态库在程序运行时被加载。多个程序可以共享同一个动态库,只有当程序运行时才会将动态库加载到内存中。这大大减小了程序的体积,同时也方便了库的更新和维护。
优点:
- 生成的程序体积较小,因为库代码没有被包含在程序中。
- 库的更新不影响已编译的程序,只需要更新动态库文件即可。
缺点:
- 依赖外部环境,运行时需要确保动态库存在且路径正确。
- 加载动态库可能会带来一定的时间开销。
2. 动静态库的原理
我们知道,一个源文件变为一个可执行文件将经历四个步骤:
- 预处理: 完成头文件展开、去注释、宏替换、条件编译等,最终形成
xxx.i
文件。- 编译: 完成词法分析、语法分析、语义分析、符号汇总等,检查无误后将代码翻译成汇编指令,最终形成
xxx.s
文件。- 汇编: 将汇编指令转换成二进制指令,最终形成
xxx.o
文件。- 链接: 将生成的各个
xxx.o
文件进行链接,最终形成可执行程序。
比如我们现在有test1.c
,test2.c
,test3.c
,以及main1.c
这四个.c
文件,经过预处理,编译,汇编之后分别生成test1.o
,test2.o
,test3.o
,以及main1.o
这四个.o
文件。最后经过生成a.out
的可执行文件。
但是此时我们的main2.c
文件的生成同时也需要依赖test1.c
,test2.c
,test3.c
这三个文件,生成可执行程序的步骤都是一样的。此时我们就可以选择将test1.c
,test2.c
,test3.c
这三个文件生成的test1.o
,test2.o
,test3.o
进行打包,之后再使用时,只需要链接这个"包"即可,这个"包"其实就是我们常说的库。
所以动静态库的本质其实是一堆xxx.o
文件的集合。对于库的使用,只需要提供头文件让使用者了解具体功能的作用。在编译程序时,通过链接指定的库来实现对库中功能的调用。
3. 动静态库的使用
在Linux
下,我们可以通过ldd 文件名
来查看一个可执行程序所依赖的库文件。这其中的libc.so.6
就是该可执行程序所依赖的库文件,我们通过ls命令可以发现libc.so.6
实际上只是一个软链接。
实际上该软链接的源文件libc-2.17.so
和libc.so.6
在同一个目录下,为了进一步了解,我们可以通过file 文件名
命令来查看libc-2.17.so
的文件类型。
通过上图观察,我们知道gcc/g++
编译器默认都是动态链接的,如果想使用静态链接,需要在后面加一个-static
。如果你并没有安装对应的静态库的话,可以使用以下指令安装。
sudo yum install glibc-static
sudo yum install libstdc++-static
其中需要注意的是:动静态库真实文件名需要去掉前缀lib
,再去掉后缀.so
或者.a
及其后面的版本号,比如说libc-2.17.so
就是C语言的标准库,其名为:c-2.17
。
4 动静态库的打包
为了方便更加深入理解动静态库,接下来我们以下文件为例,讲解一下我们如何将我们的文件打包成动静态库。
其中add.h
的内容如下:
#pragma once
extern int add(int x, int y);
其中add.c
的内容如下:
#include "add.h"
int my_add(int x, int y)
{return x + y;
}
其中sub.h
的内容如下:
#pragma once
extern int sub(int x, int y);
其中sub.c
的内容如下:
#include "sub.h"
int sub(int x, int y)
{return x - y;
}
4.1 静态库的打包
然后我们需要将add.h
,add.c
,sub.h
,sub.c
这个文件打包成静态库。
- 首先第一步将源文件生成对应.o文件。
- 第二步使用ar指令打包成对应的静态库。
其中ar
指令用法为ar 选项 库名 打包文件名
,其中又两个关键选项:
-r
(replace):若静态库文件当中的目标文件有更新,则用新的目标文件替换旧的目标文件-c
(create):建立静态库文件
- 将头文件和生成的静态库组织起来。
当把自己的库提供给他人使用时,通常需要给予两个文件夹:
- 一个文件夹用于存放头文件集合。比如,可以将
add.h
和sub.h
这两个头文件放置在名为include
的目录下。- 另一个文件夹用于存放所有的库文件。例如,把生成的静态库文件
libmath.a
放到名为lib
的目录下。
最后,将这两个目录(include
和lib
)都放置在mathlib
目录下,此时就可以把mathlib
提供给别人使用了。
为了方便我们处理,我们可以写一个Makefile
。
libmath.a:add.o sub.oar -rc libmath.a $^%.o:%.c #展开所以.c文件生成对应的.o文件gcc -c $^
.PHONY:clean
clean:rm -rf ./*.o mathlib.PHONY:output #发表库
output:mkdir -p mathlib/includemkdir -p mathlib/libcp ./*.h mathlib/includemv ./*.a mathlib/lib
4.2 静态库的使用
我们如果使用我们打包的静态库,在使用gcc
编译时需要带有以下三个选项:
-I
:指定头文件搜索路径。-L
:指定库文件搜索路径。-l
:指明需要链接库文件路径下的哪一个库。
由于在程序执行时,编译器并不知晓我们所声明的头文件以及链接库的具体位置,而且链接库中可能存在不同的库文件。因此,我们需要在命令行中指定头文件的搜索路径,库文件的搜索路径,以及具体使用哪个库。
比如我们需要执行main.c
,其中main.c
中使用静态库中的add
函数。
#include<stdio.h>
#include"add.h"
int main()
{int a=1;int b=2;int ret=add(a,b);printf("%d\n",ret);return 0;
}
其中需要注意的是,-I
,-L
,-l
这三个选项后面可以加空格,也可以不加空格。
那么我们就有个疑问,那就是我们使用gcc
编译文件时为什么没有带-I
,-L
,-l
这三个选项呢?
其实很简单,因为我们之前使用的库都默认在系统的路径下: 编译器能准确识别这些存在于配置文件中的路径。其实如果为了方便我们也可以将头文件和库文件拷贝到系统路径/usr/include
,/lib.64
下:
- sudo cp mathlib/include/* /usr/include/
- sudo cp mathlib/lib/* /lib.64/
这时再使用gcc
编译时就只需要带-l
选项,指明链接库文件下具体哪个库。
但是实际上,我们并不推荐将自己写的头文件和库文件拷贝到系统路径下,因为这样做可能会对系统文件造成污染。
4.3 动态库的打包
动态库的打包相对于静态库较为复杂,但大致相同,我们还是利用add.h
,add.c
,sub.h
,sub.c
这四个文件进行打包演示:
- 首先第一步将源文件生成对应.o文件。
但是与静态库不同的是,需要带-fPIC
选项,因为动态库运行时才会被加载。
<font style="color:rgb(28, 31, 35);">-fPIC(position independent code)</font>
即产生位置无关码,作用于编译阶段,其目的是告诉编译器生成与位置无关的代码。在这种情况下,所产生的代码中不存在绝对地址,全部采用相对地址(起始位置加上偏移量)。这使得动态库被加载器加载到内存的任意位置时都能够正确执行。倘若不添加该选项,代码中使用的库函数在执行时会尝试调到对应位置执行,但此时可能会因该位置被其他动态库所占用而找不到该函数。
- 第二步:使用-shared选项将所有目标文件打包为动态库。
生成对应的动态库并不需要使用ar
指令,还是使用gcc
编译,只不过需要带-shared
选项。
- 将头文件和生成的动态态库组织起来。
与静态库类似,当把自己的库提供给他人使用时,通常需要给予两个文件夹:
- 一个文件夹用于存放头文件集合。比如,可以将
add.h
和sub.h
这两个头文件放置在名为include
的目录下。- 另一个文件夹用于存放所有的库文件。例如,把生成的静态库文件
libmath.so
放到名为lib
的目录下。
最后,将这两个目录(include
和lib
)都放置在mathlib
目录下,此时就可以把mathlib
提供给别人使用了。
同样为了方便管理,我们也可以定义一个Makefile
文件。
libmath.so:add.o sub.ogcc -shared -o $@ $^
%.o:%.cgcc -fPIC -c $<
.PHONY:clean
clean:rm -rf ./*.o mathlib
.PHONY:output
output:mkdir -p mathlib/includemkdir -p mathlib/libcp ./*.h mathlib/includemv ./*.so mathlib/lib
4.4 动态库的使用
我们如果使用我们打包的动态库,使用gcc
编译时同样需要带有以下三个选项:
-I
:指定头文件搜索路径。-L
:指定库文件搜索路径。-l
:指明需要链接库文件路径下的哪一个库。
因为在程序执行时,编译器同样并不知晓我们所声明的头文件以及链接库的具体位置,而且链接库中可能存在不同的库文件。因此,我们需要在命令行中指定头文件的搜索路径,库文件的搜索路径,以及具体使用哪个库。
比如我们需要执行main.c
,其中main.c
中使用动态库中的add
函数。
#include<stdio.h>
#include"add.h"
int main()
{int a=1;int b=2;int ret=add(a,b);printf("%d\n",ret);return 0;
}
但是与静态库不同的是,我们并不能直接执行a.out
这个可执行文件。
为什么使用了-I
,-L
,-l
这三个选项,还是没有找到对应的动态库呢?
这是由于我们使用
-I
、-L
、-l
这三个选项仅仅是在编译期间向编译器告知我们所使用的头文件和库文件的具体位置以及具体的库名。然而,当可执行程序生成后,它便与编译器不再有直接关系。所以,该可执行程序运行起来时,操作系统仍找不到该可执行程序所依赖的动态库。
为了解决这个问题,我们有三种方法:
- 第一种就是将库文件拷贝到系统共享的库路径下。
sudo cp mathlib/lib/libmath.so /lib64
但是这种方法可能会对系统文件造成污染,所以我们一般不采取该方法。
- 第二种就是更改环境变量LD_LIBRARY_PATH。
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/beidi_113/tmp/mathlib/lib(对应动态库所在路径)
LD_LIBRARY_PATH
是程序运行动态查找库时所要搜索的路径,我们只需将动态库所在的目录路径添加到LD_LIBRARY_PATH
环境变量当中,程序运行起来时就能找到对应的路径下的动态库。
但是我们知道环境变量在重启时会自动恢复,所以这种方法只在当前状态下有效,具有临时性。
- 配置.conf/文件
在系统中,/etc/ld.so.conf.d/
是用于搜索动态库的路径。此路径下存放的全是后缀为.conf
的配置文件,这些配置文件中所存放的内容都是动态库的路径。
因此,若将自己库文件的路径也放置在该路径下,那么当可执行程序运行时,系统就能够找到我们的库文件。并且这种行为是永久的,并不会随重启而改变。
首先我们将对应的库文件所在地址写入一个.conf
文件中,然后将其导入/etc/ld.so.conf.d/
路径,最后使用指令ldconfig
更新一下配置文件,最后我们就能执行我们的可执行文件了。
相关文章:

[Linux]:动静态库
✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:Linux学习 贝蒂的主页:Betty’s blog 1. 动静态库的介绍 一般而言,库分为动态库和静态库。 在Linux当中…...

windows 显示进程地址空间
windows 显示进程地址空间 windows 显示进程地址空间 文章目录 windows 显示进程地址空间显示进程地址空间 显示进程地址空间 /* 3-ProcessInfo.cpp 显示进程地址空间 */#include "..\\CommonFiles\\CmnHdr.h" #include "..\\CommonFiles\\Toolhelp.h"#i…...

Android 12 SystemUI下拉状态栏禁止QuickQSPanel展开
1.概述 遇到需求,QuickQSPanel首次下拉后展示快捷功能模块以后就是显示QuickQSPanel,而不展开QSPanel,接下来要从下滑手势下拉出状态栏分析功能实现。也就是直接是展开状态。 2、涉及核心类 frameworks\base\packages\SystemUI\src\com\and…...

二分思想与相关问题(下)
接下来详细讲解几道比较难的例题,仔细体会二分和其他概念混合在一起的趣味。 下面这道题涉及了“碎片拼接”的概念,很妙,也很难想。 P r o b l e m 5 Problem5 Problem5 同时运行N台电脑的最长时间 LeetCode2141 你有 n 台电脑。给你整数 n…...

【算法专题】搜索算法
二叉树剪枝 LCR 047. 二叉树剪枝 - 力扣(LeetCode) 本题要求我们将全部为0的二叉树去掉,也就是剪枝,当我们举一个具体的例子进行模拟时,会发现,只关注于对其中一个子树的根节点进行剪枝,由于我…...

B2064 斐波那契数列
题目描述 斐波那契数列是指这样的数列:数列的第一个和第二个数都为 11,接下来每个数都等于前面 22 个数之和。 给出一个正整数 aa,要求斐波那契数列中第 aa 个数是多少。 输入格式 第 11 行是测试数据的组数 nn,后面跟着 nn 行…...

Spark的介绍
一、分布式的思想 不管是数据也好,计算也好,都没有最大的电脑,而是多个小电脑组合而成。 存储:将3T的文件拆分成若干个小文件,例如每500M一个小文件,将这些小文件存储在不同的机器上 。 -- HDFS 计算&#…...

SpringBoot项目是如何启动
启动步骤 概念 运行main方法,初始化SpringApplication 从spring.factories读取listener ApplicationContentInitializer运行run方法读取环境变量,配置信息创建SpringApplication上下文预初始化上下文,将启动类作为配置类进行读取调用 refres…...

科技之光,照亮未来之路“2024南京国际人工智能展会”
全球科技产业的版图正以前所未有的速度重构,而位于中国东部沿海经济带的江浙沪地区,作为科技创新与产业升级的高地,始终站在这一浪潮的最前沿。2024年,这一区域的科技盛宴——“2024南京人工智能展会”即将在南京国际博览中心盛大…...

在深度学习计算机视觉的语义分割中,Boundary和Edge的区别是?
在深度学习中的计算机视觉任务中,语义分割中的 Boundary 和 Edge 其实有一些相似之处,但它们的定义和使用场景略有不同。下面是两者的区别: 1. Boundary(边界) 定义:Boundary 是指一个对象或区域的边界&a…...

【JAVA入门】Day41 - 字节缓冲流和字符缓冲流
【JAVA入门】Day41 - 字节缓冲流和字符缓冲流 文章目录 【JAVA入门】Day41 - 字节缓冲流和字符缓冲流一、缓冲流的体系结构二、字节缓冲流2.1 字节缓冲流提高效率的底层原理 三、字符缓冲流 在IO流体系中,FileInputStream,FileOutputStream,F…...

collocate join,bucket join,broadcast join,shuffle join对比分析
在分布式计算和大数据处理中,尤其是在使用像 Apache Spark、Hive 等大数据处理框架时,Join 操作是非常常见的。根据数据分布方式和执行机制,Join 操作可以分为不同的类型,如 Collocate Join、Bucket Join、Broadcast Join 和 Shuffle Join。以下是它们的详细对比分析: 1.…...

微信自动通过好友和自动拉人进群,微加机器人这个功能太好用了
又发现一个好用的功能,之前就想找一个这种工具,现在发现可以利用微加机器人的两个功能来实现,分别是加好友和关键词拉群 首先 微加机器人的专业版 > 功能 > 加好友设置 可以设置一个关键词通过,这样别人加好友的时候只需要输入制定内…...

R语言统计分析——功效分析3(相关、线性模型)
参考资料:R语言实战【第2版】 1、相关性 pwr.r.test()函数可以对相关性分析进行功效分析。格式如下: pwr.r.test(n, r, sig.level, power, alternative) 其中,n是观测数目,r是效应值(通过线性相关系数衡量࿰…...

Django创建模型
1、根据创建好应用模块 python manage.py startapp tests 2、在models文件里创建模型 from django.db import modelsfrom book.models import User# Create your models here. class Tests(models.Model):STATUS_CHOICES ((0, 启用),(1, 停用),# 更多状态...)add_time mode…...

盘点2024年大家都在用的短视频剪辑工具
你现在休息的时间是不是都靠短视频来消遣?看着看着你就会发现短视频制作好像我也可以了吧?这次我就介绍一些简单好操作的短视频剪辑工具。 1.FOXIT视频剪辑 连接直达>>https://www.pdf365.cn/foxitclip/ 短视频剪辑其实也不难,只需…...

“左侧文字横向”的QTabWidget
左侧用 QToolButton 组, 右侧用 QStackedWidget,信号槽绑定切换页面 可定制化高 QButtonGroup* btnGp new QButtonGroup(this);btnGp->addButton(ui->btn1, 0);btnGp->addButton(ui->btn2, 1);btnGp->addButton(ui->btn3, 2);connect…...

python学习之字符串操作
str python # 定义一个字符串变量 print(id(str))print(str) # 打印整个字符串 print(str[0:-1]) # 打印字符串第一个到倒数第二个字符(不包含倒数第一个字符) print(str[0]) # 打印字符串的第一个字符 print(str[2:5]) # 打印字符串第三到第…...

第7篇:【系统分析师】计算机网络
考点汇总 考点详情 1网络模型和协议:OSI/RM七层模型,网络标准和协议,TCP/IP协议族,端口 七层:应用层,表示层,会话层,传输层,网络层,数据链路层,…...

无人机培训机构组装调试技术详解
一、基础知识学习 在进入无人机组装调试领域之前,扎实的基础知识是不可或缺的。学员需掌握以下内容: 1. 无人机基本原理:了解无人机的飞行原理,包括升力、推力、重力和阻力等基本物理概念,以及无人机的飞行控制系统&…...

汽车的舒适进入功能是什么意思?
移动管家汽车的舒适进入系统是指无钥匙进入功能,它允许驾驶者在距离车辆一定范围内自动感应解锁车辆,并具备无钥匙启动功能。舒适进入系统的核心优势包括: 智能化操作:无需传统钥匙,通过智能感应实现车门解锁和…...

杂七杂八-系统环境安装
杂七杂八-系统&环境安装 1. 系统安装2. 环境安装 仅个人笔记使用,后续会根据自己遇到问题记录,感谢点赞关注 1. 系统安装 Windows安装linux子系统WSL2:使用windows系统跑linux程序(大模型)WSL VSCode:VSCode连接WSL实现高效…...

Redis高可用,Redis性能管理
文章目录 一,Redis高可用,Redis性能管理二,Redis持久化1.RDB持久化1.1触发条件(1)手动触发(2)自动触发 1.2 Redis 的 RDB 持久化配置1.3 RDB执行流程(1) 判断是否有其他持久化操作在执行(2) 父进…...

React项目中使用发布订阅模式
React项目中使用发布订阅模式 1.创建发布订阅器2.在组件中使用发布订阅器3. 订阅数据 发布订阅模式(也称观察者模式)是一种管理跨组件通信的有效方式,尤其是在不希望直接依赖于特定组件的情况下。这种模式允许一个对象(发布者&…...

buck boost Ldo 经典模型的默写
BUCK: BOOST: LDO: BUCK-BOOST:...

velero v1.14.1迁移kubernetes集群
1 概述 velero是vmware开源的一个备份和恢复工具,可作用于kubernetes集群下的任意对象和应用数据(PV上的数据)。github地址是https://github.com/vmware-tanzu/velero。 对于应用数据,可分文件级别的复制和块级别的复制。文件级…...

Qt Model/View之Model
在检查如何处理选择之前,您可能会发现检查模型/视图框架中使用的概念很有用。 基本概念 在模型/视图架构中,模型提供了一个标准接口,用于视图和委托访问数据。在Qt中,标准接口由QAbstractItemModel类定义。无论数据项如何存储在…...

如何在 Vue 3 中使用 Element Plus
在 Vue 3 中使用 Element Plus 是一个相对直接的过程,因为 Element Plus 是为 Vue 3 设计的 UI 组件库。以下是在 Vue 3 项目中集成和使用 Element Plus 的基本步骤: 1. 安装 Element Plus 首先,你需要在你的 Vue 3 项目中安装 Element Plu…...

【TVM 教程】在 Relay 中使用 Pipeline Executor
Apache TVM 是一个端到端的深度学习编译框架,适用于 CPU、GPU 和各种机器学习加速芯片。更多 TVM 中文文档可访问 → Apache TVM 中文站tvm.hyper.ai/ 作者:Hua Jiang 本教程介绍如何将「Pipeline Executor」与 Relay 配合使用。 import tvm from t…...

使用mingw64 编译 QT开发流程
1. 安装QT5 QT5.12.12 安装时选择mingw的开发包 2. 使用qtdesigner 进行ui设计 生成ui文件 3. 将ui文件转换为.h 文件 uic mywindow.ui -o ui_mywindow.h代码中指向生成的 UI 对象的地方 要改成这个Form 4. 编译 创建mainwindow.cpp #include "mainwindow.h"…...