计算机系统C++首页投稿(暂停使用,暂停投稿)

计算机系统003 - 硬件体系

2017-06-20  本文已影响160人  SniperPan

前一篇中粗略讲述了二进制加法运算的过程,其中假定数据是从寄存器直接装载到加法器两端,加法器产生的结果也同样保存在另一寄存器中,而没有在意数据是如何从外界传递到了寄存器里,也并未给出寄存器中结果将以何种方式呈现给加法操作的真正用户。

因此本章将从计算机体系结构入手,介绍自然语言与机器语言间的共识。

1. 问题表现形式

当我们希望求助他人帮忙解决某一问题时,首先要做的是用双方共同支持的自然语言(如普通话、英语)来描述问题,等待对方提出解决问题的方法或是协同完成。然而如果希望将某一任务交付给计算机来完成,那么很抱歉,计算机并不会思考,他只是流程的接收和执行者,也就是说,如何形成流程,仍然需要我们来完成。

也可能有人会讲,不是啊,当我打开计算机,双击一个视频文件,计算机就会帮我播放,我并没有告诉它如何播放,用什么播放吧。但你回过头来看看这段文字,就可以发现其中还是含有通用流程,和你要寻找食物必须打开冰箱一样。至于视频播放的真正过程,后面会讲到,此处先给一个概念。

所有的信息都是以电信号存储于存储器中,读取后得到的是二进制字节流,至于该字节流描述成什么具体内容需要依赖上下文,以及解释器。比如同样一句“不好吧”使用不同语气,对于不同接收者而言可能获取到不同的含义。同样,一串字节流,使用音频播放器可能被解析为声音,使用notepad可能被处理为文本,甚至如果想象力再丰富一点,也可以假象成这是一个文字解密游戏。
因此通常会在字节流中埋下一些记号,标明数据类型,这些称为元数据。在元数据基础上,不同视频格式会有不同数据组合方式,通过枚举不同格式尝试解析视频文件,如可以匹配为某格式,就按照该格式进行播放;如不能,则报错不能正确播放某文件。

综上所述,要想托管任务给计算机执行,那么计算机必须有两个充分条件:

计算机能够执行的任务归根结底是数学计算,而对于如何进行统一、可行的任务描述,人们做了大量尝试,直到图灵机概念的出现。

1.1 图灵机

图灵为了给出计算机的清晰数学描述,他观察人们计算式采用的方法和行为,然后将其抽象成一种统一的表达机制。图灵将人们用纸笔进行数学运算的过程抽象成下列两种简单动作:

为了模拟人的运算过程,图灵造出一台假想的机器,该机器按照读取规则读取纸带上数值,然后进行计算。

这台机器主要由如下几个部分组成:

简而言之就是TABLE中规则控制读写头HEAD在无限长纸带TAPE上读写,根据读写内容进行运算,并选择是否保存结果至纸带特定位置中。如运行过程中发生状态变化,则保留在状态寄存器内。

1.2 七层转换

图灵机是思想指导,回归到实践而言,对于任务描述的方法可表达为七层转换。《计算机系统概论》1.7节中提出,要控制电子器件按照我们的意图工作,需经历如下七个完整过程:

指令集结构实际上就是程序和计算机硬件之间接口的一个完整定义。

综上所述,从人类自然语言表述的问题到最终执行的单元器件,需要做七层转换。对于为什么要分层以及为什么是七层,通常来讲,高层通常是低层的抽象结果,意在忽略部分无关细节,提供分析问题的更高层次的视野。不过到目前为止表述的方法都是理论性的,接下来就看一下历史中实践的结果。

2. 冯·诺依曼结构

冯·诺依曼结构是一种将程序指令存储器和数据存储器合并在一起的电脑设计概念结构,它用于实现通用图灵机和一种相对于并行计算的序列式结构参考模型。

接下来我们对冯·诺依曼结构进行细化,如下图所示。


从图中可以看出,冯·诺依曼结构主要有如下5个组成部分:

哈佛结构
虽然冯·诺依曼结构是现代计算机的主要结构,但我们也必须知道,还有一个结构叫做哈佛结构。它和冯·诺依曼结构之间最大的差异在于数据在内存中排列方式。冯·诺依曼结构中允许指令和数据混合存储在同一存储模块中,而哈佛结构必须使用独立的存储模块来分别存储指令和数据。

2.1 硬件实现(0 器件,1 电路)

本系列的第一篇中主要介绍了电学知识,为的就是在理解计算机时能够形成完整通路,而不是只能触及操作系统这一层,视底层硬件为黑盒,不知所以然。从前面我们知道硬件只能识别电信号,无论是组合逻辑电路还是时序逻辑电路,输出的产生终归需要输入的参与,而在托管任务时,任务就是我们需要的输入。

然而我们不可能在托管任务后还要守在一旁切换电信号以提供准确输入换取输出,既不高效也不可理智。因此最好能将任务描述使用一种能够最终转换为电信号的语言进行描述,而硬件在收到任务描述后,根据其中规则和数据自动完成计算,产出结果。同时为了保证任务描述的精确性,对于电信号我们进行了一次抽象,将信号幅度抽象成有无,对应为0和1,即所谓的二进制。

2.2 指令结构及实现

冯·诺依曼结构在现有基础上提出计算过程应该是:程序和数据都是以bit流的方式存放在计算机内存中,程序在控制单元的控制下,依次完成指令的读取和执行。

二进制在历史悠久的人类自然语言面前是过于苍白的,为了能够将二进制位和人类想表达的操作对应起来,这一次,抽象的任务落在了自然语言上。既然计算机实质上是负责计算任务,因此可以不直接参考自然语言,而是选择抽象数学运算的基本规则(如加减乘除),形成指令。

目前通用计算机中指令是其执行的最小单位,指令本身本身由操作码和操作数组成。操作码标明其行为,操作数标明行为作用对象。
指令集架构还定义了其他内容,现存的指令集结构也各不相同,如Intel和AMD系的。但通常一条指令包含内容如下:

指令的处理过程是在控制单元控制下一步步完成的,这里执行的步骤顺序称为指令周期,每一步称为节拍,通常一个指令周期包含6个节拍,分别如下:

立即寻址 操作数为立即数 MOV BX,8080H
寄存器寻址 操作数为寄存器 MOV BX,AX
直接寻址 操作数为地址 MOV AX,[1234H]
寄存器间接寻址 操作数为SI/DI/BX/BP之一 MOV BX,[DI]
寄存器相对寻址 操作数为SI/DI/BX/BP之一,加上偏移量 MOV BX,[DI+100H]
基址加变址寻址 操作数是BX/BP之一,加上SI/DI之一 MOV BX,[BX+DI]
相对基址加变址寻址 操作数是BX/BP之一,加上SI/DI之一,加上偏移量 MOV BX,[BX+DI+100H]

2.3 从问题到开发语言

终于现在有了指令集,它定义了处理器可以执行的所有操作,而我们剩下要做的就是将问题描述成一条条顺序或条件控制的跳转执行指令。对于简单的、小规模的任务,通过编写汇编函数,通常也能实现功能,除了效率略低,移植性不佳(平台支持的指令集可能不同)等。但对于复杂、大规模的任务,使用指令集语言就略显力不从心了,毕竟所有操作如MOV等都需要小心翼翼地不停叮嘱,这时候人们又想到了抽象。

虽然指令集接口各家、各平台互不相同,那能不能想出一套更高层次的,不在意平台差异的开发语言来描述问题。这样一来,还可以顺便把一些繁琐的、啰嗦的地址处理指令模块化,将任务描述这件事情从细节中解脱?答案是肯定的,现在我们有了更贴近底层的C/C++,主打一次编译处处运行的Java,甚至不要编译纯靠解释器在运行时逐字逐句翻译的Python等脚本语言,这些都让我们能够更关注问题本身,而不是事无巨细的实现。

当然从开发语言到指令集并不是省略了那些繁琐的细节,而是通过编译器、解释器、虚拟化运行环境等模块代劳。软件开发中有一句话和东北烧烤哲学类似,没有什么问题是一顿烧烤不能解决的,如果有,那就两顿;同样没有什么问题是一个中间层不能解决的,如果有,那就两个。

3. 总结

到目前为止,我们阐述了从问题本身出发,形成任务描述(指令),并由电路执行运算。这些都是整体的概念,或者说,对于实现细节,都是做了较多的抽象,为的是从宏观上有所了解。后续将对体系中各组成部分进行分别深入,力求浅出,再不济也要知道一些关键概念。

这里或者本系列也均不会进行任务无谓的语言之争,本系列文章寻求的是问题的解决方法、思维,而不是各实现细节之间的差异。

追寻知识融会贯通后的快感。

上一篇下一篇

猜你喜欢

热点阅读