深入理解计算机系统(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 条评论
登录 后参与评论

相关文章

来自专栏LuckQI

Linux实战下~zookeeper集群与solr集群

1695
来自专栏北京马哥教育

nginx 总结

1. Nginx无法启动解决方法 在查看到logs中报了如下错误时: 0.0.0.0:80 failed (10013: An attempt was made...

3679
来自专栏SpringBoot 核心技术

第十七章:使用SpringSecurity让SpringBoot项目更安全

1824
来自专栏BestSDK

是不是Bash编程老司机,看完这10条细节就知道了

Bash,作为大部分 Linux 发行版的出厂预设 Shell,因其晦涩难懂的语法设置,以及需要特别留心的编程细节,几乎成为 Linux 区别于其他操作系统的代...

3397
来自专栏十月梦想

ES6基础语法之模块化

比如我希望在a.js中使用b.js的变量或者函数,那么ES6就中模块化就帮到咱们啦!

814
来自专栏企鹅号快讯

全文搜索引擎Elasticsearch入门教程

全文搜索属于最常见的需求,开源的Elasticsearch(以下简称 Elastic)是目前全文搜索引擎的首选。 它可以快速地储存、搜索和分析海量数据。维基百科...

1827
来自专栏深度学习之tensorflow实战篇

python如何保存矩阵,保存matrix,保存numpy.ndarray

问题:如何将array保存到txt文件中?如何将存到txt文件中的数据读出为ndarray类型?python如何保存矩阵,保存matrix,保存numpy.nd...

1.7K6
来自专栏PHP实战技术

你知道这种开发模式能更好的帮你排错吗?

很多时候我们在开发一个项目的时候写着写着sql语句报错了?(这里多指使用框架开发,当然也有原声sql语句),之后有时候会扎耳挠腮,看来看去都感觉自己的sql语句...

826
来自专栏Python小屋

使用with关键字让你的Python代码更加Pythonic

首先解释一下上一篇文章Python科学计算扩展库numpy中的广播运算中最后的小题目,该题目答案是一个元组(True, 5),原因在于Python中的等号=虽然...

3428
来自专栏破晓之歌

Python中os与sys两模块的区别 原

os: This module provides a portable way of using operating system dependent func...

441

扫码关注云+社区