动态链接(8/11)
静态链接的缺点:生成的可执行文件体积较大,当多个程序引用相同的公共代码时,这些公共代码会多次加载到内存,浪费内存资源。
为了解决这个问题,动态链接对静态链接做了一些优化:对一些公用的代码,如库,在链接期间暂不链接,而是推迟到程序运行时再进行链接。这些在程序运行时才参与链接的库被称为动态链接库。程序运行时,除了可执行文件,这些动态链接库也要跟着一起加载到内存,参与链接和重定位过程,否则程序可能就会报未定义错误,无法运行。
动态链接的好处是节省了内存资源:加载到内存的动态链接库可以被多个运行的程序共享,使用动态链接可以运行更大的程序、更多的程序,升级也更加简单方便。
在 Windows 下解压一个软件安装包,里面的 .dll 后缀的文件就是动态链接库,需要和可执行文件一起安装到系统中。程序在运行前会首先把它们加载到内存,链接成功后程序才能运行。
在 Linux 环境下,动态库文件的后缀为 .so。
gcc -fPIC -shared add.c sub.c mul.c div.c -o libtest.so
gcc main.c libtest.so
cp libtest.so /usr/lib/ # 程序就可以运行了
在上面的程序中,可执行文件 a.out 是动态链接生成的,所以在运行 a.out 之前,libtest.so 这个动态链接库要放到 /lib、/usr/lib 等系统默认的库路径下,否则 a.out 就会链接失败,无法正常运行。
在 Linux 环境下,当我们运行一个程序时,操作系统首先会给程序 fork 一个子进程,接着动态链接器被加载到内存,操作系统将控制权交给动态链接器,让动态链接器完成动态库的加载和重定位操作,最后跳转到要运行的程序。
动态链接器本身也是一个动态库(/lib/ld-linux.so文件),动态链接器被加载到内存后,会首先给自己重定位,然后才能运行。像这种自己给自己重定位然后自动运行的行为,一般称为自举。
动态链接器解析可执行文件中未确定的符号及需要链接的动态库信息,将对应的动态库加载到内存,并进行重定位操作。这个过程其实和静态链接的重定位过程一样,只不过推迟到了运行阶段而已。重定位结束后,程序中要引用的所有符号都有了地址和定位,动态链接器将控制权交给要执行的程序,跳转到该程序运行。
动态链接需要考虑的一个重要问题是加载地址。静态链接时,加载地址等于链接地址,这个地址是固定的。动态链接过程中,类似静态链接的重定位,动态链接库被加载到内存后,目标文件的起始地址也发生了变化,需要重定位。一个可执行文件对动态链接库的符号引用,要等动态链接库加载到内存后地址才能确定,然后对可执行文件中的这些符号修改即可。
main() 函数调用了 add() 函数,但 add() 函数的地址还不能确定,等到 libtest.so 加载到内存后,add() 函数的地址才能确定下来。加载器通过动态链接、重定位操作,更新了符号表中 add() 函数的实际地址,并修正 main() 函数指令中引用 add() 函数的地址,然后程序才可以正常运行。
这种装载时重定位的操作,虽然解决了可执行文件中绝对地址的引用问题,但也带来了另外一个问题:对于每个进程,动态库被加载到了内存的不同地址,也只能被进程自身共享,无法在多个进程间共享,无法节省内存,违背了动态库的设计初衷。如果有一种方法,将动态库设计成无论放到哪里,都可以执行,而且可以被多个进程共享,那么这个问题就迎刃而解了。
与地址无关的代码
如果想让我们的动态库放到内存的任何位置都可以运行,都可以被多个进程共享,一种比较好的方法是将我们的动态库设计成与地址无关的代码。将指令中需要修改的部分(如绝对地址符号的引用)分离出来,剩余的部分就和地址无关了。需要被修改的指令(符号)和数据在每个进程中都有一个副本,互不影响各自的运行。
编译代码时加上 -fPIC
参数(Position-Independent Code)就可以实现代码与地址无关:把这段代码放在内存中的任何位置,都无须重定位,直接运行即可(使用相对跳转指令代替对绝对地址的访问)。
全局偏移表
在动态库的设计中,对于模块内的符号相互引用,通过相对寻址很容易实现代码与地址无关。但是当动态库作为第三方模块被不同的应用程序引用时,库中的一些绝对地址符号(如函数名)将不可避免地被多次调用,需要重定位。动态库中的这些绝对地址符号,如何能做到同时被不同的应用程序引用呢?
每个应用程序将引用的动态库(绝对地址)符号收集起来,保存到一个表中,这个表用来记录各个引用符号的地址。当程序在运行过程中需要引用这些符号时,可以通过这个表查询各个符号的地址。这个表被称为全局偏移表(Global Offset Table,GOT)。
在一个可执行文件中,其引用的动态库中的绝对地址符号会被分离出来,单独保存到 GOT 表中,GOT 表以 section 的形式保存在可执行文件中,这个表的地址在编译阶段已经确定了。当程序运行需要引用动态库中的函数时,会将动态库加载到内存,根据动态库被加载到内存中的具体地址,更新 GOT 表中的各个符号(函数)的地址。等下次该符号被引用时,程序可以直接跳到 GOT 表查询该符号的地址,因为 GOT 表在可执行文件中的位置是固定不变的,所以程序中访问 GOT 表的指令也是固定不变的,唯一需要变化的是:动态库加载到内存后,库中的各个函数的位置确定,在 GOT 表中实时更新各个符号在内存中的真实地址就可以了。
这样做的好处是:在内存中只需要加载一份动态库,当不同的程序运行时,只要修改各自的 GOT 表,它们引用的符号都可以指向同一份动态库。
延迟绑定
动态连接通过使用与地址无关这一技术,加载到内存任意地址都可以运行。与地址无关这一技术在 ARM 平台可以使用相对寻址来实现。ARM 相对寻址的本质其实就是寄存器间接寻址,只不过基址换成了 PC 而已,访问效率还是比较低的,包括程序运行之前的动态链接和重定位操作,也会对程序的及时响应和性能造成一定的影响。可执行文件一般都采用延迟绑定:程序在运行时,并不急着把所有的动态库都加载到内存中并对它们进行重定位。当动态库中的函数第一次被调用时,才会把用到的动态库加载到内存并进行重定位。
C 标准库起始就是以动态库的封装形式保存在 Linux 系统中的,不同的应用程序都会调用 printf() 函数,当它们在内存中运行时,只需要加载一份 printf() 函数代码到内存就可以了。各个应用程序在引用 printf 这个符号时,就会启动链接器,将这份代码映射到各自进程的地址空间,更新各自 GOT 表中 printf() 函数的实际地址,然后通过查询 GOT 表找到 printf() 在内存中的实际地址,就可通过间接访问跳转执行。
共享库
现在大多数软件都是采用动态链接的方式开发的,不仅可以节省内存空间,升级维护也比较方便。在发布软件包时,可执行文件及其以来的动态链接共享库被一起打包发布,如果你依赖的是系统默认自带的共享库,如 C 标准库,则不需要跟软件一起打包。程序安装时,可执行文件会复制到 Linux 系统的默认路径下,如 /bin、/sbin、/usr/bin、/usr/local/bin 等,这些路径由环境变量 PATH 管理和维护。可执行文件依赖的共享库一般要放到库的默认路径下面:如 /lib、/usr/lib 等。当程序运行时,动态链接器首先被加载到内存运行,动态链接器会分析可执行文件,从可执行文件的 .dynamic
段中查询该程序运行需要依赖的动态共享库,然后到库的默认路径下查找这些共享库,加载到内存中并进行动态链接,链接成功后将 CPU 的控制权交给可执行程序,程序就可以正常运行了。
动态链接器在查找共享库的过程中,除了到系统默认的路径下查找,也会到用户指定的一些路径下去查找,用户可以在 /etc/ld.so.conf 文件中添加自己的共享库路径。为了减少每次查找文件的时间消耗,/etc/ld.so.conf 修改后,可以使用 ldconfig 命令生成一个缓存 /etc/ld.so.cache 以提高查找效率。每当我们新增、删除或修改共享库的路径时,使用 ldconfig 更新一下缓存就可以了。
相关文章:

动态链接(8/11)
静态链接的缺点:生成的可执行文件体积较大,当多个程序引用相同的公共代码时,这些公共代码会多次加载到内存,浪费内存资源。 为了解决这个问题,动态链接对静态链接做了一些优化:对一些公用的代码࿰…...
Python 之 Http 获取网页的 html 数据,并去掉 html 格式等相关信息
Python之 Http 获取网页的 html 数据,并去掉 html 格式等相关信息 目录 Python之 Http 获取网页的 html 数据,并去掉 html 格式等相关信息...

干不完根本干不完,我也不想加班,快来围观时间管理大师
时间不够用,怎么办? 成功不靠加班。生产队的驴都不加班,你加什么班?到点就下班,该玩玩,该学习认真学,累了就睡觉。 你可以做任何事,但不必做所有事。 时间管理,不是管…...

常见设计模式
概念 设计模式是怎么解决问题的一种方案 常见的设计模式 单例模式 概念:保证一个类仅有一个实例,并提供一个访问它的全局访问点。 应用:项目封装个websocket用于大屏,redux,vuex都应用了单例模式的思想;…...

Android之版本号、版本别名、API等级对应关系(全)(一百六十二)
简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 人生格言: 人生…...

Redis的简介,安装(Linux、Windows),配置文件的修改---详细介绍
Redis基础 文章目录 Redis基础1、Redis入门1.1、Redis简介1.2、Redis下载与安装1.2.1、在Linux系统安装Redis1.2.2、在Windows系统安装Redis 1.3、Redis服务启动与停止1.3.1、在Linux中启动服务1.3.2、在Windows中启动服务1.3.3、设置密码校验1.3.4、redis的远程连接 Redis是一…...

Vscode-工具使用
Vscode ,这玩意儿是开源的,以前用收费的破解版,过段时间就高版本不匹配,这次搞个不要钱的玩玩,记录使用心得 下载 下载地址:官网 点击下载,但是这里有个问题下载比较慢,解决办法&a…...

Ceph Reef版本 RBD 性能测试:80万写IOPS(10节点、60个NVMe SSD)
2023-05-16 08:30 发表于上海 摘自:https://mp.weixin.qq.com/s/mKkPElmCktoZaRk0m0IbqA 1、背景 Ceph 社区最近冻结了即将发布的 Ceph Reef 版本,今天我们研究一下 Ceph Reef 版本在 10 个节点、60 个 NVMe 磁盘的集群上的 RBD 性能。 在确保硬件没有…...

微信小程序调用map数据 并在wxml中对数组进行截取的操作
wxs文件的位置如图 实现数组截取 只保留五张图片 <wxs module"filter" src"./slicefunc.wxs"></wxs> <view class"wrap"><view class"search-box" bindtap"toSearch"><view class"v1"…...
前端项目打包
前端项目打包指的是将前端项目的源代码打包成一个或多个静态文件,以便于部署到服务器上,供用户访问。下面是一些常用的前端项目打包工具和打包步骤: 1、Webpack:Webpack 是一个强大的打包工具,可以将多个 JavaScript、…...
venv使用教程及pyvenv与python3-venv的区别
推荐使用python3-venv 原因:官方公告称——pyvenv自3.6 版本起弃用 原文:pyvenv 是针对 Python 3.3 和 3.4 创建虚拟环境的推荐工具,并在 Python 3.6 中被弃用。 Linux下创建虚拟环境(以Ubuntu为例) 首先安装依赖包 sudo apt-get instal…...

协程(一)单机--》并发--》协程
目录 一 协程的概述1.1 并行与并发1.2 线程1.3 新的思路1.4 Goroutine 二 第一个入门程序 一 协程的概述 我查看了网上的一些协程的资料,发现每个人对协程的概念都不一样,但是我认可的一种说法是:协程就是一种轻量级的线程框架(K…...
P1722 矩阵 II
题目背景 usqwedf 改编系列题。 题目描述 如果你在百忙之中抽空看题,请自动跳到第六行。 众所周知,在中国古代算筹中,红为正,黑为负…… 给定一个12n 的矩阵(usqwedf:这不是一个 2n 的队列么ÿ…...

【数据结构】树和二叉树的概念及结构
1.树概念及结构 1.1树的概念 树是一种非线性的数据结构,它是由n(n>0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。 有一个特殊的结点&#…...

8.1.tensorRT高级(3)封装系列-模型编译过程封装,简化模型编译代码
目录 前言1. 模型编译过程封装2. 问答环节总结 前言 杜老师推出的 tensorRT从零起步高性能部署 课程,之前有看过一遍,但是没有做笔记,很多东西也忘了。这次重新撸一遍,顺便记记笔记。 本次课程学习 tensorRT 高级-模型编译过程封装…...

化工行业案例 | 甄知科技助力万华化学重构IT服务价值,打造信息中心ERP!
随着科技的发展,新材料的应用领域与日俱增,近年来,全球化工新材料产业发展整体步入高技术引领、产品迭代速度快、产业规模和需求不断扩大的阶段。一体化协同与数字化转型策略是实现化工新材料生产原料自给、节能降耗、降低排放和物料成本的重…...

day6 STM32时钟与定时器
STM32时钟系统的概述 概念 时钟系统是由振荡器(信号源)、定时唤醒器、分频器等组成的电路。 常用的信号有晶体振荡器和RC振荡器。 意义 时钟是嵌入式系统的脉搏,处理器内核在时钟驱动下完成指令执行,状态变换等动作ÿ…...

【JavaEE进阶】SpringBoot 配置文件
文章目录 SpringBoot配置文件1. 配置文件的作用2. 配置文件的格式3. properties 配置文件说明3.1 properties 基本语法3.2 读取配置文件3.3 properties 优缺点分析 4. yml配置文件说明4.1 yml 基本语法4.2 yml 配置读取 5. properties和yml的对比 SpringBoot配置文件 1. 配置文…...
ResNet创新点总结
ResNet(Residual Networks)是深度学习中的一个重要架构,其创新点主要体现在解决了深层神经网络训练中的梯度消失和梯度爆炸问题,从而使得可以构建更深的神经网络。以下是 ResNet 的创新点总结: 1. 残差连接&#x…...

Scratch 之 3D 介绍及教程
第一章 为什么 3D 很难? 1.1 3D 难在何处? 3D 之所以会使我们觉得困难,是因为 Scratch 软件只有两个坐标轴,既:X轴、Y轴。 2维坐标系 而 3D 却拥有三个坐标轴: 3维坐标系 怎么办?很简单&…...

云原生 DevOps 实践路线:构建敏捷、高效、可观测的交付体系
📝个人主页🌹:一ge科研小菜鸡-CSDN博客 🌹🌹期待您的关注 🌹🌹 一、引言:DevOps 与云原生的深度融合 在传统软件工程范式下,开发与运维之间存在天然的壁垒。开发希望尽快…...
JavaScript 核心原理深度解析-不停留于表面的VUE等的使用!
一、解释 JavaScript 作为当今最流行的编程语言之一,广泛应用于 Web 开发、移动端开发、后端开发等多个领域。然而,许多开发者在使用 JavaScript 时,往往只关注其表面的语法和 API,而对其底层原理和核心机制了解甚少。深入理解 J…...
STM32启动文件学习(startup_stm32f40xx.s)
原代码 ;******************** (C) COPYRIGHT 2016 STMicroelectronics ******************** ;* File Name : startup_stm32f40xx.s ;* Author : MCD Application Team ;* version : V1.8.0 ;* date : 09-November-2016 ;* Desc…...

[AI绘画]sd学习记录(一)软件安装以及文生图界面初识、提示词写法
目录 目录一、安装软件二、文生图各部分模块 1. 下载新模型 & 画出第一张图2. 提示词输入 2.1 设置2.2 扩展模型2.3 扩展模型权重调整2.4 其他提示词输入2.5 负向提示词2.6 生成参考 3. 采样方法4. 噪声调度器5. 迭代步数6. 提示词引导系数 一、安装软件 软件安装&…...

【自动思考记忆系统】demo (Java版)
背景:看了《人工智能》中的一段文章,于是有了想法。想从另一种观点(⭕️)出发,尝试编码,告别传统程序员一段代码解决一个问题的方式。下图是文章原文和我的思考涂鸦✍️,于是想写一个自动思考记…...

vue实现点击按钮input保持聚焦状态
主要功能: 点击"停顿"按钮切换对话框显示状态输入框聚焦时保持状态点击对话框外的区域自动关闭 以下是代码版本: <template><div class"input-container"><el-inputv-model"input"style"width: 2…...

JavaSwing之--JMenuBar
Java Swing之–JMenuBar(菜单栏) JMenuBar是 Java Swing 库中的一个组件,用于创建菜单栏,通常位于窗口的顶部。它是菜单系统的容器,用于组织和显示应用程序的菜单结构 菜单栏由菜单构成,菜单由菜单项或子菜单构成,也…...
SSH/RDP无法远程连接?腾讯云CVM及通用服务器连接失败原因与超全排查指南
更多服务器知识,尽在hostol.com 嘿,各位服务器的“船长”和“管理员”们!咱们在浩瀚的数字海洋中驾驭着自己的服务器“战舰”,最怕遇到什么情况?除了数据丢失,恐怕就是突然发现自己被锁在“驾驶舱”门外—…...
【深尚想】OPA855QDSGRQ1运算放大器IC德州仪器TI汽车级高速8GHz增益带宽的全面解析
1. 元器件定义与核心特性 OPA855QDSGRQ1 是德州仪器(TI)推出的一款 汽车级高速运算放大器,专为宽带跨阻放大(TIA)和电压放大应用优化。核心特性包括: 超高速性能:增益带宽积(GBWP&a…...
zookeeper 学习
Zookeeper 简介 github:https://github.com/apache/zookeeper 官网:https://zookeeper.apache.org/ 什么是 Zookeeper Zookeeper 是一个开源的分布式协调服务,用于管理分布式应用程序的配置、命名服务、分布式同步和组服务。其核心是通过…...