深入理解计算机系统阅读笔记-第三章

2024-08-29 10:12

本文主要是介绍深入理解计算机系统阅读笔记-第三章,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

第三章 程序的机器级表示

本章通过对比C语言程序代码和汇编程序代码了解程序的机器级表示

3.1 历史的观点

Intel处理器的发展历史,后文基于IA32指令集。

3.2 程序编码

基于unix系统gcc编译器;

在linux系统使用如下命令编译c文件,它会调用一些列程序(参考1.2),将c转化为可执行代码。

gcc -O2 -o helloworld helloworld.c

gcc是linux默认编译器

-O2 表示使用第二级优化,优化级别越高,程序运行的越快,但编译时间会更久。

3.2.1 机器级代码

汇编代码非常接近机器代码,但可读性更高,能够理解汇编代码以及它是如何与c代码对应的,是理解计算机如何执行程序非常关键的一步。

汇编程序可以看到如下c程序中无法提现的内容:

1. 程序计数器(%eip)

2. 正数寄存器:8个,用于存储32位值,可以存贮地址(对应c的指针),整数数据。比如程序状态,或临时数据(如局部变量)。

3. 条形码寄存器:保存最近执行的算数指令的状态信息。主要用来实现控制流中的条件变化(如实现if,while语句)

4. 浮点寄存器:8个,用于存储浮点数据

c可以在存储器中声明和分配各种类型的数据,但汇编只把存储器看成一个按字节寻址的大数组,不区分数据类型。

一条机器指令只能执行非常简单的基本操作。如将两个寄存器的数据相加,在存储器和寄存器之间传递数据,条件分支转移到新的指令等,编译器就是把c转换成这种简单的序列来实现c中复杂的代码。

3.2.2 代码示例

这本书太老了,所有的示例,在12代i5,ubuntu18的系统上运行和本书都对不上,所以强行理解吧。

int accum = 0;int sum(int x, int y)
{int t = x + y;accum += t;return t;
}

通过-S参数编译汇编代码code.s

gcc -O2 -S code.c

GCC是用过GAS(Gnu ASsembler) 格式产生汇编代码的,这种格式和intel文档和微软编译器使用的格式差异很大。

生成的汇编代码如下

sum:pushl %ebpmovl %esp,%ebpmovl 12(%ebp), %eaxaddl 8(%ebp), %eaxaddl %eax, accummovl %ebp,%esppopl %ebpret

使用-c参数可以生成目标文件code.o

gcc -O2 -c code.c

 使用反汇编器objdump可以将目标文件反汇编成一种类似于汇编代码的格式,输入如下命令

objdump -d code.o

直接输出结果

通过这个结果可以发现以下特性:

1. IA32指令长度从1~15个字节不等。指令编码被设计成常用的指令以及操作数较少的指令所需的字节数少,而那些不常用的或操作数较多的指令所需字节数较多。

2. 指令格式:从某个给定位置开始,可以将字节唯一地解码成机器指令。如只有指令push1 %ebp是以字节值55开头的。

3. 反汇编器只是根据目标文件中的字节序列来确定汇编代码的。它不需要访问程序的源代码或汇编代码。

4. 反汇编器使用的指令命名规则与GAS有细微差别。上面结果中省略了很多指令结尾的“l”。

5. 与code.s中的汇编代码相比,结尾多了nop指令。它根本不会被执行(它在过程返回指令之后),即使执行也不会有任何影响(nop,即no operation)。编译器插入这条指令的目的是为了填充存储该过程的空间。

实际的可执行代码必须包含main函数

int main()
{return sum(1, 3);
}

使用如下命令生成可执行文件

gcc -O2 -o prog code.o main.c

 3.2.3 关于格式的注释

GCC产生的汇编包含一些程序员不需要关系的信息,比如以“.”开头的行都是指导汇编器和链接器的命令(directive),后面的代码示例将会添加行号和注释。

3.3 数据格式

C基本数据类型的机器表示,GAS的每个操作都有一个字符后缀,表面操作数的大小。例如movb传送字节,movw传送字,movl传送双字。

3.4 访问信息

IA32的cpu包含8个32位寄存器大多数情况,前六个寄存器可作为通用寄存器,最后两个寄存器保存着指向程序栈中重要位置的指针,只有根据栈管理的标准惯例才能修改这两个寄存器的值。

如图所示,字节操作指令可以独立地读写前四个寄存器的两个低位字节,类似ah和al被称为寄存器的元素

3.4.1 操作数指令符

IA32支持多种操作数格式

操作数可以分为三种类型:

1. 立即数(immediate):在GAS中用$接整数,如$-577或$0xF;

2. 寄存器(register):表中用Ea表示任意寄存器a,用R[Ea]表示它的值,相当于将寄存器集看成数组R,Ea看做索引。

3. 存储器引用:根据计算出来的地址访问存储器的位置,用Mb[Addr]表示对存储在存储器中从地址Addr开始的b字节值的引用。为简便,通常省去b。

表中最下方是最通常的形式,由4部分组成,一个立即数便宜Imm,一个基址寄存器Eb,一个变址或索引寄存器Ei,一个伸缩因子s(scale factor),s必须是1、2、4、8;其他形式是这种通用形式的特殊情况,省略了某些部分。

练习题和答案

 3.4.2 数据传送指令

数据传送指令注意点:

源操作数:可以使立即数,寄存器,存储器地址;

目的操作数:寄存器,存储器

源操作数和目的操作数不能同时是存储器地址,所以如果要实现从存储器的一个地址传输数据到另一个地址的功能,需要拆分成两条指令实现。

movsbl:单字节传输,将前面24位设置为源字节的最高位扩展成32位,然后传输到目的操作数。

movzbl:单字节传输,将前面加24个0,扩展成32位,然后传输到目的 操作数中。

pushl和popl用来压栈和出栈。栈指针是前面的8个寄存器中的倒数第二个%esp。栈向下增长,即栈顶地址是最低的。

push1 %ebp

等同于

subl $4, %esp
movl %ebp, (%esp)

流程图

3.4.3 数据传送示例

从上面的代码可知:

1. 过程参数xp和y存储在寄存器%ebp中地址偏移8和12的地方。

3.5 算数和逻辑操作

下表列出一些双字整数操作。

3.5.1 加载有效地址

加载有效地址(Load Effective Address) 指令leal实际是movl指令的变形,它是将有效地址写入目的地址。假设%edx的值是x,则下面指令的作用是把%eax的值设为7+5x

leal 7(%edx, %edx,4), %eax

练习题3.3 假设%eax值为x,%ecx值为y,填写结果。

 

3.5.2 一元和二元操作

一元操作:只有一个操作数,既做源,又做目的。这个操作数可以是寄存器和存储器地址。比如incl(%esp)会使栈顶元素加1。

二元操作:第二个操作数既是源,又是目的,如subl %eax,%edx

 

3.5.3 移位操作

移位量用单个字节编码,只允许0~31位的移位,移位量可以是立即数,也可以放在单字节寄存器元素%cl中。

算数左移和逻辑左移一致,都是低位补0。

sarl执行算数右移 ,高位补符号位,shrl执行逻辑右移,高位补0

3.5.4 讨论

3.5.5 特殊的算数操作

3.6 控制

3.7 过程

3.8 数据分配和访问

3.9 异类的数据结构

3.10 对齐(alignment)

3.11 综合:理解指针

3.12 现实生活:使用GDB调试

3.13 存储器的月结引用和缓冲区溢出

3.14 *浮点代码

3.15 *在C程序中嵌入汇编代码

3.16 小结

这篇关于深入理解计算机系统阅读笔记-第三章的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1117567

相关文章

深入解析Spring TransactionTemplate 高级用法(示例代码)

《深入解析SpringTransactionTemplate高级用法(示例代码)》TransactionTemplate是Spring框架中一个强大的工具,它允许开发者以编程方式控制事务,通过... 目录1. TransactionTemplate 的核心概念2. 核心接口和类3. TransactionT

深入理解Apache Airflow 调度器(最新推荐)

《深入理解ApacheAirflow调度器(最新推荐)》ApacheAirflow调度器是数据管道管理系统的关键组件,负责编排dag中任务的执行,通过理解调度器的角色和工作方式,正确配置调度器,并... 目录什么是Airflow 调度器?Airflow 调度器工作机制配置Airflow调度器调优及优化建议最

一文带你理解Python中import机制与importlib的妙用

《一文带你理解Python中import机制与importlib的妙用》在Python编程的世界里,import语句是开发者最常用的工具之一,它就像一把钥匙,打开了通往各种功能和库的大门,下面就跟随小... 目录一、python import机制概述1.1 import语句的基本用法1.2 模块缓存机制1.

深入理解C语言的void*

《深入理解C语言的void*》本文主要介绍了C语言的void*,包括它的任意性、编译器对void*的类型检查以及需要显式类型转换的规则,具有一定的参考价值,感兴趣的可以了解一下... 目录一、void* 的类型任意性二、编译器对 void* 的类型检查三、需要显式类型转换占用的字节四、总结一、void* 的

深入理解Redis大key的危害及解决方案

《深入理解Redis大key的危害及解决方案》本文主要介绍了深入理解Redis大key的危害及解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着... 目录一、背景二、什么是大key三、大key评价标准四、大key 产生的原因与场景五、大key影响与危

深入理解C++ 空类大小

《深入理解C++空类大小》本文主要介绍了C++空类大小,规定空类大小为1字节,主要是为了保证对象的唯一性和可区分性,满足数组元素地址连续的要求,下面就来了解一下... 目录1. 保证对象的唯一性和可区分性2. 满足数组元素地址连续的要求3. 与C++的对象模型和内存管理机制相适配查看类对象内存在C++中,规

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

认识、理解、分类——acm之搜索

普通搜索方法有两种:1、广度优先搜索;2、深度优先搜索; 更多搜索方法: 3、双向广度优先搜索; 4、启发式搜索(包括A*算法等); 搜索通常会用到的知识点:状态压缩(位压缩,利用hash思想压缩)。

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听