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

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

相关文章

深入解析 Java Future 类及代码示例

《深入解析JavaFuture类及代码示例》JavaFuture是java.util.concurrent包中用于表示异步计算结果的核心接口,下面给大家介绍JavaFuture类及实例代码,感兴... 目录一、Future 类概述二、核心工作机制代码示例执行流程2. 状态机模型3. 核心方法解析行为总结:三

spring IOC的理解之原理和实现过程

《springIOC的理解之原理和实现过程》:本文主要介绍springIOC的理解之原理和实现过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、IoC 核心概念二、核心原理1. 容器架构2. 核心组件3. 工作流程三、关键实现机制1. Bean生命周期2.

MySQL数据库约束深入详解

《MySQL数据库约束深入详解》:本文主要介绍MySQL数据库约束,在MySQL数据库中,约束是用来限制进入表中的数据类型的一种技术,通过使用约束,可以确保数据的准确性、完整性和可靠性,需要的朋友... 目录一、数据库约束的概念二、约束类型三、NOT NULL 非空约束四、DEFAULT 默认值约束五、UN

Java Stream流使用案例深入详解

《JavaStream流使用案例深入详解》:本文主要介绍JavaStream流使用案例详解,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录前言1. Lambda1.1 语法1.2 没参数只有一条语句或者多条语句1.3 一个参数只有一条语句或者多

深入理解Apache Kafka(分布式流处理平台)

《深入理解ApacheKafka(分布式流处理平台)》ApacheKafka作为现代分布式系统中的核心中间件,为构建高吞吐量、低延迟的数据管道提供了强大支持,本文将深入探讨Kafka的核心概念、架构... 目录引言一、Apache Kafka概述1.1 什么是Kafka?1.2 Kafka的核心概念二、Ka

利用Python快速搭建Markdown笔记发布系统

《利用Python快速搭建Markdown笔记发布系统》这篇文章主要为大家详细介绍了使用Python生态的成熟工具,在30分钟内搭建一个支持Markdown渲染、分类标签、全文搜索的私有化知识发布系统... 目录引言:为什么要自建知识博客一、技术选型:极简主义开发栈二、系统架构设计三、核心代码实现(分步解析

Java并发编程必备之Synchronized关键字深入解析

《Java并发编程必备之Synchronized关键字深入解析》本文我们深入探索了Java中的Synchronized关键字,包括其互斥性和可重入性的特性,文章详细介绍了Synchronized的三种... 目录一、前言二、Synchronized关键字2.1 Synchronized的特性1. 互斥2.

一文带你深入了解Python中的GeneratorExit异常处理

《一文带你深入了解Python中的GeneratorExit异常处理》GeneratorExit是Python内置的异常,当生成器或协程被强制关闭时,Python解释器会向其发送这个异常,下面我们来看... 目录GeneratorExit:协程世界的死亡通知书什么是GeneratorExit实际中的问题案例

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

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

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

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