【Linux】动态库与静态库的底层比较

news2024/11/24 3:15:31

在这里插入图片描述

送给大家一句话:
人生最遗憾的,莫过于,轻易地放弃了不该放弃的,固执地坚持了不该坚持的。 – 柏拉图

(x(x_(x_x(O_o)x_x)_x)x)

(x(x_(x_x(O_o)x_x)_x)x)

(x(x_(x_x(O_o)x_x)_x)x)


底层比较

  • 1 前言
  • 2 编译使用比较
  • 2 如何加载
  • Thanks♪(・ω・)ノ谢谢阅读!!!
  • 下一篇文章见!!!

1 前言

我们前两篇文章讲解了如何建立动静态库与如何使用动静态库。
接下来我们就来深入聊聊动静态库。

2 编译使用比较

那么 gcc编译的时候是怎么进行的:

  1. gcc不加-static选项默认使用动态库,没有提供动态库就只能使用静态库
  2. gcc加-static选项就使用静态库

那么-static的意义是什么呢?

  • 将我们的程序进行静态链接,这就要求我们链接的任何库都要通过对应的静态库版本!!!
    一般我们的操作系统都是动态库

并且在对.o文件打包的时候:

  1. 静态库使用ar -rc 文件名...
  2. 动态库使用gcc -shared,前提是.o文件里进行-fPIC位置无关码的设置gcc -fPIC -c 文件名

使用的方法:

  1. 静态库:
    • 安装到操作系统中,.h 文件放入/user/include中,.a文件放入/lib64/中 就可以了
    • gcc test.c -I../mylib/include/ -L ../mylib/lib -lmyc 使用命令直接表明使用的头文件路径,库文件路径和使用的库
  2. 动态库:
    • 直接安装到系统中/lib64/(或者建立软连接)
    • 命令行修改环境变量
    • 修改环境变量初始化脚本文件.bashrc
    • 增添配置文件

预测一下,如果我们使用别人的库,别人应该给我们提供什么?一批头文件 + 一批库文件(.so .a)

2 如何加载

如果要谈库是如何加载的,就要想来谈一谈可执行程序是怎么运行的!

首先,可执行程序与库都是磁盘文件。在可执行程序的运行之前需要先找到对应的文件。静态库很简单,不需要考虑这么多,因为在编译期间就把静态库的内容拷贝到了可执行文件当中。就不必谈论找到静态库这一说了。动态库就不一样,需要在运行的过程中寻找与加载!

根据我们先前学习的进程相关知识,可以大致画出一个示意图:
在这里插入图片描述
可执行程序运行的过程会把磁盘中a.out的文件读入到内存中,并形成对应的进程PCB模块与数据模块,然后就进入执行队列中进行调度运行。但是对应的方法并没有在可执行程序中,所以动态库是怎样被调用的呢?又是什么时候被调用呢?

动态库也会写入到内存中,并通过页表映射到地址空间中的共享区。让调用的时候通过共享区来找到对应的方法实现。
其他的可执行文件相要调用动态库中的方法是,也可以通过页表来映射就可以。所以动态库只需要在内存中存在一份

有个问题:我们的可执行程序,编译成功之后,如果没有加载运行,二进制代码中有没有对应的“地址”?

接下来我们来通过程序代码来探究一下。
我们创建一个新的目录,并写一段代码:

  1 #include<stdio.h>  
  2   
  3 int sum(int top)  
  4 {  
  5   int i = 1;  
  6   int ret = 0;  
  7   for(; i <= top ; i++)  
  8   {  
  9     ret += i;  
 10   }  
 11   
 12   return ret;  
 13 }  
 14   
 15 int main()  
 16 {
 17   int top = 100;
 18   int res = sum(top);                                                                                                                                                         
 19                                                                                                               
 20   printf("result:%d\n",res);                                                                                                                  
 21                                                                                                                                               
 22   return 0 ;                                                                                                                                  
 23 }    

我们把他编译一下,之后进行反汇编objdump -S code,下面就是程序汇编代码:
在这里插入图片描述
其中可以看到,前面都有一列地址,所以我们的可执行程序里面默认包含着地址。我们之间看源代码不用加载运行,就可以想象着一步一步运行我们的程序!

我们介绍一下ELF格式的程序,二进制是有自己的固定格式的,elf可执行程序的头部储存这可执行程序的属性!
可执行程序会变成无数条汇编语句,每条汇编语句都有对应的地址!那这个地址是什么地址,又是如何进行编址的呢?当前环境当中就是从000000...ffffff... 的地址(虚拟地址也叫逻辑地址)来进行平坦模式的编址。这样通过0 + 偏移量 就可以调用对应汇编的语句
在这里插入图片描述

操作系统中还要一个加载器,可以通过地址将数据拷贝到内存中。通过ELF+加载器 可以帮我们找到这个程序的开始与结束位置!!!

进程我们知道:进程 = 内核数据结构 + 代码与数据
那现在有个问题:当我们要加载这个程序时,是先加载内核数据结构还是先加载代码与数据呢?

来我们来进行模拟一下:

  1. 首先我们肯定是要形成PCB(状态 ,优先级…)
  2. 然后更关键的是创建地址空间(mm_struct),里面有区域划分(code_start , code_end , global_start),那么这些区域划分的初始值从哪里来呢???
  3. 初始值从可执行程序来!通过可执行程序自身的头部属性信息(虚拟地址)来初始化地址空间。虚拟地址空间不是操作系统独有的 ,OS ,编译器,加载器都会存在虚拟地址
  4. 此时就可以来把程序加载到内存中了

在这里插入图片描述

CPU中存在这样一个寄存器pc指针,用来指向当前执行指令的下一条指令的地址,pc指向哪里,CPU就执行哪里的语句!
依次进行就可以完成代码的执行!

总结一下:

  1. 进程创建阶段,初始化地址空间,让CPU知道main函数的入口地址
  2. 加载 -> 每一行代码与数据就都有了物理地址,自己的虚拟地址自己也就知道了,就可以构建映射了

接下来我们就来看看动态库是如何加载的:
先来看看动态库的回报代码,发现也是使用平坦模式进行编址的!
在这里插入图片描述
所以同样的,与加载可执行程序类似,会把动态库读入内存中,并建立对应的页表映射,**动态库的虚拟地址在进程地址空间里是在共享区里的。**那么对应的函数方法就有了起始与终止位置
在这里插入图片描述

那么当代码运行的时候,指向到了库函数,这是怎么处理?

  1. 首先,库的虚拟地址储存在共享区
  2. 在磁盘中,动态库的编址是平坦模式的编址,其地址0x1234就像是距离0000...的一个偏移量
  3. 然后在共享区里,这个偏移量是没有改变的1
  4. 所以想要执行库函数,就直接到共享区通过库的起始地址 + 偏移量找到对应的函数就可以执行了。所以只有了偏移量与库的初始地址,无论库加载到哪里都可以成功寻找到该函数 -> 也就验证了位置无关码!所以形成.o文件的时候就要加上-fPIC!!!

同样其他进程也可以通过共享区的库的起始地址 + 偏移量映射,来访问内存中的函数。库函数调用,其实也是在进程的地址空间里来回跳转!!!与非库函数类似奥!

那么怎么知道一个库有没有被加载到内存中呢?

动态库是由操作系统来管理的,所以就要有对应的描述结构体!!!所以使用的时候,想要知道有没有加载,就可以通过库的名称来找到对应的描述结构体,来查看是否被加载!!!

Thanks♪(・ω・)ノ谢谢阅读!!!

下一篇文章见!!!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

连升三级!openGauss单机版从2.1.0经停3.0.0升级至5.0.0

前言 如前文所述&#xff0c;我们的小demo项目起初安装了openGauss的2.1.0版本&#xff0c;由于2.1.0不是长期维护&#xff08;LTS&#xff09;版本&#xff0c;所以要升级到5.0.0LTS。考虑到虽然是DEMO项目&#xff0c;但也有些体验用户&#xff0c;所以为了保障业务连续性&a…

网络基础-Telnet协议

Telnet&#xff08;Telecommunication Network&#xff09;是一种基于文本的远程终端协议&#xff0c;允许用户通过网络连接到远程计算机&#xff0c;并在远程计算机上执行命令&#xff1b;它使用TCP作为传输层协议&#xff0c;并依赖于网络连接在客户端和服务器之间进行通信&a…

商务分析方法与工具(九):Python的趣味快捷-Pandas处理公司财务数据集思路

Tips&#xff1a;"分享是快乐的源泉&#x1f4a7;&#xff0c;在我的博客里&#xff0c;不仅有知识的海洋&#x1f30a;&#xff0c;还有满满的正能量加持&#x1f4aa;&#xff0c;快来和我一起分享这份快乐吧&#x1f60a;&#xff01; 喜欢我的博客的话&#xff0c;记得…

Linux部署

先把需要的东西准备好&#xff1a; 第一步解压tomcat&#xff1a; tar -zxvf apache-tomcat-8.5.20.tar.gz 第二步解压jdk: tar -zxvf jdk-8u151-linux-x64.tar.gz 第三步配置Java环境变量&#xff1a; vim /etc/profile 把下面代码放进去&#xff1a; export JAVA_HOME/root…

定时任务的几种实现方式

定时任务实现的几种方式&#xff1a; 1、JDK自带 &#xff08;1&#xff09;Timer&#xff1a;这是java自带的java.util.Timer类&#xff0c;这个类允许你调度一个java.util.TimerTask任务。使用这种方式可以让你的程序按照某一个频度执行&#xff0c;但不能在指定时间运行。…

基于SpringBoot+Vue社区老人健康信息管理系统

摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统社区老人健康信息管理系统信息管理难度大&#xff0c;容错…

网络爬虫概述与原理

网络爬虫概述与原理 网络爬虫简介狭义上理解功能上理解常见用途总结 网络爬虫分类通用网络爬虫聚焦网络爬虫增量网络爬虫深度网络爬虫 网络爬虫流程网络爬虫采集策略深度有限搜索策略广度优先搜索策略 网络爬虫简介 通过有效地获取网络资源的方式&#xff0c;便是网络爬虫。网…

【SpringBoot】解锁后端测试新境界:学习Mockito与MockMvc的单元测试魔法

文章目录 前言&#xff1a;Java常见的单元测试框架一.Junit5基础二.SpringBoot项目单元测试1.添加依赖2.SpringBoot单元测试标准结构3.SpringBoot单元测试常用注解 三.单元测试中如何注入依赖对象1.真实注入&#xff08;AutoWired、 Resource&#xff09;2.Mock注入2.1.前言2.2…

数据结构复习指导之树、森林

文章目录 树、森林 考纲内容 复习提示 1.树的存储结构 1.1双亲表示法 1.2孩子表示法 1.3孩子兄弟表示法 2.树、森林、与二叉树的转换 2.1树转换为二叉树 2.2森林转换为二叉树 2.3二叉树转换为森林 3.树和森林的遍历 3.1树的遍历 3.2森林的遍历 树、森林 考纲内容…

开源推荐榜【FunClip是一款完全开源、本地部署的自动化视频剪辑工具】

FunClip是一款完全开源、本地部署的自动化视频剪辑工具&#xff0c;通过调用阿里巴巴通义实验室开源的FunASR Paraformer系列模型进行视频的语音识别&#xff0c;随后用户可以自由选择识别结果中的文本片段或说话人&#xff0c;点击裁剪按钮即可获取对应片段的视频&#xff08;…

zookeeper安装集群模式

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&#xff0c;看懂了就去分享给你的码吧。 ZooKeeper是一个分…

JeeSite V5.7.0 发布,Java快速开发平台,Vite5、多项重构重磅升级

JeeSite V5.7.0 发布&#xff0c;Java快速开发平台&#xff0c;Vite5、多项重构重磅升级 升级内容 新增 参数配置 IP 地址黑白名单过滤器动态参数 新增 侧边栏是否展开第一个菜单的开关 first-open 新增 AesTypeHandler 处理字段数据加密解密或脱敏 新增 JsonTypeHandler …

247 基于matlab的梁的振型仿真

基于matlab的梁的振型仿真。利用有限元理论&#xff0c;求二维梁的固有频率和振型。短边固定&#xff0c;给定长度、横截面积&#xff0c;弹性模量及材料密度已知。并对比理论计算结果进行分析。各参数自己设定。程序已调通&#xff0c;可直接运行。 247 梁的振型仿真 固有频率…

Linux环境下parted工具使用

在工作中&#xff0c;我们经常会遇到大于分区大于2T的磁盘&#xff0c;由于系统盘最大不能超2T&#xff0c;我们会在做raid时将划分VD来进行装系统&#xff0c;但系统自动安装后无法将磁盘全部识别出来&#xff0c;管理员有时会要求手动对分区进行挂载&#xff0c;这个文档介绍…

收放卷伺服控制系统详细算法介绍(电子齿轮+张力PID卷绕轴控制功能块)

收放卷控制系统涉及的内容非常多,这里我们介绍全伺服系统利用电子齿轮指令实现主从轴的比例随动速度控制,收放卷控制算法介绍常用链接如下 1、收放卷+排线控制 收放卷+排线控制系统框图-CSDN博客文章浏览阅读24次。1、收放卷前馈量计算FC收放卷前馈量计算FC(CODESYS ST源代…

将python库下载到本地安装—Pypi官网wheel版本选择详解—小白详解版

python库—本地安装文件下载&#x1f680; 在项目中需要在内网环境下配置python的环境&#xff0c;因此需要将用于安装python库的文件下载到本地传到内网环境当中然后再安装&#xff0c;通过这契机我开始了解了一下如何离线下载安装python的第三方库&#xff0c;以及配置本地的…

【Docker】Ubuntu下Docker的基本使用方法与常用命令总结

【Docker】docker的基本使用方法 镜像image与容器container的关系基本命令- 查看 Docker 版本- 拉取镜像- 查看系统中的镜像- 删除某个镜像- 列出当前 Docker 主机上的所有容器&#xff0c;包括正在运行的、暂停的、已停止的&#xff0c;以及未运行的容器- 列出当前 Docker 主机…

Matlab 验证 复数的幂计算规则

复数的幂计算规则 close all a9; b0:0.1:5;result1 exp(1j*2*pi*a.*b); result2 (exp(1j*2*pi*a)).^b; idxfind(result1result2); b_idxb(idx);figure plot(b,angle(result1(:)),-r*) hold on plot(b,angle(result2(:)),bo) grid on

C++内存管理(1)

目录 1.new用法说明 2.new/delete在栈里面的运用 3.operator new/operator delete函数 4.构造函数的显式调用 5.malloc&&new&&free&&delete区别 1.new用法说明 &#xff08;1&#xff09;在C语言阶段&#xff0c;我们无论是为数组开辟空间&#x…

Disk Map for Mac,让您的Mac更“轻”松

还在为Mac磁盘空间不足而烦恼吗&#xff1f;Disk Map for Mac来帮您轻松解决&#xff01;通过独特的TreeMap视觉显示技术&#xff0c;让您一眼就能看出哪些文件和文件夹占用了大量空间。只需简单几步操作&#xff0c;即可快速释放磁盘空间&#xff0c;让您的Mac更“轻”松。快来…