汇编3-计算机程序是如何运行的

1.引言

前面我们已经提到过寄存器以及cpu的一些基本工作原理,那么在一个程序运行时,寄存器、cpu、内存等都是担任着怎样的角色呢?我们学习汇编的目的就是为了更好的了解计算机底层是如何工作的,所以我觉得有必要在学习汇编指令之前谈一谈一个计算机程序是如何在计算机上运行起来的。本文会尽可能详细地描述从写一个源程序到它被执行的过程中都计算机内部都发生了哪些事情。

2.计算机是如何工作的呢?

从宏观上来讲,计算机是给人提供服务的,提供方便的。人类想要告诉计算机一些东西,通过什么途径呢?输入设备(如鼠标,键盘,等等);计算机想要告诉人类一些东西也会通过输出设备(如屏幕,打印机,等等);计算机在读入用户的数据后,会对数据进行计算加工,这时会用到运算器;而运算的结果可能不会立刻输出给用户,而是暂存起来,这时计算机会用到存储器;而不论是运算的过程还是存储的过程,逻辑流程的安排,步骤序列则需要控制器。人类与计算机的交互,就是通过这五大部件的通力合作展开的,或者说,计算机的工作就是通过这五大部件实现的。

现代计算机基本上都是遵照冯·诺依曼体系结构进行设计、生产建造的。而该体系结构的核心概念就是存储程序计算机,即存储在计算机中的程序的执行,是通过一条条指令的执行而实现的(三阶段:取指令、指令译码、执行指令)。这是我们从微观的角度来看待计算机是如何工作的。
————-摘自计算机是如何工作的

3.源程序到目标程序的过程

1.源程序的存储

我们就拿大家最熟悉的程序hello world来描述这个过程。

这里写图片描述
相信读者知道计算机是以0、1串来表示数据的,所以我们的hello world程序在计算机中也是以这样的形式存在的,8个位为一个字节,一个字节表示程序中的某些字符,也就是每个字符对应一个8位的整数值,这个整数值就是ascii码。
所以我们的源程序用ascii码就是这样表示的:
这里写图片描述
现在这些ascii码都是十进制方式展示在我们面前,但是在计算机当中还是一串串0、1组成的8位的串。这种以ascii码保存的文件就是文本文件,其它方式保存的文件都是二进制文件。

2.源程序的编译
虽然我们能够很轻松地看懂源程序,但是计算机却不能理解这些是什么东西,更不要谈运行我们的hello.c了,所以必须要让计算机也能看懂我们的意思,这时候就需要编译系统来帮忙了。

  1. 编译系统的作用:将源程序翻译为一些计算机能够理解的低级机器指令。
  2. 编译系统的组成:预处理器、编译器、汇编器、连接器
    编译系统的工作流程:
    这里写图片描述
  3. 3.

1.预处理器:根据以#开头的命令,修改源程序。如根据#include stdio.h>行,预处理器读取系统头文件stdio.h中的内容,代替此行内容。源程序经过预处理后,得到另一个c程序,此程序通常以.i为后缀保存。


2.编译器:将预处理后的.i文件转换成汇编程序。编译器将不同的高级语言(如c语言,C++语言)转换成严格一致的汇编语言格式进行输出。汇编语言以标准的文本格式确切的描述每机器语言指令。编译器得到的文件通常以.s为后缀保存。


3.汇编器:将汇编语言(.s文件)翻译成机器语言指令,并将这些指令打包成一种可定位目标程序格式。汇编后得到的文件即为二进制文件,通常以.o为后缀。


  1. 链接器:hello,world程序中调用过printf函数,它是一个c标准库里的函数。Printf函数存放在一个名为printf.o的单独预编译的文件中。而这个文件必须以适当的方式并入到我们的程序中,这个工作由链接器完成。将外部的.o文件并入后,得到一个完整的hello,world可执行文件。可执行文件加载到存储器后,由系统复制执行。

在unix系统中,源程序到目的程序的转换室友编译器驱动程序执行的。
输入命令:gcc hello.c -o hello
就会执行上述步骤并生成目标二进制程序。

4.运行目标程序

现在我们已经有了目标程序了,是时候运行了。
在unix系统中输入程序名:
当用户输入一行命令后,shell先判断它是不是一个shell内置命令,如果不是,shell会假定用户输入为一个可执行文件的名字,从而去加载并执行该文件。
注:

源程序是放在磁盘中的。
加载:把一个字节或一个字从主存复制到寄存器,覆盖掉寄存器中原来的值。

现在我们来关注下细节,计算机硬件到底做了什么?
这里写图片描述
上图是计算机一些基本的硬件组成。
其中一些硬件需要特别说明:

1.总线:在各个部件之间传输数据。

2.I/O设备:IO设备是系统与外界通信的通道,如鼠标,键盘,显示器,磁盘都是典型的IO设备。

3.主存储器:简称主存,是处理器执行程序时用于临时存放程序及其数据。主存由一组动态随机存储器芯片组成。也就是我们通常说的内存,它是与磁盘不一样的东西,就是我们计算机中的内存条

4.处理器:也就是cpu,解释执行存储在主存中的指令。其内部的pc永远指向下一条将要被执行的指令的地址。处理器的操作主要是围绕PC、ALU(算术\逻辑运算单元)、主存来进行运作的


CPU按照指令可能会执行的操作有:

加载:把一个字节或一个字从主存复制到寄存器,覆盖掉寄存器中原来的值。

存储:把一个字节或一个从寄存器复制到主存,并覆盖主存中原来的值。

操作:把两个寄存器的内容复制到ALU,ALU对两个字做算术运算后存回其中的一个寄存器,该寄存器中原来的值会被覆盖。

跳转:从cpu执行的指令抽取一个字的内容存入PC,覆盖掉原来的值,从而改变下一条要执行的指令,达到跳转的目的。

最开始shell程序处于执行状态,等待用户输入命令。当我们在shell环境下输入“.\hello”时,输入的字符被一个个读入寄存器并送入主存。
这里写图片描述
按下回车后,shell程序知道我们已经输入完成。
由于我们的可执行文件是放在磁盘里的。所以接下来要做的就是把磁盘的代码和数据复制到主存储器中(通过DMA方式加载程序,则不需要通过CPU,而是将hello可执行文件直接从磁盘复制到主存),如下图
这里写图片描述
可执行程序加载到主存后,cpu就执行hello程序的机器指令,而这些指令完成的工作便是将”hello,world\n”这几个字符从主存中复制寄存器文件中(register file),再将其从寄存寄文件中复制到显示设备上进行显示,如图:
这里写图片描述
到此,hello world程序从源程序到成功执行整个流程已经完成了。

5.拓展

1.高速缓存

其实上面的硬件组成并不符合现在的计算机,从上文可以看到内存是直接与寄存器进行数据交换的,这样其实是很浪费资源的,因为现在的cpu的处理速度非常快,但是对内存的读取与其比起来就显得特别慢,如果寄存器直接与内存打交道,效率就会很低,所以前辈们引入了高速缓存(cache)。高速缓存是寄存器与主存的一个过渡,也结合了两者的优点。高速缓存可以存放比寄存器多的数据,读取速度又快于主存。所以很多需要频繁使用的数据就可以放在cache中,这样就不用每次都去访问主存,大大提高了效率。
后来这种在较快的处理器与较大较慢的存储器之间插入一个过渡设备的思想得到了普及,就形成了后来的存储器分层结构:
这里写图片描述

2.操作系统管理硬件

在我们的hello程序中,我们都没有直接操作键盘、显示器、磁盘等,这些事情都是操作系统帮我们做了。操作系统其实就是介于硬件与我们的引用程序之间的一层软件。
这里写图片描述
操作系统有两大功能:

  1. 防止硬件被失控的程序操纵。
  2. 提供给应用程序提供操纵复杂的低级设备一个简单一致的方法。

每天进步一点点

相关文章
相关标签/搜索