编译器

编译器是将一种语言翻译为另一种语言的计算机程序

编译器将源语言编写的代码作为输入,产生用目标语言编写的等价程序。通常,源语言为面向人类的高级语言,如C\C++\Fortran等,而目标语言为面向目标机的机器语言,如Intel x86\ARM\MIPS\SPARC等 。

4933701-d98d679df5206903.png
编译器编译过程
4933701-da83bc6a82e3e9bf.png
编译器架构

示例

$ vim hello.c
#include <stdio.h>

int main()
{
  printf("hello, world!\n");
}

程序的生命周期是从一个高级C语言程序开始的,因为这种形式能够被人读懂。然后,为了在系统上运行C程序,每条C语句都必须被其他程序转化为一系列的低级机器语言指令。然后这些指令按照一种称为可执行目标程序的格式打包,并以二进制磁盘文件的形式存放起来,目标程序也称为可执行目标文件。

在UNIX上,从源文件到目标文件的转化是由编译器驱动程序完成的:

$ gcc -o hello hello.c

GCC编译器驱动程序读取源程序,并将其翻译成一个可执行的目标文件。这个翻译过程分为4个阶段完成,执行这4个阶段的程序一起构成了编译系统(compilation system)。

4933701-8d8ffa0f959815ef.png
编译系统
  • 预处理阶段
    预处理器(cpp)根据字符#开头的命令,修改原始的C程序。#include <stdio.h>该命令告诉预处理器读取系统头文件stdio.h的内容,将把它直接插入到程序文件中。结果就得到了另一个C程序,通常以.i作文件扩展名。
  • 编译阶段
    编译器(ccl)将文本文件hello.i翻译为文本文件hello.s,它包含一个汇编语言程序。汇编语言程序中的每条语句都以一种标准的文本格式确切地描述了一条低级机器语言指令。汇编语言是非常有用的,因为它为不同高级语言的不同编译器提供了通用的输出语言。
  • 链接阶段
    hello程序调用了printf()函数,它是每个C编译器都会提供的标准C库中的一个函数。printf()函数存在于一个名为printf.o的单独的预编译好了的目标文件中,而这个文件必须以某种方式合并到我们的hello.o程序中。链接器(ld)就负责处理这种合并。结果就得到hello文件,它是一个可执行目标文件(可执行文件),可被加载到内存,由系统执行。

处理器读取并解释存储在储存器中的指令

系统的硬件组成

4933701-700fe67e8ec061a4.png
典型系统的硬件组成 - Intel Pentium系统产品模型
  • 总线
    贯穿系统的一组电子管道称作总线,它携带信息字节并负责在各个部件间传递。通常总线被设计成传送定长的字节块,也就是字(word)。字中的字节数(即字长)是一个基本的系统参数,在各个系统中情况都不尽相同。现在的大多数机器字长有的是4个字节(32位),有的是8个字节(64位)。
  • I/O设备
    输入/输出设备是系统与外界联系的通道,每个I/O设备都通过一个控制器或适配器与I/O总线相连。控制器和适配器之间的区别主要在于它们的封装方式。控制器是置于I/O设备本身的或系统的主印制电路板(主板)上的芯片组,而适配器则是一块插在主板插槽上的卡。无论如何,它们的功能都是在I/O总线和I/O设备之间传递信息。
  • 主存
    主存是一个临时存储设备,在处理器质性程序时,用来存储方程序和程序处理的数据。从物理上来说,主存是由一组动态随机存取存储器(DRAM)芯片组成的。从逻辑上来说,存储器是一个线性的字节数组,每个字节都有其唯一的地址(即数组索引),这些地址是从零开始的。一般来说,组成程序的每条机器指令都由不同数量的字节构成。与C程序变量相对应的数据项的大小是根据类型变化的。
  • 处理器
    中央处理器(CPU)简称处理器,是解释或执行存储在主存中指令的引擎。处理器的核心是一个字长的存储设备或寄存器,称为程序计数器(PC)。在任何时刻,PC都指向主存中的某条机器语言指令(即含有该条指令的地址)。
    从系统通电开始直到系统断电,处理器一直在不断地执行程序计数器指向的指令,再更新程序计数器,使其指向下一条指令。处理器看上去是按照一个非常简单的指令执行模型来操作的,这个模型是由指令集结构就决定的。在这个模型中,指令按照严格的顺序执行,而执行一条指令包含执行一系列的步骤。处理器从程序计数器(PC)指向的存储器处读取指令,解释指令中的位,执行该指令指示的简单操作,然后更新PC,使其指向下一条指令,而这条指令并一定与存储器中刚刚执行的指令相邻。
    这样的简单操作并不多,而且操作是围绕着主存、寄存器文件(register file)、算术/逻辑单元(ALU)进行的。寄存器文件是一个小的存储设备, 由一些1字长的寄存器组成,每个寄存器都有唯一的名字。ALU计算新的数据和地址值。

CPU在指令的要求下可能会执行以下操作:

  • 加载:把一个字节或一个字从主存复制到寄存器,以覆盖寄存器原来的内容。
  • 存储:把一个字节或一个字从寄存器赋值到主存的某个位置,以覆盖这个位置上原来的内容。
  • 操作:把两个寄存器的内容复制到ALU,ALU对这两个字做算术操作,并将结果存放到一个寄存器中,以覆盖该寄存器中原来的内容。
  • 跳转:从指令本身中抽取一个字,并将这个字复制到程序计数器(PC)中,以覆盖PC中原来的值。

处理器看上去只是它的指令集结构的简单实现,但是实际上现代处理器使用了非常复杂的机制来加速程序的执行。因此,可这样区分处理器的指令集结构和微体系结构:指令集结构描述的是每条机器代码指令的效果;而微体系结构描述的是处理器实际上是如何实现的。

运行程序

初始时,外壳程序执行它的脚本,等待我们输入一个命令。当我们在键盘上输入字符串'./hello'后,外壳程序将字符串逐一读入寄存器,再把它存放到存储器中。当我们在键盘上敲回车键时,外壳程序就知道我们一已经结束命令的输入。然后外壳执行一系列指令来加载可执行的hello文件,将hello目标文件中的代码和数据从磁盘复制到主存。数据包括最终会被输出的字符串。

4933701-3eb48c05b52f4f94.png
从键盘上读取hello命令

高速缓存至关重要

系统耗费大量时间把信息从一个地方挪到另一个地方。程序的机器指令最初是存放在磁盘上的,当程序加载时,他们被复制到主存,当处理器运行程序时,指令又从主存复制到显示设备。从程序员的角度来看,这些复制就是开销,减缓了程序真正的工作。因此,系统设计者的一个主要目标就是使这些复制操作尽可能快地完成。

4933701-9b22436df7879fc7.png
从磁盘加载可执行文件到内存

根据即系原理,较大的存储设备 要比较小的存储设备运行的慢。快速设备的造价远高于低速设备。 针对处理器和主存之间的差异,系统设计者采用了更小、更快的存储设备,高速缓存存储器(高速缓存),作为暂时的集结区域,用来存放处理器近期可能会需要的信息。

4933701-c1c68f1d953e2469.png
高速缓存存储器
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值