[编程语言][汇编语言]计算机与汇编语言

1、什么是计算机 ?


简单的说计算机是这样一种机器:当咱们把一些信息输入到计算机中后,它通过计算后告诉咱们信息处理的结果。计算机可以完成不少任务,甚至有些任务是极其复杂的。但计算机在完成这些任务的时候,最终实际上是作数值的计算。因此咱们叫这种机器为“计算”机。计算机是为了方便人类而产生的,理论上来讲计算机能完成的任务人类也是可以完成的。因此,计算机不少方面其实和人类很像。linux

一、CPU、内存与硬盘

咱们去电脑城买电脑的时候,卖场的工做人员一般会介绍一台电脑的配置如何。笔者的笔记本就是i7CPU、8G内存以及1TB硬盘的。什么是CPU?什么是内存?还有,什么是硬盘?这些问题可能对一个熟悉电脑的人来讲小菜一碟,但对某些刚刚接触电脑的人来讲仍是比较困难的。下面咱们打开电脑的外壳,看看这些东西究竟是什么样子的。web

CPU实际上是个不到半个手掌大小的方块,它的周围有一些引脚。人们所说的内存实际上是一个长方形的条形电路板,也就是内存条。而硬盘是一个铁盒子,咱们没法打开这个铁盒子深刻地了解硬盘的内部结构。编程

以上是咱们感官的认识,下面咱们来了解它们的功能。小程序

CPU(Central Processing Unit,中文名称是中央处理器)是咱们计算机中最为重要的一个部件。它完成绝大多数的运算,比如是咱们的大脑。ruby

内存(Memory)也被称为内存储器,其做用是用于暂时存放CPU中的运算数据。内存就像是咱们手头的草稿纸。咱们的大脑不能计算过于复杂的计算,不然就须要借助草稿纸来暂时记下中间的计算结果。CPU其实和咱们的大脑是同样的,只能计算必定范围内的运算。对于稍稍复杂一些运算,CPU就须要暂时把中间结果保存在内存中。svg

硬盘(Hard Disk)主要用于保存数据。咱们借助草稿纸获得复杂运算的结果后,为了便于老师批阅,一般是把运算结果填写的练习本上的。CPU能够把运算结果保存在其内部,固然也能够保存在内存中。因为电子硬件的特性,计算机断电之后CPU和内存中保存的数据会彻底丢失。硬盘的电子硬件特性决定了它即使是断电,也不会丢失数据。这样咱们就能够把运算结果保存在硬盘上。学习

二、寄存器

什么!寄存器又是个什么东西?atom

咱们的大脑在计算的时候,其实计算发生在大脑内部某个空间的。咱们能够经过平时的训练来扩充这个计算空间,这样咱们就能够计算更加复杂的运算。说到底,咱们的大脑内部也有一个相似草稿纸的空间。对于1+2这种简单运算,咱们的大脑彻底能够立刻获得结果。对于17×8这样的运算,咱们也能够比较容易地获得结果:首先计算7×8获得56,而后计算1×8的到8,而后再把以前运算结果的进位5和此次获得的8相加获得13,最终的结果就是136。为了获得了136这个最终结果,咱们的大脑产生了5六、8和13这些中间结果,而这些中间结果实际上是保存在咱们大脑内部的“草稿纸”中的。看似还比较容易,但若是咱们想计算1234×5678呢?除了个别人,若是不凭借手边的草稿纸,要获得正确结果恐怕就不是那么容易了。spa

咱们的大脑如此,计算机又是怎样的呢?一些高手口中说的“32位计算机”、“64位计算机”,咱们能够理解为一台计算机一次运算可以处理的数值的最大长度是32位或是64位的。在这里,咱们先不探究32位或64位是怎样的一个长度,只须要了解计算机的运算能力不是万能的,超过必定的范围计算机就无能为力了。计算机的运算功能是由CPU来完成的,因此CPU的运算能力就是计算机的运算能力。在计算机内部也是有“草稿纸”的,这些“草稿纸”就是寄存器(register)。寄存器和内存同样,也是能够保存数值,可是寄存器可以存储数据的能力远远小于内存。操作系统

组成寄存器和内存的电子元件是不相同的。寄存器的速度比较快,但制形成本比较高;内存的速度比较慢,但制形成本比较低。若是咱们只使用内存,那么速度更快的CPU大部分时间是在等待内存;若是咱们只使用寄存器,那么一台电脑将不是咱们这些普通人可以买得起的。为了得到必定的运算速度,也为了可以下降成本。工程师决定在CPU内部内置一些寄存器来完成CPU的运算功能,同时CPU也须要在内存中保存其运算结果。

在这里咱们总结一下:计算机的运算功能是依靠CPU完成的;在运算过程当中要CPU要用到其内部的寄存器;而当寄存器没法知足计算的须要时,咱们能够把中间结果暂时保存在内存中;当计算完成而且须要永久保存其结果时,咱们就把它们保存在硬盘里。

2、机器语言与汇编语言


一、机器语言

咱们在学习英语以前,咱们听不懂美国人到底说了写什么。一样的,咱们计算机也是不能理解“1+2=?”这种极其简单的计算问题。计算机只能理解机器语言,也就是二进制指令,二进制就是计算机世界中的通用语言。“1011 1000 0000 0001 0000 0000 1000 0011 1100 0000 0000 0010”这段二进制指令的含义就是让CPU计算1+2的运算。若是这么多的1和0中写错一个数字,就可能变成另外的含义了。为了方便阅读,咱们能够把这段二进制指令以16进制数表示:“B80100 83C002”。但是即使如此,机器语言对咱们来讲仍是很难阅读。

二、汇编语言

咱们能够理解汉语,能够理解数学算式。经过学习,咱们还能够理解英语、法语。固然,经过学习以及自身超强的记忆力,咱们也可以理解机器语言。但这就背离了咱们发明计算机的初衷,为了方便运算,咱们居然要花巨大的时间和精力来学习以及记忆一门世界上最难懂的语言。

世界上谁会这么作呢?但不这么作又该如何让计算机明白咱们的意图呢?聪明的前辈们发明了汇编语言(assembly language)这种中间语言。借助于汇编语言,咱们能够很容易地将本身的意图转换为汇编语言。而汇编语言和机器语言一一对应的关系,又决定了它们之间能够很是容易地进行转换。用汇编语言表达以前机器语言就是:

mov $1, %ax
    add $2, %ax

嗯,有点意思了。mov很像move,add是加法运算的英文单词。只是为何在1和2的前面有个美圆符号$?%ax又是个什么东西?未知的东西好像有些多了,但别着急,这些疑问咱们会一一谈到了。

前面的代码片断中的mov、add是助记符(mnemonic),%ax是寄存器。每一行为一条指令。第一条指令的意思是把1搬进(move)ax寄存器,第二条指令的意思是把ax寄存器的数值加上(add)2。之后咱们会发现,“B80100”其实就是mov $1, %ax,而”83C002“就是add $2, %ax。看看,有了汇编语言作翻译,咱们是否是很容易就让计算机知道咱们到底想让它作些什么事情了。

汇编语言就是把机器指令用人们能比较理解的简短的助记符表示出来的语言。这里要提一下的是,汇编语言虽然与机器语言一一对应,但却有多种不一样的表达格式。最经常使用的汇编语言格式为“Intel汇编格式”和“AT&T汇编格式”,而上面的代码片断就是“AT&T汇编格式”的。不过它们之间的差别很小,也很容易掌握,并且它们也很容易相互转换,彻底不会影响到咱们学习汇编语言自己。

3、第一个汇编程序


前面说了这么多文科知识,咱们已经很枯燥了。下面就开始咱们本身的第一个汇编程序。不过,咱们首先须要知道获得最终的成品以前,咱们都须要作哪些事情。

第一步,咱们在脑海里把本身的意图转换成一条条的汇编指令后,就能够经过键盘输入到计算机里而且保存为一个文本文件。这个文本文件咱们称为源代码文件(source file),简称源文件。其扩展名为“s”,好比“boot.s”文件。源文件就是咱们起点。

第二步,咱们经过汇编器(assembler)把源代码文件转换成目标文件(object file)。此时,咱们获得的目标文件是一个包含二进制指令序列的文件,而这些二进制指令序列就是计算机可以理解的机器语言。这个过程称为汇编。每个源文件汇编后会获得一个目标文件,固然N个源文件汇编后获得的就是N个目标文件。也就是说,汇编的过程是以源文件为单位进行的,源文件与目标文件是一一对应的。虽然如此,咱们的工做尚未结束。

第三步,咱们须要经过把连接器(linker)把目标文件连接成在相应环境下能够执行的文件,便可执行文件(executable file)。这个过程咱们称为连接。若是有多个目标文件,咱们能够把它们连接成一个可执行文件。也就是说,在连接过程当中,目标文件与可执行文件是多对一的关系。好了,到这里,咱们已经获得了咱们想要的成品了。

以上这三步是咱们必须完成的。固然了,确定有朋友会问到:“第二部获得的不是已是机器语言了吗,为何还须要第三步呢?”说的没错,第二部中的目标文件确实已经包含机器语言了。不过,对于咱们练习的小程序,用一个源文件就能够了。可是,若是一个大型项目,甚至是Linux这样的“巨型”项目,就须要分红多个模块,分别进行编辑、汇编,而后再连接成一个可执行文件。另外,目标文件缺乏程序加载所必须的信息,而这些信息必须由连接器补充完整。

好像又说了一堆枯燥的文科知识,不过一旦了解了上面的这些内容将有助于咱们完成本身的第一个汇编程序。为了下降难度,尽量少地讲解新知识,咱们来编写一个Linux下的“hello, world!”汇编程序。

一、编辑源文件

将下面的源代码编辑在一个文本文件中,而且文件名保存为“hello.s”。

/* * File: ch1/hello.s * Author: HuoYun * Created on: 2015/11/30 15:15:47 * Modified on: 2015/11/30 15:15:47 * Describe: * 本程序为了演示汇编以及连接的过程。 * Compile command: * as hello.s -o hello.o * ld hello.o -o hello * Usage: hello */

    .section .text
    .global _start
_start:
    # 调用系统调用,在屏幕上显示信息。
    mov $4, %eax
    mov $1, %ebx
    mov $message, %ecx
    mov $len, %edx
    int $0x80

    # 调用系统调用,退出程序。
    mov $1, %eax
    mov $0, %ebx
    int $0x80

    .section .data
message:
    .ascii "hello, world!\n"
    len = .-message

咱们稍后来说述如何编辑这个汇编源代码。

二、汇编

咱们进入保存源代码文件的目录,而后执行下面的命令:

$ as hello.s -o hello.o

若是咱们编辑的源代码没有问题,就会获得boot.o这个目标文件。但若是咱们在编辑的过程当中不当心输错了一个地方,汇编器可能会提示错误。这个时候就须要咱们仔细地检查而且更正了。

三、连接

咱们继续执行下面的命令:

$ ld hello.o -o hello

这个时候就会的到boot这个可执行文件。固然若是因为某种缘由,连接器提示发生了错误,一样的,咱们依然要查找并更正错误。

四、执行

当咱们正确地获得了可执行文件boot后,咱们就能够执行下面的命令来运行咱们的第一个汇编程序了:

$ ./hello

若是获得下面的运行结果,说明咱们以前的步骤都没有错误。之后咱们编译程序时,多数状况下都会使用以前的步骤。因此,之后咱们再也不详细讲述每一个程序的编译过程,而是将编译命令记录在源文件的注释中。

hello, world!

至此,是否有些兴奋和成就感呢?编写程序代码是个枯燥的过程。有的时候咱们洋洋洒洒地写了成百上千行代码,可就是没法编译经过,或者编译经过了却没有获得咱们预期的效果。这时,咱们只能慢慢地寻找bug。找到了一个bug,但发现还有另外的bug。是的,咱们就是在编辑、找错、再编辑、再找错这种屡次重复的过程当中完善程序的,最终咱们获得的是一个咱们指望的程序。编写程序是乏味的,产生的错误是让人厌烦的。没有一我的一开始就能写出正确无误的代码,大量的编程会下降本身的失误率。学习编程,最忌讳的就是眼高手低,由于事情常常不是你想象的那样。书中剩余的代码示例均已在Ubuntu linux 15.04操做系统下编译完成,咱们尽量地保证代码的正确性,让你们可以获得同样的结果。看到这里,若是你仅仅只是经过以前的描述直到了“hello, world!”汇编程序的容许结果,那请你亲自动手敲敲键盘,看看是否可以获得和我同样的结果。

“纸上得来终觉浅,绝知此事要躬行。”我以这一句做为本章的结束,再一次地说明亲自编写代码的重要性。