程序的机器级表示

计算机能读懂是机器代码(machine code)—— 用字节序列编码的低级操作 —— 也就是0和1。编译器基于编程语言的规则、目标机器的指令集和操作系统的规则,经过一系列阶段产生机器代码。由于机器语言全是由0和1组成的,所以对于编程人员来说编写机器代码十分困难,也不容易学习。汇编语言(assembly language)就是机器语言的可读形式,学习汇编语言有很多的好处。

An assembly (or assembler) language,[1] often abbreviated asm, is a low-level programming language for a computer, or other programmable device, in which there is a very strong (generally one-to-one) correspondence between the language and the architecture's machine code instructions.

Machine code or machine language is a set of instructions executed directly by a computer's central processing unit (CPU). Each instruction performs a very specific task, such as a load, a jump, or an ALU operation on a unit of data in a CPU register or memory. Every program directly executed by a CPU is made up of a series of such instructions......Numerical machine code (i.e., not assembly code) may be regarded as the lowest-level representation of a compiled or assembled computer program or as a primitive and hardware-dependent programming language.

我们在用高级语言编程时(例如C语言),高级语言本身提供的抽象级别比较高,因此有“程序设计”的概念,方便我们编程。大多数情况下,抽象级别比较高,工作效率就会提高,也更高效可靠。高级语言相较汇编语言的一个最大优点是:用高级语言编写的程序可以在很多不同的机器上运行,而汇编代码与硬件本身有关联,例如指令集不同那么汇编代码就不同。

Each assembly language is specific to a particular computer architecture. In contrast, most high-level programming languages are generally portable across multiple architectures but require interpreting or compiling.

那我们为什么要花时间学习汇编和机器代码呢?

纵观计算机的发展历程,有一个很重要的观点就是用分层的思路来构造整个系统,每个下层都对其上层隐藏本层的细节。这个抽象原理对于理解计算机很多方面的知识都有帮助,例如网络四层模型就是一个很典型的例子。扯回编程语言这块,由于语言是“高级”的,自然屏蔽了很多机器级实现过程的细节。例如,如果只是单纯的学习C语言,那对于寄存器、程序计数器、系统的栈、缓冲区溢出等知识点没有办法很好的理解,因为它们对于C语言程序员来说都是被隐藏的处理器状态。C语言的指针通常是引起错误和不理解的来源,但在汇编中访存、取地址都是很常见的操作。学习汇编,能让你学到一些在C语言学习中由于粒度不够细而学不到的东西。除此之外,读完这一章之后,你还能领略到C语言的伟大之处。

抽象和分层是很重要的思想,但对于严谨的学习者来说,并不意味只要懂得抽象原理就足够了。能够阅读和理解汇编代码是仍是一项很重要的技能。精通细节是理解更深和更基本概念的先决条件。随着时间的推移,对于汇编代码的学习需求也有变化,我们现在不需要直接用汇编语言编写程序,只需要能够阅读并理解编译器产生的代码就可以了。

处理器执行的指令,被编码成二进制形式。一个处理器支持的指令及其编码集称为指令集体系结构(Instruction-Set Architecture, ISA)。

An instruction set, with its instruction set architecture (ISA), is the interface between a computer's software and its hardware, and thereby enables the independent development of these two computing realms; it defines the valid instructions that a machine may execute.

ISA就是机器级程序的格式和行为,这种类型的指令,每一条完成的工作都是非常基本的,例如把两个数相加。因此学习汇编代码就有助于我们了解编译器在把C代码转换成机器代码时所做的转换。相对于C代码,编译器重新排列执行顺序、消除不必要的计算和变量、用基本的、快速的操作代替慢速操作,甚至用迭代替代了递归。学习逆向工程(reverse engineering),来研究高级语言,甚至研究系统创建的过程。(好书啊好书)

可以说,汇编语言很有其特殊性,高级语言的一些抽象的概念,都被归一和具象化了,C语言的指针,在汇编的世界里简直不值一提,因为地址,对地址的引用再平常不过了。另一方面,内存,缓存,寄存器,CPU,又构成了一个奇妙的世界,它们屏息恭立,静待主公的每一条喻令。

我不相信,一个人用纯手工一条条指令去雕琢他的程序,用手指感受计算机的呼吸时会无动于衷。……

作者:匿名用户

链接:https://www.zhihu.com/question/23088538/answer/23631875

来源:知乎

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Architecture

汇编内容比较多,部分涉及具体细节的内容有删减,仅做简单介绍,详情请参考书本。

处理器

Intel 处理器系列俗称 x86,经历了一个长期的、不断发展过程。1965年,Intel公司的创始人Gordon Moore,根据当时的芯片技术(那时他们能够在一个芯片上制造大概有64个晶体管的电路)做出推断:未来10年内,芯片上晶体管数量每年都能翻一番。这个预测就是著名的摩尔定律。正如事实所证明的那样,他的预测有点乐观,还有点短视。在超过45年中,半导体工业一直能够让晶体管数量每18个月翻一番。

指令集

就像上面讲到的那样,计算机系统用到了许多不同形式的抽象,利用简单的模型隐藏各种细节。其中之一就是指令集,也就是ISA。在学习汇编时,ISA模型给人的感觉好像看上去应该是顺序指令执行,也就是先取出一条指令,等到它执行完毕,再开始下一条。然而处理器的实际工作方式远比这种模式精细复杂,因为处理器可以并发执行多条指令,因此需要做很多的额外工作。在计算机科学中,用巧妙的方法在提高性能,并完成更多更复杂的功能的同时,又同时保持了一个更简单、更抽象的底层模型功能,这种想法是无处不在的。

IA32和x86-64就是两种ISA,也是着重介绍的重点。这两者是当今大多数计算机的主导语言,后者是前者在64位机器上的扩展版本。

IA32的机器代码和原始C代码差别非常大。下面这些对于C语言程序员隐藏的概念在IA32中是可见的:

  • 程序计数器(Program Counter,简称PC)

    • 用来指示将要执行的下一条指令的地址。
  • 整数寄存器文件(Register File)

    • 非常重要的概念。用来存储地址(对应C语言的指针)或者整数数据,或者用来记录程序的状态,或者保持临时数据,例如局部变量、函数的返回值等等。
  • 条件码(Condition Code)

    • 保持最近执行过的算术or逻辑指令的状态信息。用来实现控制或数据流条件变化。(对应C语言的if等等)

对于机器级编程来说,有第另一种极为重要的抽象,那就是把存储器地址抽象成虚拟地址(Virtual Memory),提供的模型,让机器代码只是简单地把存储器看成是一个非常大的字节数组。所以,C语言中的各种数据类型,和自定义数据类型(struct),在机器代码中都是用一组字节来表示。汇编代码不区分有符号、无符号常数,不区分各种类型的指针,甚至不区分指针和整数。

  • 程序存储器(Program Memory)

    • 包含程序的可执行机器代码,操作系统需要的一些信息,例如支持系统运行的栈,以及为用户分配的存储器块(例如malloc)。程序存储器用虚拟地址来寻址(Addressable),操作系统负责管理虚拟地址空间,将虚拟地址翻译成实际处理器中的物理地址。

处理器能够执行的操作其实是非常有限的,简单来说只有三种:存取数据、计算和传输控制。存取数据是在内存和寄存器之间传输数据,进行计算则是对寄存器或者内存中的数据执行算术运算,传输控制主要指非条件跳转和条件分支。

代码示例

下面给出一个C语言代码以及其汇编代码中的一些关键行。一开始看不懂没关系,我们只需要对其有一个感性认知。

int accum = 0;

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

对应的由编译器产生的汇编代码(部分行)如下:

sum:
pushl %ebp
movl %esp, %ebp
movl 12(%ebp), %eax
addl 8(%ebp), %eax
addl %eax, accum
popl %ebp
ret

观察汇编语言代码,我们可以看到第一个字符串就是操作符,后面可能跟着几个操作数,由逗号分开。操作符就被唯一解码成对应的机器指令,但汇编语言的操作符是能让我们看得懂的。

在汇编代码中,每一行都代表一条机器指令。例如pushl就是把寄存器%ebp的内容压入栈。这段代码已经不存在任何关于局部变量名和数据类型的信息(除了全局变量accum,还没有确定会放在哪里)。

如果使用汇编器(Assembler)将汇编语言翻译为机器语言,机器语言是二进制格式,无法直接查看。但其中有一列17个字节的字节序列:

55 89 e5 8b 45 0c 03 45 08 01 05 00 00 00 00 5d c3

这就是上面列出的汇编指令对应的机器代码。我们可以看出来,机器实际执行的就是对一系列指令进行编码的字节序列。机器对于我们写的C语言代码可以说是一无所知的。


See Also

【CSAPP笔记】4. 汇编语言——基础知识的更多相关文章

  1. iOS 阶段学习第11天笔记(OC基础知识)

    iOS学习(OC语言)知识点整理 一.OC基础知识 1)#import  用于导入头文件,预处理阶段加载引用,只加载一次. 2)OC 依赖于Foundation框架下的头文件Foundation.h, ...

  2. ios开发学习笔记001-C语言基础知识

    先来学习一下C语言基础知识,总结如下: 在xcode下编写代码. 1.编写代码 2.编译:cc –c 文件名.c 编译成功会生成一个 .o的目标文件 3.链接:把目标文件.o和系统自带的库合并在一起, ...

  3. Spring笔记01(基础知识)

    1.基础知识 01.Spring:轻量级Java EE开源框架,它是由Rod Johnson为了解决企业应用程序开发的复杂性而创建. 02.目标:实现一个全方位的整合框架,实现“一站式”的企业应用开发 ...

  4. [terry笔记]data guard基础知识

    如下介绍了data guard的基础知识,整理自网络: Data Gurad 通过冗余数据来提供数据保护,Data Gurad 通过日志同步机制保证冗余数据和主数据之前的同步,这种同步可以是实时,延时 ...

  5. Java 8实战之读书笔记二:基础知识

    好记性不如烂笔头,整理一些个人觉得比较重要的东西. 一.基础知识 第1章 为什么要关心Java 8 Java 8提供了一个新的API(称为"流", Stream),它支持许多处理数 ...

  6. MySQL必知必会笔记——查询的基础知识

    查询基础知识 第七章 数据过滤 组合where子句 MySQL允许给出多个WHERE子句.这些子 句可以两种方式使用:以AND子句的方式或OR子句的方式使用. AND操作符 可使用AND操作符给WHE ...

  7. FreeRTOS学习笔记——FreeRTOS 任务基础知识

    RTOS 系统的核心就是任务管理,FreeRTOS 也不例外,而且大多数学习RTOS 系统的工程师或者学生主要就是为了使用RTOS 的多任务处理功能,初步上手RTOS 系统首先必须掌握的也是任务的创建 ...

  8. 《Python基础教程(第二版)》学习笔记 -> 第一章 基础知识

    写笔记的原因:书也看了一遍,视频也看了,但总是感觉效果不好,一段时间忘记了,再看又觉得有心无力,都是PDF的书籍,打开了就没有心情了,上班一天了,回家看这些东西,真的没多大精力了,所以,我觉得还是把p ...

  9. [Python笔记]第一篇:基础知识

    本篇主要内容有:什么是python.如何安装python.py解释器解释过程.字符集转换知识.传参.流程控制 初识Python 一.什么是Python Python是一种面向对象.解释型计算机程序设计 ...

随机推荐

  1. 纯SVG实现的Loading动画,拿走不谢

    转载自:https://blog.csdn.net/wo_shi_ma_nong/article/details/88833828 话不多说,直接上代码. ( 到这里看效果: http://www.v ...

  2. day 86 Vue学习之五DIY脚手架、webpack使用、vue-cli的使用、element-ui

      本节目录 一 vue获取原生DOM的方式 二 DIY脚手架 三 vue-cli脚手架的使用 四 webpack创建项目的玩法 五 element-ui的使用 六 xxx 七 xxx 八 xxx 一 ...

  3. devise的使用

    ~ 在gemfile中加入 gem 'devise' ~ 终端输入 $ bundle install $ rails generate devise:install ~ 确保登录之后能正常跳转 在co ...

  4. Web安全0002 - SQL注入 - 注入流程

    注:本文是学习网易Web安全进阶课的笔记,特此声明. 一.信息搜集 — 数据库类型 - 报错信息.特有语句— 数据库版本(@@version,$version)— 数据库用户— 判断数据库权限 二.数 ...

  5. go基础1:Hello world与变量声明

    一.go语言版hello world package main # 指明文件属于main包 main包使用后来定义一个独立的可执行程序,而不是库main包中的main函数是特殊的,不管在什么程序中,m ...

  6. Autovacuum 的运行限制

    磨砺技术珠矶,践行数据之道,追求卓越价值 回到上一级页面:PostgreSQL内部结构与源代码研究索引页    回到顶级页面:PostgreSQL索引页 作者:高健@博客园  luckyjackgao ...

  7. Keil 中的Code,RO-data,RW-data,ZI-data

    我们开发项目中,编译.链接完成后,都要看一下RAM和Flash的利用情况,尤其在有bootloader的情况下,确保自己的单片机选型能符合项目的要求.其中ARM程序的组成,是很容易混淆的.此处所说的“ ...

  8. python通讯录系统

    ---恢复内容开始--- 对于一般的通讯录系统,主要有两个参数:姓名和电话号码,所以可以利用python编程里面的字典来进行建立之间的所属关系, 可以利用以下代码简单实现: print('|--- 欢 ...

  9. iFIERO - (一) 宇宙大战 SPACE BATTLE — 场景SCENE、SpriteKit精灵、PARTICLE粒子及背景音乐

    开始游戏教程前,首先介绍一下SpriteKit是什么?SpriteKit提供了一个图形渲染和动画的基础结构,你可以使用它让任意类型的纹理图片或者精灵动起来.SpriteKit使用渲染循环,利用图形硬件 ...

  10. 使用python实现解析二元一次方程

    二元一次函数的实现 import cmathimport mathimport sys 这里导入cmath包是在后面用来处理复数的情况导入math使用来处理 平方 根号等的运算而导入sys的意义是为了 ...