Linux——动静态库

在进行开发过程中,我们不可避免地会使用到人家的库,那么库到底是什
么?而库又分为动态库和静态库,那么这两个又是什么?这篇博客由我来
简单介绍动静态库。

文章目录

  • 1. 库
  • 2. 静态库
    • a. 静态库的制作
    • b. 使用静态库
  • 3. 动态库
    • a. 动态库的制作
    • b. 动态库使用
    • c. 使用动态库的前置准备
  • 4. 动态库的加载
  • 5. 再次认识虚拟地址空间

1. 库

我们进行大型开发的时候,会有许多个头文件和原文件,并且也会使用C语言的标准库,还有或多或少的第三方库。当我们写出这样的程序:
在这里插入图片描述
我们知道它编译是使用了C语言的标准库,而我们包了stdio.h这个头文件,按常识来说,我们也因该会有他的原文件,而我们看到他源文件存放的地方找到他时,我们找不到.c为后缀的。但是可能会找到以lib开头.so或者.a.和某些版本号这样的形式的文件。而这样的文件就是库,而库分为动态库(Linux中.so后缀)和静态库(Linux中.a后缀)。库大致是我们所使用的其他原文件的集合目的,而使用动静态库的开发也提高了开发效率和安全性。这里的说明只是大致的说明,接下来我会详细的介绍。

2. 静态库

我们在开发过程中会建立多个头文件和原文件,比如制作一个简陋般的计算器:
在这里插入图片描述
在这里插入图片描述
我们使用gcc编译它们:
在这里插入图片描述
这里在编译时不需要加上头文件的原因是,gcc会自动在系统默认路径和当前路径下寻找需要的头文件。这样编译我们感觉还行,但是如果我们的原文件有100个呢?当我们把我们的头原文件交给他人使用的时候,其他人使用也是添加这么多原文件编译吗?会不会太费劲了?而且如果我们使用这么多的原文件需要交叉使用生成不同的可执行呢?每次都把他们重新编译一边吗?

a. 静态库的制作

所以这时候我们其实可以把所有的原文件处理成可重定向二进制文件,也就是将这些文件都处理成.o文件,这样最起码我们形成多个可执行的时候,不需要再重新编译浪费时间了,而只需要链接需要的文件就可以了:

在这里插入图片描述
而现在假如把我们写的头文件和.o文件交付给他人使用比交付给.c文件安全性高且效率高:
当用户使用时,只需要将他的原文件和发过来的.o文件编译链接就可以了:
在这里插入图片描述
我们这样使用是传过来的原文件较少,如果有许多个呢?这时候库就出现了。我们可以将这些.o文件进行打包这样使用的时候就没有那么麻烦而只用包相应的头文件就可以了,将.o文件打包的命令是ar命令:
在这里插入图片描述
其中的-rc选项意思是当生成文件已经存在时替换它,不存在则生成。
这样就做好了我们的静态库,现在我们将我们的静态库把User文件中的.o文件替换掉:
在这里插入图片描述
但是这样的格式是不太标准的,我们知道C标准库将头文件放在include目录下,库文件放在lib64目录下,我们也可以这样做:
在这里插入图片描述

b. 使用静态库

使用静态库需要使用到gcc的三个选项:
在这里插入图片描述
-I(大写i):让gcc找头文件的时候也在这个路径下找
-l(小写L):后面跟库的真实名字(去掉lib前缀和.a/.so后缀)
-L:后跟链接库的路径
这样就可以使用我们的静态库了,有人就会问了,编译链接动态库的时候gcc还的加个选项啊-static,这个选项表明的意思其实是:使用的库必须全部以静态库链接,如果链接的库只有动态库则直接报错,而我们的gcc默认编译链接是动静态混合的
静态库链接加载的方式是直接将内容拷贝到我们的可执行中

3. 动态库

在实际开发中我们的动态库是使用的较多的。

a. 动态库的制作

动态库的制作也是将.o文件打包,但是它是使用gcc来打包,并且打包时候的.o文件是经过处理的,我们会用到一个选项:-shared。处理.o文件时的选项-fPIC。
在这里插入图片描述
我们也像制作静态库标准那样来:
在这里插入图片描述
交付给用户:
在这里插入图片描述
这样一个动态库就制作好了。

b. 动态库使用

动态库的使用和静态库差不多:
在这里插入图片描述
但是当我们运行后;
在这里插入图片描述
它说找不到我们的动态库。原因就是动态库的加载是当我们的可执行程序成为进程后,动态库也会加载到内存中,而我们的可执行中只存储着动态库的位置,并把它加载到内存中。所以当我们的可执行使用动态库的时候就需要找到他,这里说它找不到的原因是,可执行程序找动态库只会在它的默认路径下找:
在这里插入图片描述
这里有个ldd命令,会显示可执行链接的静态库的情况。
所以我们要使用动态库还需要一些前置准备:

c. 使用动态库的前置准备

1. 将获取到的第三方库下载到我们的系统中

也就是将我们的头文件放到include中,将动态库放到lib64中:
在这里插入图片描述
而当这么做之后,我们以后有关链接这个动态库的编译,就不需要说明其他,而只需要说明库的真实名字即可。 在这里插入图片描述这也是使用第三方库最推荐的做法。

2. 当前目录/lib64下建立动态库的软链接

在这里插入图片描述
建立软链接:
在这里插入图片描述
在这里插入图片描述

3.环境变量

在Linux中有一个环境变量LD_LIBRARY_PATH。
在这里插入图片描述

它存储着让可执行找静态库的默认路径。所以只要添加我们的路径到它这里面也可以:
在这里插入图片描述
更改环境变量;
在这里插入图片描述
在这里插入图片描述

4. 更改配置文件

在Linux的一个路径下存着这么一些文件:
在这里插入图片描述
这些以.conf结尾的文件存储着可执行文件查找动态库时的路径:
在这里插入图片描述
所以我们也可以把我们的动态库所在路径添加到这里:
在这里插入图片描述
在这里插入图片描述
如果还是找不到的话,可以使用sudo ldconfig命令刷新一下配置文件。

以上就是我们动静态库的制作和使用,如果你写了一个比较好的库供人使用的话,那么就可以用以上格式,压缩之后传输给别人,别人解压缩安装好你的库就可以直接使用了。

4. 动态库的加载

我们要知道,我们写好代码之后经过编译器编译它就不存在什么变量名和函数名了,统统都变成了二进制,而这种二进制也不是杂乱无章的,它是有规则的,在Linux下,它遵循ELF格式。进行着有规则的数据分布,而他的数据分布大致如下:
在这里插入图片描述

可以看到它跟虚拟地址空间的的分布有些相似。这不是偶然现象,而是,ELF文件的格式就是虚拟地址空间的格式,因为栈和堆都需要动态分配,所以没有这两个分布。由此可以看出虚拟地址空间不只是一种技术,也是一套标准。
其中符号表就是记录了代码中所使用的函数与地址的映射关系。汇编代码中对函数的调用我们可以发现是通过call一个地址来跳转的,也就是说,程序没被运行的时候,可执行文件中就已经有了地址。而既然在未加载进内存中的时候就已经有了地址,那这个地址是什么呢?其实它就是虚拟地址,因为由于上面ELF文件存储数据的格式,导致它对文件编址的时候也基本遵循虚拟地址空间的方式。而这种地址在磁盘中就已经有了,所以它也叫逻辑地址,我们一般叫这个名字。
由于ELF可执行文件的编址符合虚拟地址,那么在32位机器下,它的可编址范围是从0到FFFFFFFF,那我们就可以将我们的代码进行编址了,它的编址大致如下:
在这里插入图片描述
在这其中补充个小知识点:我们上面使用到的地址其实都可以看成是从0开始然后加上一个偏移量(例如0+11223344)的方式,而这种以0为基地址,然后+偏移量寻址的方式叫做平坦模式。而ELF文件的编码都是以0为基地址,不会改变,所以我们可执行文件的编址方式也叫做绝对编址

我将代码简略表示了一下,在我们的main函数中只调用了printf函数和用户自己定义的Add函数,由于Add是我们属于我们自己代码的函数,所以当编译的时候直接就把它硬编址,给编好了地址,而我们的printf函数是库函数,而这个库是动态库,动态库又不会加载到我们的程序中,所以我们对它的编址先持保留。这个时候我们的可执行文件已经编译好了。当我们运行这个文件的时候,系统知道我们会使用到C标准库中的函数,当我们运行我们的可执行文件时,这个动态库当进程需要的时候这个动态库就已经在内存中了(因为函数的定义还是在动态库中)。所以我们的C标准库需要加载进内存中,但是我们的进程在执行代码的时候使用的并不是物理内存的地址,而是虚拟内存的地址啊。所以我们动态库要跟我们进程的虚拟地址空间产生联系,而虚拟地址空间中,共享区正好是建立这个“联系”的地方,而这种联系也是通过页表建立(会详细说明):
在这里插入图片描述
这里就要注意了,我们的可执行只会使用一个动态库吗?肯定不是的,它可能会调用许多个库。而这些库在某些时候会使用它,有些时候又不使用了,我说明的意思是这个共享区中动态库的“联系”是时刻变化的,那既然它在变化,说明某个动态库建立好“联系”后,又断开了然后又建立了,那么这次的建立和第一次建立“联系”的位置在共享区中是一个位置吗?结果肯定是不一定了,有可能被其他动态库在原来那个位置建立了。那么我们就需要动态库在进程地址空间中共享区建立联系的的位置是想在哪里建立就在哪里建立
这个“联系”中有一个重要的参数其中就是动态库在虚拟地址空间的地址
而动态库也是需要对其中的内容编址的呀,它这个时候还能使用平坦模式,还能以绝对编址的方式对代码编址吗?要知道动态库在虚拟地址空间的地址不是固定的,它的开头可不是0。动态库它就是为了进程而服务的。这个时候动态库就采用了相对编址的方式来对代码进行编制:
在这里插入图片描述
这样不管我们的动态库在地址空间中哪个地址中建立联系,只要库目前已经加载到地址空间中,那么它的地址也就确定了。我们只需要那个地址+所需函数的偏移量就能找到所需函数了。这就是相对编址。我们也发现了这种相对所选择的参考系是虚拟地址空间,而如果是库本身的话,那他也是绝对编址。
那么现在调用printf函数的时候我们的过程就如下:
在这里插入图片描述
这也就是为什么我们需要动态库的可执行程序运行时需要告诉动态库的信息了,因为它也要加载进内存中供进程使用使用的方式还是自身加载到共享区中的地址+目标函数的偏移量,来调用函数。这也是gcc 编译形成
.o文件时,需要使用位置无关选项了。而只使用静态库的程序不需要告诉进程静态库的信息原因就是你可以理解为,静态库在链接时,就相当于把自己的代码拷贝到了我们的程序代码中。
以上是我们运行一个程序,假如我们运行一百个程序呢?这一百个程序都用到了printf函数。那么这个时候如果程序使用动态库的话,还需要重复加载C标准库到内存中吗?显然是不用了,,因为只要各自进程中,建立与内存中已经加载好的那一个C标准库建立联系就可以了。所以这也是动态库为什么更受欢迎的原因,一方面是它可以用编译器gcc直接打包生成,另一方面,也节省了内存。而这也是为什么虚拟地址空间中存动态库的区域也叫做共享区了,动态库也叫做共享库。一个进程需要用到多个库,多个进程用到更多的库,那么库也需要被管理,那管理的方式自然还是先描述后组织了。

5. 再次认识虚拟地址空间

经过上面动态库的加载原理,我们现在再来看看我们的程序到底是怎么运行的。
我们知道我们的程序中存在地址,那么当我们的程序加载到内存中创建PCB,建立虚拟地址空间,成为一个进程时,我们的代码在物理内存中,物理内存需不需要对代码形成的指令的进行编址?答案肯定也是需要的。我们虚拟地址空间的分布那自然就把可执行文件的逻辑地址搬过来就成了我们的虚拟地址了。CPU执行我们的指令时,他肯定会从main函数开始,那main函数是怎么被找到呢?遍历吗?其实ELF文件格式中会记录main函数的地址,叫做entry,供操作系统读取。
CPU中会有一个指令寄存器来处理指令,要执行指令,首先要找到指令,那么这时候找指令使用的是虚拟地址还是物理地址呢?其实是虚拟地址。这样当CPU找指令的时候使用虚拟地址然后通过页表,找到指令的物理地址(我们说了物理内存也是需要对指令进行编址的)然后开始执行指令,这时候如果是调用函数的话,那么它的指令是call 一个地址,而这个指令又是在文件中的地址,所以这个地址肯定也是虚拟地址,CPU又会使用这个虚拟地址,来通过页表找到函数然后继续执行。这样我们的CPU好像在虚拟地址空间、页表、物理内存上转起来了。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/361223.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

打击者H5小游戏

欢迎来到程序小院 打击者 玩法&#xff1a;点击飞机上下左右移动躲过子弹射击&#xff0c;打掉上方敌人飞机&#xff0c; 遇到药包会增加能量&#xff0c;弹药包会升级武器&#xff0c;快去射击吧^^。开始游戏https://www.ormcc.com/play/gameStart/262 html <div id"…

基于矢量控制的交流电机驱动simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 4.1 交流电机基础 4.2 矢量控制原理 4.3 矢量控制的实现 5.完整工程文件 1.课题概述 基于矢量控制的交流电机驱动simulink建模与仿真。系统仿真输出电压&#xff0c;电流&#xff0c;电机转速以及扭矩…

语言革命:NLP与GPT-3.5如何改变我们的世界

文章目录 &#x1f4d1;前言一、技术进步与应用场景1.1 技术进步1.2 应用场景 二、挑战与前景三、伦理和社会影响四、实践经验五、总结与展望 &#x1f4d1;前言 自然语言处理&#xff08;Natural Language Processing&#xff0c;NLP&#xff09;是人工智能领域的一个重要分支…

快速入门存内计算—助力人工智能加速深度学习模型的训练和推理

存内计算&#xff1a;提高计算性能和能效的新技术 传统的计算机架构是将数据存储在存储器中&#xff0c;然后将数据传输到计算单元进行处理。这种架构存在一个性能瓶颈&#xff0c;即数据传输延迟。存内计算通过将计算单元集成到存储器中&#xff0c;消除了数据传输延迟&#…

中国的茶文化:现代生活中的茶文化

中国的茶文化&#xff1a;现代生活中的茶文化 引言 在现代社会的快节奏生活中&#xff0c;茶文化并未随时间流逝而褪色&#xff0c;反而以其独特的方式融入了全球各地人们的日常生活。它超越了饮品本身的范畴&#xff0c;成为一种连接历史、人文与现代生活方式的艺术形式。本文…

Git 介绍 与 配置

Git 介绍 Git是一个分布式版本控制系统&#xff0c;用于跟踪文件的更改和协作开发。它可以管理项目的版本历史记录&#xff0c;并允许多个开发者在同一时间进行并行开发。 解决上图产生的问题就出现了git 分布式版本控制系统 看下图 Git 配置 Git的基本配置包括用户名和电子邮…

Linux split命令 切割文件

目录 一. 主要配置项二. 按照行数切割文件三. 按照指定大小切割文件 一. 主要配置项 ⏹将文件按照行数或者大小切割为若干份小文件&#xff0c;主要作用就是用来切割文件 -l&#xff1a;表示将文件按照行分割-d&#xff1a;表示使用数字作为分割后的文件名后缀, 而不是默认的…

BUUCTF-Real-[ThinkPHP]5-Rce

1、ThinkPHP检测工具 https://github.com/anx0ing/thinkphp_scan 漏洞检测 通过漏洞检测&#xff0c;我们发现存在rce漏洞&#xff01; 2、漏洞利用 ---- [!] Name: Thinkphp5 5.0.22/5.1.29 Remote Code Execution VulnerabilityScript: thinkphp5022_5129.pyUrl: http://n…

跟着cherno手搓游戏引擎【16】Camera和Uniform变量的封装

相机封装&#xff1a; OrthographicCamera.h: #pragma once #include <glm/glm.hpp> namespace YOTO {class OrthographicCamera{public:OrthographicCamera(float left,float right , float bottom,float top);const glm::vec3& GetPosition()const { return m_Pos…

阿赵UE学习笔记——13、贴花

阿赵UE学习笔记目录 大家好&#xff0c;我是阿赵。   继续学习虚幻引擎的使用。这次介绍一种特殊的材质类型&#xff0c;贴花。 一、获取贴花资源 在没有分析贴花的原理之前&#xff0c;可以先去获得一些免费的贴花资源来使用&#xff0c;比如在Quixel上面就有专门的一个资源…

rp-bf:一款Windows下辅助进行ROP gadgets搜索的Rust库

关于rp-bf rp-bf是一款Windows下辅助进行ROP gadgets搜索的Rust库&#xff0c;该工具可以通过模拟Windows用户模式下的崩溃转储来爆破枚举ROP gadgets。 在很多系统安全测试场景中&#xff0c;研究人员成功劫持控制流后&#xff0c;通常需要将堆栈数据转移到他们所能够控制的…

iZotope RX 10.4.2 mac激活版 音频修复和增强工具

iZotope RX 10 for Mac是一款专业的音频修复软件&#xff0c;旨在提供强大、精确的工具&#xff0c;让用户能够清晰、纯净地处理音频。以下是其主要功能和特点&#xff1a; 软件下载&#xff1a;iZotope RX 10.4.2 mac激活版下载 强大的降噪功能&#xff1a;iZotope RX 10采用了…

P1228 地毯填补问题

地毯填补问题 题目描述 相传在一个古老的阿拉伯国家里&#xff0c;有一座宫殿。宫殿里有个四四方方的格子迷宫&#xff0c;国王选择驸马的方法非常特殊&#xff0c;也非常简单&#xff1a;公主就站在其中一个方格子上&#xff0c;只要谁能用地毯将除公主站立的地方外的所有地…

IMX6LL|打造自己的驱动总线

xbus&#xff1a;打造自属的驱动总线 驱动总线 软件与硬件代码分离&#xff0c;提高程序的复用性 device–关联硬件代码driver_devices–关联软件代码bus_type–统一管理、设置match匹配规则 设备驱动模型体现分离思想 bus-xbus-devices-drivers 总线管理 buses_init()函…

贪吃蛇---C语言---详解

引言 C语言已经学了不短的时间的&#xff0c;这期间已经开始C和Python的学习&#xff0c;想给我的C语言收个尾&#xff0c;想起了小时候见过别人的老人机上的贪吃蛇游戏&#xff0c;自己父母的手机又没有这个游戏&#xff0c;当时成为了我的一大遗憾&#xff0c;这两天发现C语…

C++ 之LeetCode刷题记录(二十四)

&#x1f604;&#x1f60a;&#x1f606;&#x1f603;&#x1f604;&#x1f60a;&#x1f606;&#x1f603; 开始cpp刷题之旅。 目标&#xff1a;执行用时击败90%以上使用 C 的用户。 119. 杨辉三角 II 给定一个非负索引 rowIndex&#xff0c;返回「杨辉三角」的第 rowI…

基于stm32F4卷积神经网络手写数字识别项目

加我微信hezkz17 可以申请加入嵌入式人工智能技术研究开发交流答疑群&#xff0c;赠送企业嵌入式AI 图像理解/音/视频项目核心开发资料 1 采用CNN BP反向传播算法更新权重系数 2 原理解析 3 实现策略 训练与识别分离&#xff0c;先在电脑上训练好CNN BP神经网络的模型&#…

音视频数字化(数字与模拟-音频广播)

在互联网飞速发展的今天,每晚能坐在电视机前面的人越来越少,但是每天收听广播仍旧是很多人的习惯。 从1906年美国费森登在实验室首次进行无线电广播算起,“广播”系统已经陪伴人们115年了。1916年,收音机开始上市,收音机核心是“矿石”。1920年开始“调幅”广播,1941年开…

又涨又跌 近期现货黄金价格波动怎么看?

踏入2024年一月的下旬&#xff0c;现货黄金价格可以说没了之前火热的状态&#xff0c;盘面上是又涨又跌。面对这样的行情&#xff0c;很多投资者不知道如何看了。下面我们就来讨论一下怎么把握近期的行情。 先区分走势类型。在现货黄金市场中有两种主要的走势类型&#xff0c;一…

WebAssembly核心编程[1]:wasm模块实例化的N种方式

当我们在一个Web应用中使用WebAssembly&#xff0c;最终的目的要么是执行wasm模块的入口程序&#xff08;通过start指令指定的函数&#xff09;&#xff0c;要么是调用其导出的函数&#xff0c;这一切的前提需要创建一个通过WebAssembly.Instance对象表示的wasm模块实例(源代码…