【Linux】学习-动静态库
动静态库
头文件与库的区别
-
头文件一般而言,是声明和宏定义。头文件是在预处理阶段使用的
-
库文件是已经编译好的二进制代码。是一种目标文件,库文件是在链接阶段使用的
-
对于头文件和库我们可以这样理解,就是头文件提供的是一个函数的声明,并没有这个函数的具体代码,而库就是存放这个函数的具体实现代码
我们在写代码的时候,经常依赖别人给我们提供的函数,比如头文件中的reverse函数,我们在写的时候,这个头文件中包含有这个函数的声明,而这个函数的定义是存在于库中的,使用库的意义在于,我们可以将函数的定义直接编译成二进制,使得别人看不见这个函数的具体实现方法,别人使用时,只需要根据声明中的说明即可使用,这样使得软件作者既实现了发布方便或替换方便,方便程序的部署与开发。也方便了一些发布者不想让别人看到自己写的函数的具体实现方法,保护了代码隐私。当然,库中肯定不止有函数的定义,这里只是举了一个简单的例子,方便理解。
库的种类
库文件是已经编译好的二进制代码,这个二进制代码可以是动态的,如.so;也可以是静态的,如.a
如果是动态的,则最后生成的程序文件在运行时,需要这个动态库的支持,动态库又叫做共享目标文件
如果是静态的,则最后生成的 可执行程序文件运行时可以脱离这个库文件而独立运行。 静态库又叫做可重定位目标文件
- 静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库
- 动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
- 一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码
- 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接(dynamic linking)
- 动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间
目标文件
在生成我们的可执行程序之前,我们的程序一共会经过四个阶段:预处理,编译,汇编,链接。
在经过前面三个阶段后,汇编阶段会生成所谓的目标文件,windows下为.obj文件,Linux下为.o文件,目标文件是还没经过链接的文件,也就是说此时的目标文件中,有些符号还未被调整成正确的有效地址,仍然临时地址,如何理解呢?可以理解成,此时你写的程序内使用的reverse函数中还只是只有声明的部分,需要通过找到reverse存在的库的目标文件链接起来,这样才能成功调用reverse函数。
PC端的可执行文件格式主要包括了windows下的PE(Portable Execute)以及linux下的ELF(Execute Linkable Format),而目标文件属于生成可执行程序文件过程中的中间文件,其格式几乎与可执行程序的文件一模一样,因此可以看成是一种类型的文件,只不过是需要再通过最后一个阶段链接阶段将目标文件中各种各样的段合并起来,本质上文件格式并没有改变什么,想要深入了解可以参考一下这篇博客:目标文件详解,而我们的ELF格式的文件也是有分类型的:可重定位文件(relocate file),可执行文件(executable file),共享目标文件(shared object file)
ELF文件格式类型:
-
可执行文件
Linux下和windows下的.exe文件,也就是可以直接执行的文件
-
可重定位文件
windows下的.obj文件和Linux下的.o文件,也就是需要通过链接后形成可执行程序的目标文件
-
共享目标文件
动态链接文件,可以和其他可重定位文件和共享目标文件一起链接,生成新的目标文件
使用readelf -a filename 可以查看目标文件的ELF格式
relocatable:重定位
executable:可执行
静态库的特征、包装与使用
特征
- 静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库
- 如果是静态的,则最后生成的 可执行程序文件运行时可以脱离这个库文件而独立运行。 静态库又叫做可重定位目标文件
因为静态库是直接把依赖的库的代码链接到可执行文件中,也就是在进行链接一步时,把库中需要的内容直接拷贝一份到最终的可执行文件中,比如说你在自己的代码中写的reverse函数,头文件中是只有声明的,链接一步时, 需要进行合并符号表,而reverse函数的定义存在于库中,也就是需要合并库的符号表,若选择了静态库,即静态链接方式,就是把库中的符号表拷贝一份进可执行文件中,使得可执行文件的大小会明显地变得非常大。
静态库的优点:
加载快,可以独自运行
由于库中的内容已经被你拷贝进了可执行文件中,因此可执行程序执行时不再需要任何依赖,自己就包含了声明与定义,随时都可以自己执行。
静态库的缺点:
占用空间大
这不仅仅体现在可执行文件的大小上,若多个源文件都是以静态链接的方式去链接同一个静态库,那么一模一样的代码就会被拷贝多次,重复加载,使得磁盘空间也被重复占用。
兼容性与拓展性不佳
由于静态链接是将静态库拷贝进自己的代码中,那么当库中某一个函数的实现被修改,而可执行文件中保存的是旧的代码,这样就使得可执行文件又要再一次重新编译链接,不方便程序的升级迭代与重新部署。
包装
静态库形成步骤:
-
先写好 .c文件和 .h 文件
-
将 .c 文件编译成目标文件
-
使用ar命令生成静态库
库名字前缀必须是lib
-rc -> r(replace) c(creat)
查看静态库中目录列表:
t:列出静态库中的文件
v:verbose 详细信息
-
将头文件和库文件打包起来
将所有的头文件放在include目录下,静态库文件放在lib目录下,再将include目录和lib目录放在同一个目录下,这样将此目录发送给其他人即可使用:
- 可以使用Makefile文件一次性全部生成:
使用
-
使用:将mylib文件夹放在main函数所在的目录下
gcc编译器默认会先在当前路径下搜索头文件,然后再去库路径下搜索,所以需要显性的告诉编译器头文件的路径在哪:使用 -I 选项
还需要告诉编译器库的路径: 使用 -L 选项
以及告诉编译器库的名字:使用 -l 选项,使用-l选项时,去掉前缀 lib 和后缀 .o 即可得到库文件名
还可以直接将我们的库直接拷贝到系统路径下,这样编译时就不再需要指定路径,编译器会自动到系统路径下找
但不建议这么做,由于自己的库没有经过可靠性验证,尽量不要安装到系统路径下污染库文件
问题
使用file+文件 查看可执行程序所使用的链接方式, ldd+文件命令 查看可执行程序所依赖的库文件:
这就奇了怪了,为什么会是动态链接呢?我们使用ldd查看一下所依赖的库是什么:
我们查看一下所依赖的这个库文件的类型:
原来是一个共享目标文件类型,但我们已经测试过此可执行程序是可以运行的,也就是说,确确实实是已经使用了我们自己写的静态库了,那究竟是怎么回事呢?
其实,一切都要看我们写的程序中所需要依赖的库,由于我们的程序中存在<stdio.h>库中的printf函数,因此是需要用到C语言库的,而我们编译时指定库的路径时,并没有指定C语言库的路径,只指定了我们所需要的Add函数和Print函数所对应的库的路径,也就是说,没有指定路径时,编译器默认是用动态链接的方式使用C语言中的动态库,若我们强行要将所有的依赖文件全部改为静态链接方式也可以,带上选项 -static:
我们可以发现用这种方式将C语言的静态库文件以静态链接方式加载到可执行文件main中,使得main的文件大小非常之大,但我们所用到的仅仅只有printf函数!!这样就造成了空间的浪费!!
比较一个静态库一个动态库的方式下的文件大小,可以发现差距已经达到万级,因此,C语言库的文件还是以动态链接的方法合适!不到万不得已不使用静态链接!
动态库的特征、包装与使用
特征
- 如果是动态的,则最后生成的程序文件在运行时,需要这个动态库的支持,动态库又叫做共享目标文件
- 动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
静态库是库的代码与自己的代码一起在代码区来回跳,也就是执行可执行程序时就把库代码拷贝一份到自己的代码区了。动态库是一开始在硬盘中,执行到需要库的代码时,再将库加载进内存中,然后将库中对应所需的函数等方法建立与页表的映射关系,映射到共享区当中,然后代码区执行的时候再跳转到共享区:
动态库的优点:
节省磁盘空间,占用空间小
动态链接只需要将动态库的内容加载一次到内存中,若多个文件同时都依赖此库也不需要加载多次,并且只需要加载一些符号表信息与相对路径信息即可,不会占用可执行文件的大小空间
兼容性佳,拓展性强
还是以reverse函数的例子,如果reverse函数的实现改变了,那么只需要改变动态库本身即可,不需要重新编译执行可执行文件,因为他记录的仅仅是他的信息,通过信息去查找函数,若库本身就已经变了,那么下一次通过此信息去寻找时,找到的也是更新后的内容,不影响更新迭代,因此动态库大大方便了程序的升级和部署
动态库的缺点:
依赖性
由于静态链接的方式是直接将静态库文件加载进自己的可执行文件,因此下一次执行时不管静态库文件还存不存在都不影响使用,但动态链接不一样,由于可执行文件记录的是动态库信息,可以理解成记录的是如何找到动态库在内存中的位置,因此若动态库不存在了,可执行文件也无法正常运行了,具有强依赖性!
复杂性高
由于动态库的特殊链接方式,因此还需要设计动态链接器等等复杂的技术来辅助,而不像静态库一样粗暴,因此具有一定设计复杂性
包装
动态库形成步骤:直接用Makefile展示最后结果,若不明白回去静态库的形成再吃一遍
-
先写好.c .h 文件
-
将.c 文件以fPIC生成无关码方式编译形成.o文件,作用:告诉编译器此目标文件在任何路径都无所谓
-
直接用编译器使用 -shared选项生成动态库.so 文件
-
将.h 文件和 .so 文件包装起来
-
使用到的命令:
-
-
展示:
使用
还是跟静态库使用一样,先把包装好的目录放置main函数所在的目录下,并使用gcc编译器编译时指明头文件路径,库路径以及动态库名字:
问题
但我们发现,此时可执行程序无法运行,并且提示原因是找不到此共享文件目标,这是怎么回事呢?我们再一次通过ldd+文件名来查看
此时我们发现提示的是找不到对应的动态库,但我们不是已经指明了路径了吗?这是为什么?
- 其实,我们使用 -I -L -l选项时,其实是告诉编译器路径,但一旦可执行程序编译完毕,编译器就做完了他自己的事情了,我们要运行可执行程序时,就与编译器没有关系了,而是操作系统的事情,操作系统此时就找不到路径了,静态库不用找是因为库中代码已经被拷贝进自己的代码中了,并不需要再去找了, 所以为了让操作系统能够顺利找到并执行,有以下方法:
- 拷贝.so文件到系统共享库路径下,一般在/lib64
成功正常运行
- 使用环境变量LD_LIBRARY_PATH
但这种属于内存级别的环境变量,在下一次重启时就会被清掉了:
35)]
- [外链图片转存中…(img-55lGQr3v-1707561573535)]
成功正常运行
- 使用环境变量LD_LIBRARY_PATH
[外链图片转存中…(img-A6itzueN-1707561573535)]
但这种属于内存级别的环境变量,在下一次重启时就会被清掉了:
[外链图片转存中…(img-yaa4exwB-1707561573536)]
相关文章:

【Linux】学习-动静态库
动静态库 头文件与库的区别 头文件一般而言,是声明和宏定义。头文件是在预处理阶段使用的 库文件是已经编译好的二进制代码。是一种目标文件,库文件是在链接阶段使用的 对于头文件和库我们可以这样理解,就是头文件提供的是一个函数的声明&…...

人工智能之数学基础【最小二乘法】
原理 最小二乘法由勒让德(A.M.Legendre)于1805年在其著作《计算彗星轨道的新方法》中提出,主要思想是最小化误差二次方和寻找数据的最佳匹配函数,利用最小二乘法求解未知参数,使得理论值与观测值之差(即误差,或称为残差)的二次方和达到最小,即: E = ∑ i = 1 n ϵ …...

【Java安全】ysoserial-URLDNS链分析
前言 Java安全中经常会提到反序列化,一个将Java对象转换为字节序列传输(或保存)并在接收字节序列后反序列化为Java对象的机制,在传输(或保存)的过程中,恶意攻击者能够将传输的字节序列替换为恶…...

Nginx报错合集(502 Bad Gateway,504 Gateway nginx/1.18.0 (Ubuntu) 等等报错)
1.504 Gateway Time-outnginx/1.18.0 (Ubuntu) 日志报错: 2024/02/11 04:38:54 [error] 564#564: *29 upstream timed out (110: Connection timed out) while reading response header from upstream, client: *******, server: *******, request: "GE…...

Rust开发WASM,WASM Runtime运行
安装wasm runtime curl https://wasmtime.dev/install.sh -sSf | bash 查看wasmtime的安装路径 安装target rustup target add wasm32-wasi 创建测试工程 cargo new wasm_wasi_demo 编译工程 cargo build --target wasm32-wasi 运行 wasmtime ./target/wasm32-wasi/d…...

快速重启网络服务 IP Helper
有时候,因为需要配置虚拟机,又或者网络环境复杂的情况下。win10重启后,会造成网络服务失效。所以这时候需要重启网络服务。即重启IP Helper。每次 我的电脑->鼠标右键 管理->服务和应用程序->服务->IP Helper 右键重启࿰…...

【MySQL】MySQL函数学习和总结
🌈个人主页: Aileen_0v0 🔥热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 💫个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-Ny0xnYjfHqF7s3aS {font-family:"trebuchet ms",verdana,arial,sans-serif;font-siz…...

MySQL进阶查询篇(7)-触发器的创建和使用
MySQL数据库触发器的创建和使用 触发器(Trigger)是MySQL数据库中非常强大且有用的功能,它可以在特定的数据库事件发生时自动执行一段预定义的代码。触发器可以用于实现数据完整性约束、自动化业务逻辑、审计日志等功能。本文将介绍MySQL数据库中触发器的创建和使用…...

前端面试题——JS实现反转链式表
前言 反转单向链表就是将整个单链表的数据进行倒序的过程。 例如,如果反转之前的单链表是0->1->2->3,那么反转之后的单链表应该是3->2->1->0。这个操作通常是通过改变链表中每个节点的指针方向来实现的,即让每个节点的指…...

小周带你正确理解Prompt-engineering,RAG,fine-tuning工程化的地位和意义
有人会说:"小周,几天不见这么拉了,现在别说算法了,连code都不讲了,整上方法论了。" 我并没有拉!而且方法论很重要,尤其工程化的时候,你总得知道每种技术到底适合干啥&…...

【精选】java多态进阶——多态练习测试
🍬 博主介绍👨🎓 博主介绍:大家好,我是 hacker-routing ,很高兴认识大家~ ✨主攻领域:【渗透领域】【应急响应】 【python】 【VulnHub靶场复现】【面试分析】 🎉点赞➕评论➕收藏…...

Git详细讲解
文章目录 一、Git相关概念二、本地分支中文件的添加 、提交2.1 文件状态2.2 创建Git仓库2.2.1 git init2.2.2 git clone 2.3 添加操作(git add)2.4 提交操作(git commit)2.5 撤销操作2.5.1 撤销 add操作2.5.2 撤销 commit操作2.5.3 覆盖上一次的commit操…...

k8s弃用docker后使用ctr导入镜像
很多公司的k8s安装比较早,在生产环境一般很少升级,因此还是老版本,在使用新版本的时候,容易陷入老版本的思维中,从而掉坑,这里记录一下整个排查过程,希望对遇到类似的同学起到一定的帮助。 k8s 抛弃弃用docker 学习容器技术的过程中,我看到有不少同学留言问 Kubernet…...

mxxWechatBot开发中..
大家伙,我是雄雄,欢迎关注微信公众号:雄雄的小课堂。 免责声明:该工具仅供学习使用,禁止使用该工具从事违法活动,否则永久拉黑封禁账号!!!本人不对任何工具的使用负责&am…...

C#系列-C#log4net日志保存到文件(15)
在C#中使用log4net将日志保存到文件是一个常见的做法。log4net是一个功能强大的日志记录框架,它允许你配置日志的输出格式、级别、目标(例如文件、控制台、数据库等)等。 下面是如何配置log4net以将日志保存到文件的基本步骤: 安…...

linux 08 文件查找
02. 第一. alias:起别名(可以输入别名就可以执行对应的命令),语法:alias 别名‘ls -l’ 第二. locate: locate 找不到最近的文件 更新locate 后 find命令: find: find 路径 选项 文件名&#x…...

【Java面试】数据类型常见面试题
什么是包装类型 将基本类型包装进了对象中得到的类型 基本类型和包装类型有什么区别 用途不同:基本类型一般用于局部变量,包装类型用于其他地方存储方式不同:用于局部变量的基本类型存在虚拟机栈中的局部变量表中,用于成员变量…...

unity学习案例总结
动态标签 GitHub - SarahMit/DynamicLabel3D: Simple dynamic labels for a 3D Unity scene...

Halcon 频域缺陷检测
文章目录 傅里叶变换频谱矩形圆菱形黑白相间的亮带去除图纹(反傅里叶变换)去除图纹滤波器处理 Halcon 频域空间域检测缺陷Halcon 频域差分空间域 缺陷检测(lines_gauss 提取线)Halcon 频域差分空间域(blob特征…...

架构整洁之道-软件架构-测试边界、整洁的嵌入式架构、实现细节
6 软件架构 6.14 测试边界 和程序代码一样,测试代码也是系统的一部分。甚至,测试代码有时在系统架构中的地位还要比其他部分更独特一些。 测试也是一种系统组件。 从架构的角度来讲,所有的测试都是一样的。不论它们是小型的TDD测试ÿ…...

nodejs学习计划--(十)会话控制及https补充
一、会话控制 1.介绍 所谓会话控制就是 对会话进行控制 HTTP 是一种无状态的协议,它没有办法区分多次的请求是否来自于同一个客户端, 无法区分用户 而产品中又大量存在的这样的需求,所以我们需要通过 会话控制 来解决该问题 常见的会话控制…...

fast.ai 机器学习笔记(四)
机器学习 1:第 11 课 原文:medium.com/hiromi_suenaga/machine-learning-1-lesson-11-7564c3c18bbb 译者:飞龙 协议:CC BY-NC-SA 4.0 来自机器学习课程的个人笔记。随着我继续复习课程以“真正”理解它,这些笔记将继续…...

LLM大模型常见问题解答(2)
对大模型基本原理和架构的理解 大型语言模型如GPT(Generative Pre-trained Transformer)系列是基于自注意力机制的深度学习模型,主要用于处理和生成人类语言。 基本原理 自然语言理解:模型通过对大量文本数据的预训练ÿ…...

这种学习单片机的顺序是否合理?
这种学习单片机的顺序是否合理? 在开始前我有一些资料,是我根据网友给的问题精心整理了一份「单片机的资料从专业入门到高级教程」, 点个关注在评论区回复“888”之后私信回复“888”,全部无偿共享给大家!!…...

13 年后,我如何用 Go 编写 HTTP 服务(译)
原文:Mat Ryer - 2024.02.09 大约六年前,我写了一篇博客文章,概述了我是如何用 Go 编写 HTTP 服务的,现在我再次告诉你,我是如何写 HTTP 服务的。 那篇原始的文章引发了一些热烈的讨论,这些讨论影响了我今…...

flask+python高校学生综合测评管理系统 phl8b
系统包括管理员、教师和学生三个角色; 。通过研究,以MySQL为后端数据库,以python为前端技术,以pycharm为开发平台,采用vue架构,建立一个提供个人中心、学生管理、教师管理、课程类型管理、课程信息管理、学…...

【GameFramework框架内置模块】1、全局配置(Config)
推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址 大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。 一、前言 【GameFramework框架】系列教程目录: https://blog.csdn.net/q7…...

PySpark(四)PySpark SQL、Catalyst优化器、Spark SQL的执行流程、Spark新特性
目录 PySpark SQL 基础 SparkSession对象 DataFrame入门 DataFrame构建 DataFrame代码风格 DSL SQL SparkSQL Shuffle 分区数目 DataFrame数据写出 Spark UDF Catalyst优化器 Spark SQL的执行流程 Spark新特性 自适应查询(SparkSQL) 动态合并 动态调整Join策略 …...

2024第六届中国济南国际福祉及残疾人用品展览会/失能护理展
龘龘龙年-第六届山东福祉展会-将于5月27-29日在济南黄河国际会展中心举办; 一、引言 2024年,中国龙年,龙象征着力量、繁荣与希望。在这个特殊的年份,一场备受瞩目的盛会即将拉开帷幕。2024年第六届中国(济南…...

SegmentAnything官网demo使用vue+python实现
一、效果&准备工作 1.效果 没啥好说的,低质量复刻SAM官网 https://segment-anything.com/ 需要提一点:所有生成embedding和mask的操作都是python后端做的,计算mask不是onnxruntime-web实现的,前端只负责了把rle编码的mask解…...