深入理解计算机系统(3.2)------程序编码以及数据格式

  在进行本章的讲解之前,我们先说明讲解的机器语言型号。上一篇汇编语言和机器语言我们讲过,机器语言是直接面向处理器(Processor:CPU)的程序设计语言,但是每一种这样的微处理器(CPU)由于硬件设计和内部结构的不同,所以每一种微处理器都有自己的机器指令集,也就是机器语言。而汇编语言是便于记忆的机器语言。本系列博客将会介绍两种相关的机器语言:Intel IA32 和 x86-64。前者是当今大多数计算机的主导语言,而后者是在 64 位机器上运行的扩展,我们先从 Intel IA32开始。

1、机器级代码

  前面我们就说过,计算机系统使用了多种不同的抽象,利用更简单的抽象模型来隐藏实现的细节。对于机器级编程来说,有两种抽象特别重要:

  ①、第一种是将机器级程序的格式和行为定义为指令集体系结构(Instruction set architecture ,ISA),它定义了处理器状态、指令的格式,以及每条指令对状态的影响。大多数 ISA,包括 Intel IA32 和 x86-64,将程序的行为描述成好像每条指令是按顺序执行的,即一条指令结束后,下一条指令开始。处理器的硬件远比描述的精细复杂,它们并发的执行许多指令,但是可以采取措施保证整体行为与 ISA 指定的顺序执行完全一致。

  ②、第二种是机器程序使用的存储器地址是虚拟地址,提供的存储器模型看上去是一个非常大的字节数组。存储器系统的实际实现是将多个硬件存储器和操作系统软件组合起来。

  在整个编译过程中,编译器会完成大部分工作,将把用 C 语言提供的相对比较抽象的执行模型表示的程序转化成处理器执行的基本指令,也就是汇编语言,汇编语言在被汇编器转化成机器语言,然后计算机去执行。汇编语言也就是具有更好的可读性的机器语言,所以能够理解汇编代码以及它与原始 C 代码的关系,是理解计算机如何执行程序的关键步骤。

  我们在写 C 程序时,处理器的状态都是隐藏的,即我们编码不用去直接操作处理器。但是在汇编语言中,如下的几个处理器状态是可见的:

  一、程序计数器(在 IA32 中通常称为 PC,用 %eip 表示):指示将要执行的下一条指令在存储器中的地址。

  二、整数寄存器文件:包含8个命名的位置,可以存储一些地址或者整数的数据。有的用来记录某些重要的程序状态,有的则用来保存临时数据。

  三、条件码寄存器:保存最近执行的算数或逻辑指令的状态信息,它们用来实现控制或数据流中的条件变化,比如用来实现 if 和 while 语句。

  四、浮点寄存器:存储浮点数。

注意:C 语言提供的模型可以在存储器中声明和分配各种数据类型的对象。但是实际上机器代码则只是简单的将存储器看成是一个很大的、按字节寻址的数组。

  汇编代码不区分有符号或者无符号整数,不区分各种类型的指针。甚至不区分指针和整数。

2、程序存储器

程序存储器包含程序的可执行机器代码,操作系统需要的一些信息,用来管理过程调用和返回的运行时栈,以及用户分配的存储器块。

  程序存储器用虚拟地址来寻址,在任意给定的时刻,只认为有限的一部分虚拟地址是合法的。操作系统则负责管理虚拟地址空间,将虚拟地址翻译成实际处理器存储器(processor memory)中的物理地址。

3、程序示例

  如下这是一段 C 程序代码 hello.c:

#include <stdio.h>

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

  然后执行如下命令生成汇编程序

gcc -O1 -S hello.c

  -O1是优化选项,少优化->多优化:

  O0 -->> O1 -->> O2 -->> O3

  -O0表示没有优化,-O1为缺省值,-O3优化级别最高

  生成的汇编程序 hello.s

        .file   "hello.c"
        .text
.globl sum
        .type   sum, @function  //定义全局函数sum
sum:
.LFB12:
        .cfi_startproc
        leal    (%rsi,%rdi), %eax //把寄存器%rsi和寄存器%rdi的值的地址装入eax中,即&(rsi+rdi)=eax
        addl    %eax, accum(%rip) //把寄存器%eax和寄存器%rip的值相加,并存放到 %rip中
        ret
        .cfi_endproc
.LFE12:
        .size   sum, .-sum
.globl main   //主函数main
        .type   main, @function
main:
.LFB11:
        .cfi_startproc
        movl    $3, %esi //将数据3复制到%esi寄存器
        movl    $1, %edi
        movl    $0, %eax
        call    sum  //将 sum 指令的地址压入到栈中,也就是下一条指令执行调用 sum 函数
        rep
        ret
        .cfi_endproc
.LFE11:
        .size   main, .-main
.globl accum  //定义全局变量accum
        .bss
        .align 4
        .type   accum, @object
        .size   accum, 4
accum:
        .zero   4
        .ident  "GCC: (GNU) 4.4.7 20120313 (Red Hat 4.4.7-18)"
        .section        .note.GNU-stack,"",@progbits

注意:所有以 ‘.’ 开头的行都是指导汇编器和链接器的命令,我们通常可以忽略这些行。

  现在这些汇编指令大家可以不用完全理解,后面会详细进行讲解。

4、数据格式

  由于计算机是由16位体系结构扩展为32位体系结构的,Intel 用术语 “字”(word) 表示16位数据类型,因此 32 位表示 “双字”(double words),64 位数称为“四字”(quad words).

  前面的汇编代码我们可以看到所有的汇编指令都带有字母 l,比如movl、addl、subl、pushl等等,这个l的后缀其实就是表示的数据格式,表示我们操作的是32位的数值。

  下面我们看一下 C 语言基本数据类型对应的 IA32 表示:

  上面的图示很好理解,比如mov指令,它是一个数据传送的指令,那么movb就代表传送一个字节的数据,movw就代表传送两个字节的数据,而movl就代表传送四个字节的数据。需要注意的是,long long int在IA32架构中是不支持这种数据格式的。而且汇编代码使用后缀 “l” 来表示 4 字节整数和8字节双精度浮点数,这不会产生歧义,因为浮点数使用的是一组完全不同的指令和寄存器。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏QQ会员技术团队的专栏

有没有人告诉你—写时拷贝的真相

作者简介:梁少华,QQ动漫后台开发,腾讯高级工程师。从事后台开发4年多,参与过QQ秀、手Q红点系统、手Q游戏公会、QQ动漫等项目,有丰富的后台架构经验,擅长海...

22810
来自专栏博岩Java大讲堂

关于Java泛型"擦除"的一点思考

2625
来自专栏coderhuo

可怕的extern关键字一、不利之处二、例子三、分析四、正确做法

如果函数原型改变的话,每个extern声明的地方都要改一遍。 如果有地方没改到呢? 我们通过一个例子来看下悲剧是怎么发生的。

742
来自专栏思考的代码世界

Python编程从入门到实践之用户输入|第7天

函数input()让程序暂停运行,等待用户输入一些文本。获取用户输入后,Python将其存储在 一个变量中,以方便你使用。

3098
来自专栏北京马哥教育

Python2.x与3​​.x版本区别

? 文 | 豌豆 来源 | 菜鸟教程 Python的3.0版本,常被称为Python 3000,或简称Py3k。相对于Python的早期版本,这是一个较...

3136
来自专栏知识分享

C中的预编译宏定义

文章来自 http://www.uml.org.cn/c++/200902104.asp 在将一个C源程序转换为可执行程序的过程中, 编译预处理是最初的步骤. ...

2354
来自专栏Java帮帮-微信公众号-技术文章全总结

Java基础-day01-基础题

1. 简述java语言,具有哪些特性? (1).java语言是简单的 java语言是和c++语言类似的,其次java中丢弃了c++中一些难理解的特性,比如运算符...

2534
来自专栏xingoo, 一个梦想做发明家的程序员

汇编语言 手记3

从读写属性上存储器分为:随机存储器RAM和只读存储器ROM 从功能和连接上分类: 随机存储器RAM 装有BIOS的ROM 接口卡上的RAM ? 上述的存储器物理...

17610
来自专栏C/C++基础

C++中cin的详细用法

cin是C++编程语言中的标准输入流对象,即istream类的对象。cin主要用于从标准输入读取数据,这里的标准输入,指的是终端的键盘。此外,cout是流的对象...

913
来自专栏小樱的经验随笔

汇编语言第三版答案(王爽)

汇编语言答案(王爽)  此文只是用来存个档,不喜勿喷 检测点1.1 (1)1个CPU的寻址能力为8KB,那么它的地址总线的宽度为 13位。 (2)1KB的存储器...

35011

扫码关注云+社区