《程序员的自我修养--链接,装载与库》

第一章:温故而知新

过度优化的问题:

我们知道volatile关键字可以阻止过度优化,因为它可以完成两件事:

  • 阻止编译器为了提高速度将一个变量缓存到寄存器而不写回
  • 阻止编译器调整操作volatile变量的指令顺序

然而,在优化这一块,不仅编译器会做优化,CPU也会做优化。volatile就管不着了CPU了。
经典的例子当然是单例模式。单例模式有一种常规的解决方案是DCL,也就是双重检查锁,但是在C++中new的步骤有是分为三个步骤:分配内存,调用构造函数,将内存地址用指针保存下来。CPU就要来搞怪,将第二步第三步乱个序。
一种解决方案是:调用CPU提供的barrier指令阻止将barrier指令之前的代码交换到barrier之后。但是这种方案不具有可移植性。部分实现代码如下:

if(!pInst){lock();if(!pInst){T* temp = new T;barrier();pInst = temp;}unlock();
}

线程:

是程序执行流的最小单元。通常意义上,一个进程由多个线程组成,各个线程之间共享程序的内部空间(包括代码段,数据段,堆等)及一些进程级的资源(如打开文件和信号)

线程的访问权限:

线程调度与优先级

  • 运行(Running):此时线程正在执行
  • 就绪(Ready):此时线程可以立刻运行,但CPU已经被占用
  • 等待(Waiting):此时线程正在等待某一件事件发生,无法执行

可抢占线程和不可抢占线程

线程在用尽时间片之后会被强制剥夺继续执行的权利,而进入就绪状态,这个过程叫做抢占,即之后执行的别的线程抢占了当前线程。

在早期的一些系统中,线程是不可抢占的。线程必须手动发出一个放弃执行的命令,才能让其他的线程得到执行。可以避免一些因为抢占式线程里调度时机不确定而产生的问题。非抢占式线程已经十分少见

写时复制(Copy on Write,COW)

指的是两个任务可以同时自由地读取内存,担任一一个任务试图对内存进行修改时,内存就会复制一份提供给修改方单独使用

线程安全

信号量:

一个初始值为N的信号量允许N个线程并发访问。线程访问资源的时候首先获取信号量,进行一下操作:

最后一条应该是错了,应该是信号量不小于1,唤醒一个等待中的线程 

可重入(Reentrant)与线程安全

静态链接::第二章:编译与链接

被隐藏了的过程

通常将编译和链接合并到一起的过程称为构建(Build)

一个程序从代码到可以运行经过了四个步骤:预处理(Prepressing),编译(Compilation),汇编(Assembly),链接(Linking).c -> .i -> .s ->.o -> .exe

预编译

预编译过程主要处理那些源代码文件中的以“#”开始的预编译指令。比如“#include”,“#define”等

编译

编译过程就是把预处理完的文件进行一系列词法分析,语法分析,语义分析及优化后生产相应的汇编代码文件。

实际上 gcc 这个命令只是后台程序的包装,它会根据不同的参数要求调用预编译编译程序 cc1,汇编器 as,链接器 ld

词法分析: 

首先源代码程序被输入到扫描器,进行简单的词法分析,运用一种类似于有限状态机的算法将源代码的字符序列分割成一系列的记号(一般有关键字,标识符,字面量,特殊符号)

语法分析:

语法分析器将对由扫描器产生的记号进行语法分析,产生语法树,采用了上下文无关语法的分析手段。

语义分析:

 ​​​​​​编译器所能分析的语义是静态语义,所谓静态语义是指在编译器可以确定的语义,与之对应的动态语义就是只有在运行期才能确定的语义

主要检查声明和定义,类型转换是否出错等。做完这一步之后,语法树的每个节点都会有对应的类型。这一步还会对符号表里的符号进行更新。

中间语言生成 

图中(2+6)被优化成 8

目标代码生成与优化

主要是生成汇编代码,以及对汇编代码的优化。比如说,在优化这个阶段,可能导致指令重排序,这就引发了单例的一些问题。

汇编

汇编器是将汇编代码转变成机器可以执行的指令,每一个汇编语句几乎都对应一条机器指令。

到这一步为止,输出的是目标文件(Object File)

链接

编译器前端负责生成和机器无关的中间代码;后端负责将中间代码转化为机器目标代码。这样对于一些可跨平台的编译器而言,他们可以针对不同的平台使用相同的前端,而针对不同的机器平台有数个后端。

链接是这本书的主题。语言的发展是从编写一个代码文件到多个模块文件编写的,因此要让各个模块协同工作,就需要使用链接器。重要的一点是:在未链接之前,目标文件中的一些变量、函数地址是未决的(或者说可以这么理解:编译一个文件,可能有些变量、函数的地址确定不了,是个待定值),链接器就是来干这个事的:把一些指令对其他符号地址的引用加以修正。主要包括:地址和空间分配、符号决议和重定位。

静态链接::第三章:目标文件里有什么

目标文件的格式

  1. 可执行文件:Windows下的PE、Linux下的ELF
  2. 动态链接库:Windows的.dll,Linux的.os;静态链接库:Windows的.lib,Linux的.a
  3. ELF文件类型分类:1.可重定位文件;2.可执行文件;3.共享目标文件;4.核心转储文件
  4. 已初始化的全局变量和局部变量数据经常放在数据段.data,未初始化的全局变量和局部静态变量放在.bss段;编译后的机器指令经常放在代码段.code或.text;.rodata段存放的是只读数据,一般是程序里面的只读变量和字符串常量。.bss段只是为未初始化的全局变量和局部静态变量预留位置而已,int a=0会放在.bss段。
  5. 总体来说,程序源代码被编译以后主要分成两种段:程序指令和程序数据。代码段属于程序指令,而数据段和.bss段属于程序数据

6.ELF文件头包含:ELF魔数(魔数--用来确认文件的类型,操作系统在加载可执行文件的时候会确认魔数是否正确,如果不正确会拒绝加载)、文件机器字节长度、段表的位置、程序头入口和长度、段的数量、文件是否可执行、目标硬件、目标操作系统等

7.数据和指令分段的好处:

  • 不同段可以设置读写权限,可以防止程序被有意或者无意的修改
  • 提高CPU的缓存命中率
  • 当系统中存在多个该程序的副本,他们的指令都一样,那么内存中只需保存一份该程序的指令

8.段表:描述文件中各个段的数组,放置段的名称、段的长度、段类型、段在文件中的偏移位置、读写权限、段的属性等。编译器,链接器和装载器都是依靠段表来定位和访问各个段的属性的

9.重定位表:在处理目标文件时,必须要对目标文件中某些部位进行重定位,即代码段和数据段中那些对绝对地址的引用的位置。这些重定位信息都记录在ELF文件的重定位表里面。比如说数据的重定位表对应.rel.data

10.字符串表:因为字符串长度往往是不定的,固定结构存储较为困难。所以字符串表就是解决这个问题,使用字符串在表中的偏移来引用字符串。

11.链接过程的本质就是把多个不同的目标文件之间相互”粘“到一起,实际上拼合的是目标文件之间对地址的引用,即对函数和变量的地址的引用。我们可以将符号看作是链接中的粘合剂,整个连接过程正式基于符号才能够正确完成。每一个目标文件都有一个相应的符号表,这个表里面记录了目标文件中所用的所有符号。每个定义的符号有一个对应的值,叫做符号值。对于变量和函数来说,符号之就是它们的地址。

12.对符号进行分类:全局符号、局部符号、外部符号、段名、行号。链接只关注全局符号。

13.符号表中存放的内容有:编号、值、内存大小、类型、作用域、符号所属段、符号名字

14.特殊符号:一些内置的符号,比如_executable_start表示程序起始地址

15.函数签名:函数名、参数类型、所在类和名称空间。GCC的名称修饰方法就是:_Z + N(如果是嵌套)+n(后面的字符串长度)+str+E(结尾),比如N::C::func(int)经过名字修饰之后变成:_ZN1N1C4funcEi。由于不同编译器采用不同的名字修饰方法,必然导致由不同编译器编译产生的目标文件无法正常相互链接,这是导致不同编译器之间不能互操作的主要原因之一。

16.对于C/C++语言来说,编译器默认函数和初始化了的全局变量为强符号,未初始化的全局变量为弱符号。

17.针对强弱符号的概念,编译器就会按如下规则处理与选择多次定义的全局符号:

  • 不允许强符号被多次定义
  • 如果一个符号在某个目标文件中是强符号,在其他文件中是弱符号,那么选择强符号
  • 如果在多个文件中都是弱符号,那么选择其中占用空间最大的一个
     

第四章:静态链接

空间与地址分配:

对于多个输入目标文件,链接器如何将它们的各个段合并到输出文件?或者说,输出文件中的空间如何分配给输入文件?

按序叠加

最蠢的做法是顺序叠加多个目标文件的段,但是会存在内存对齐导致输出文件过大的情况;所以,大多数做法是采用相似段合并的策略。

相似段合并

将相同性质的段合并到一起,比如将所有输入文件的".text"合并到输出文件的".text"段

“链接器为目标文件分配地址和空间”这句话中的“地址和空间”有两个含义:第一个是在输出的可执行文件中的空间,第二个是在装载后的虚拟地址中的虚拟地址空间

两步链接:

第一步 空间与地址分配 

扫描所有的输入目标文件,并且获得他们的各个段的长度,属性和位置,并且将输入目标文件中的符号表中所有的符号定义和符号引用收集起来,统一放到一个全局符号表。这一步中,链接器将能够获得所有输入目标文件的段长度,并且将它们合并,计算出输出文件中各个段合并后的长度与位置,并建立映射关系

第二步  符号解析与重定位

使用上面第一步中收集到的所有信息,读取输入文件中段的数据,重定位信息,并且进行符号解析与重定位,调整代码中的地址等。事实上第二步是链接过程的核心,特别是重定位过程

符号解析与重定位

1.在未链接之前,目标文件中使用的其他编译单元的变量的地址未定义,会用0来给他占位,并且将这个未定义变量放进重定位表(专门用来保存与重定位相关的信息)中。

2.目前的链接器本身并不支持符号的类型,即变量类型对于链接器来说是透明的,它只知道一个符号的名字,并不知道类型是否一致。这就会导致由多个弱符号出现的时候的一些问题。COMMOM块来解决这些问题:当不同的目标文件需要的COMMON块空间大小不一致时,以最大的那块为准。

3.C++中模板导致的问题:一个模板在一个编译单元内进行了实例化,在另外一个单元可能也进行了实例化,当这两个单元链接的时候就会出现重复代码,进而导致空间浪费、地址出错、指令运行效率低的问题。一个比较有效的做法是将每个模板的实例化代码都单独存放在一个段里,每个段包含一个模板实例。同理,对于虚函数,内联函数,默认构造函数,默认拷贝构造函数也会存在这样的问题,解决方案都差不多。

4.main函数之前执行全局构造,对应汇编代码.init段;main函数之后执行全局析构,对应汇编代码.fint段

5.ABI:Application Binary Interfacd,对应API,层次不同,ABI主要内容是符号修饰标准、变量内存布局、函数调用方式等内容。C++一直被人诟病的一大原因是它的二进制兼容性不好。

 静态库链接

一个静态库可以简单地看成是一组目标文件的集合,即很多目标文件经过压缩打包后形成的一个文件。但事实上也是这样,比如libc.a就是由许多.o文件打包而成的。

链接器一般都提供多种控制整个链接过程的方法,以用来产生用户所需要的文件。一般链接器有以下三种方法:

  • ld命令行指定参数,之前使用的 ld 的 -o,-e 参数就属于这类
  • 目标文件中放置指令
  • 链接控制脚本

BFD库是一个GNU项目,它的目标是希望通过一种统一的接口来处理不同的目标文件格式。

参考文章:《程序员的自我修养》笔记_程序员的自我修养 写时复制-CSDN博客

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

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

相关文章

如何使用csdn中的c知道进行学习?

1.c知道 猜测是通过chatgpt训练链接到CSDN内部的文章内容等,进行生成的一款应用。 2.如何使用呢 打比方说,我想学习下多目标跟踪中的ukf,那么就可以输入这个关键字。 那既然是学习,就要进一步深究,有三种方式&#…

uniapp向上拉加载,下拉刷新

目录 大佬1大佬2 大佬1 大佬地址:https://blog.csdn.net/wendy_qx/article/details/135077822 大佬2 大佬2:https://blog.csdn.net/chen__hui/article/details/122497140

<软考高项备考>《论文专题 - 51 进度管理(2) 》

3 过程2-定义活动 3.1 问题 4W1H过程做什么识别和记录为完成项目可交付成果而须采取的具体行动的过程作用:将工作包分解为进度活动,作为对项目工作进行进度估算、规划、执行、监督和控制的基础为什么做对活动才能更详细更准确的分配资源、时间、成本谁…

C#,数值计算,求平方根之巴比伦算法(Babylonian algorithm)的源代码

平方根的巴比伦算法。 1 巴比伦算法介绍一 巴比伦算法可能算是最早的用于计算$sqrt{S}$的算法之一,因为其可以用牛顿法导出,因此在很多地方也被成为牛顿法。其核心思想在于为了计算x的平方根,可以从某个任意的猜测值g开始计算。在真实的运算…

屏幕截图--Snagit

Snagit是一款优秀的屏幕、文本和视频捕获、编辑与转换软件。它不仅可以捕获静止的图像,还能获得动态的图像和声音。软件界面干净清爽,功能板块一目了然,为用户提供专业的屏幕录制方案。可以根据自己的需求调整录制视频的分辨率、帧数、输出格…

探索Flutter中常用的系统组件

Flutter 是一款强大的开源移动应用框架,其丰富的系统组件使得开发者可以轻松构建漂亮且高性能的移动应用。在本文中,我们将深入探讨一些常用的 Flutter 系统组件,帮助开发者更好地理解和应用它们。 1. Scaffold(脚手架&#xff0…

Linux命令入门及ls命令

由于大家第一次接触到Linux命令,故此篇会详细讲述什么是命令,什么又是命令行?Linux命令的基础结构,什么是工作目录,什么又是HOME目录?并且带大家熟悉ls命令的基础使用。 1.命令和命令行 命令行&#xff1a…

使用spring cloud gateway作为服务网关

Spring Cloud Gateway是Spring Cloud官方推出的第二代网关框架,取代Zuul网关。网关作为流量的,在微服务系统中有着非常作用,网关常见的功能有路由转发、权限校验、限流控制等作用。 gateway需要注册到nacos中去,需要引入以下的依…

【PgSQL】聚合函数string_agg

在工作中,遇到了这样的需求,需要根据某一个字段A分组查询,统计数量,同时还要查询另一个字段B,但是呢这个字段B在分组后的记录中存在不同的值。最开始不知道有聚合函数可以实现这一功能,在代码中进行了处理。…

[嵌入式C][入门篇] 快速掌握基础3 (运算符)

开发环境: 网页版:跳转本地开发(Vscode):跳转 文章目录 一、 简介二、算术运算符(1)示例代码:(2)和--的先后顺序(直接看效果) 三、逻辑运算符(1)示例代码 四…

网络对讲终端 网络音频终端 网络广播终端SV-7011V使用说明

高速路sip广播对讲求助 隧道sip对讲调度SIP-7011 网络广播终端SV-7011 壁挂式对讲终端网络监听终端SIP广播终端 sip语音对讲终端SIP-7011 SV-7011网络对讲终端网络对讲、网络厂播、监听 SV-7101网络解码终端提供一路线路输出接功放或有源音箱。 SV-7102网络解码广播终端两…

HNU-数据库系统-讨论课2

第二次小班讨论课安排如下: 主题: 数据库系统设计与应用开发。 目的:让学生通过练习和讨论充分掌握数据库系统的设计与应用开发。 内容: 设计和实现一个数据库及应用系统。设计内容包括系统分析、系统设计、 数据库设计(需求分析、概念结构设计、逻辑…

Ranger UserSync

作用 同步User到RangerDb 架构 解析 启动一个while(True) 进程定时同步,程序入口 source sink 掉接口获取Ranger User 并且Cache 计算delta 同步

JavaScript基础(24)_dom查询练习(一)

<!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><link rel"stylesheet" href"../browser_default_style/reset.css"><title>dom查询练习一</title><style>.text {widt…

Anaconda 环境中安装OpenCV (cv2)

1、使用Anaconda 的对应环境&#xff0c;查看环境中的Python版本号 (1)使用Anaconda 查看存在的环境&#xff1a;conda info --env (2)激活环境&#xff1a;conda activate XXX 2、根据版本号&#xff0c;下载对应的 python-opencv 包 &#xff08;1&#xff09;选择国内源的…

【算法Hot100系列】括号生成

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学习,不断总结,共同进步,活到老学到老导航 檀越剑指大厂系列:全面总结 jav…

Android RecyleView 使用 Gilde 加载图片引发的卡顿问题

Glide 是一个用于 Android 的图片加载和缓存库。它可以帮助开发者快速、高效地加载网络图片、本地文件和视频帧&#xff0c;并且能够自动缓存图片数据&#xff0c;减少网络请求。Glide 具有良好的性能和易用的 API&#xff0c;支持常见的图片加载需求&#xff0c;例如图片压缩、…

scroll、offset、client —— JS三大家族

简介&#xff1a; clientHeight: 元素的可见高度&#xff0c;不包括边框和滚动条&#xff0c;是相对于视口的大小——clientHeight (content height) (padding toppadding bottom)。offsetHeight: 元素的高度&#xff0c;包括边框和滚动条&#xff0c;但不包括外边距 —— o…

即时设计:设计流程图,让您的设计稿更具条理和逻辑

流程图小助手 在设计工作中&#xff0c;流程图是一种重要的工具&#xff0c;它可以帮助设计师清晰地展示设计思路和流程&#xff0c;提升设计的条理性和逻辑性。今天&#xff0c;我们要向您推荐一款强大的设计工具&#xff0c;它可以帮助您轻松为设计稿设计流程图&#xff0c;让…

研旭开发板资料下载地址--DSP28335资料

研旭电气开发板网盘资料 温馨提示:南京研旭提供两种网盘下载方式,第一种是奶牛快传(下载速度快,但偶尔会出现下载不了的情况),第二种是百度网盘(下载速度较慢,偶尔会出现链接失效的情况),请自行选择合适的下载方式,如有其它问题请旺旺联系客服解决哦! 1、研旭F28…