漫谈处理器体系结构

漫谈处理器体系结构

前言:

这篇博客本应该是《深入理解计算机体系结构》(第二版)中第一部分第4章处理器体系结构的读后感,但是感觉这样的名字有点low,因为毕竟加入了自己的理解和总结。

ISA(Instruction-Set Architecture)

几乎所有讲体系结构的书都会讲到这个指令集。指令集确实应该是最先说明的问题。一句话概括起来指令集就是说CPU能干什么事。基本常用的指令集包括:传送指令、算术逻辑指令、跳转指令等。是体系结构需要实现的功能。指令集对人类来说是友好的,可阅读的。但对于只认识01的机器来说,还是需要对其进行编码的。编码的方式有定长编码和可扩展编码,现代处理器使用的基本上都是可扩展编码。编码方式上来说我觉得有一点值得考虑一下的就是Huffman编码,它是一种前缀编码,不可能有一个编码是另一个编码的前缀。Huffman编码可以实现减少编码的长度,节省存储空间。具体的实现可以参见1,上面的解释和代码都很详细。

硬件基础

当然,我是个写软件的,肯定不会去深入的讲解物理实现去(我也不会)。从一个程序猿的角度看,体系结构采用的肯定是时序电路。但是组合电路是时序电路的基础,与布尔代数息息相关。我理解的组合逻辑就是给定输入,就可以给定输出。加法器就是组合逻辑,输入两个值就会得到和。时序电路就是在组合逻辑的基础上加上时钟信号。什么是时钟信号?怎么加?相信大家都应该听说过石英表,通过石英的周期震荡来获得时钟信号我们就有了时间的概念。时钟对于同步来说是非常重要的,说白了同步就是什么时间该干什么事,不要乱了。就像人类社会一样,现代社会的繁荣就是因为时间积累下来了知识。同样,计算机这么智能的东西有了时钟的概念之后,自然是要把事情分为很多步骤(即你写的一条条指令)。组合逻辑就是实现其中的一步,时序逻辑就是实现好几步。那么这好几步需要做的自然而然就是要存储上一步的结果。这就是时钟寄存器的概念。时钟寄存器用来存储组合逻辑的结果,也成为划分各个操作的分界点,因为它在每次时钟改变(处于上升沿)的时候才会更新寄存器的存储值。除了时序电路和中间结果的寄存,基于冯诺依曼体系结构的中心就是存储,存储结构包括大家熟悉的主存、cache和寄存器。
对,概念计算机主要就是这些部件,一个时钟(用于同步计时),总线(用于通信),算术逻辑单元(ALU,组合电路),寄存器(Cache,主存等用于存储),时钟寄存器(用于传递逻辑电路结果)。你可能会说大学课本里面有一个很重要的控制单元(CU),它去哪了,CU实际上是上述部件的一些组合,就是下面我们要重点说的处理器实现。

处理器顺序实现

书中是从处理器的顺序实现开始讲的,也确实这样很容易理解。顺序实现就是没有流水线的实现,一条指令一个周期完成。虽然书上面讲一条指令的执行分为了很多阶段,但是在这里我觉得如果用很多模块来讲更加的清晰。区别就是阶段代表了时间,感觉顺序实现就是一个噱头,还是流水线的概念;模块代表了一条指令执行的不同部分。
一条指令就用一个组合逻辑实现了。这里我觉得有两个重要的问题需要说明:

指令集有很多的操作,岂不高射炮打了蚊子

这里的理解很正确,指令集有很多操作,需要很多不同的硬件实现,每次我们只能根据指令编码选择(可以使用经典的选择器逻辑电路实现)其中之一使用,很多时间部件基本都是空闲的。这就是流水线化的初衷,让所有的部件尽可能的忙起来,提高资源的使用率。

两条指令中间的结果怎么传递

前面已经说过了,当然是使用时钟寄存器了。常用的时钟寄存器有两个PC,CC。程序计数器(PC)是时钟寄存器是无可厚非的,因为下次执行的地址应该是上一条执行地址的下一个指令(对于顺序程序结构来说),但是这里条件码寄存器(CC)为什么也会成为时钟寄存器呢?答案在书中已经解释的很清楚了。
处理器从来不需要为了完成一条指令的执行而去读由该指令更新了的状态。
这是处理器很重要的一条规则,熟悉汇编语言的同学都知道,在汇编语言里面,条件判断是分为两步执行的。第一步进行计算,第二步取值(条件码的值)。而且对于我当前的语句来说,我有可能用到上一次的条件码的值,所以我当次是不能改变条件码的,只有等到下一条指令执行的时候(下一个时钟周期)才能对CC的值进行改变。

具体的实现可以从书中看到,就不在详述了。

处理器流水线实现

前面已经做了很多铺垫,由于资源的利用率太低,所以说需要采用流水线的方式来提高资源利用率。流水线的概念大家应该都了解,不了解可以从书中看,它可以提高系统的吞吐率。但是流水线也不是没有不好的地方,就是它会增加单条指令的运行时间。为什么呢?因为在把指令的执行划分为多个段之后,需要增加时钟寄存器来存储中间的状态,这些状态的存储更新过程是需要花费时间的。但是对于大规模的系统处理来说,当然还是用流水线处理比较方便了,小作坊式的生产只能应对很小的产量。

流水线段划分

流水线在设计的时候肯定是需要分段的,那么划分的时候有两个原则:
- 划分的段所用时间尽量相同或相近。因为流水线运行周期是由最慢阶段的延迟限制的,如果段有长有短,自然就会浪费很多时间。
- 段划分不能太细,因为中间的时钟寄存器需要花费一定的时间,这实际上为指令的执行增加了时间。如果我们划分的太细,甚至增加的时间比原来消耗的时间都多,那就得不偿失了。
上面两条原则实际上是在告诉我们段划分既不能太大也不能太小(太大肯定不行,我们划分流水线就是为了变小,不然直接用顺序好了)。常用的分段方式就是把流水线划分为:取指、译码、执行和访存四个阶段。那这里自然而然有个问题,就是执行阶段有很多非常复杂的操作,比如说浮点数操作,可能大概需要二十几个时钟周期的操作,这样就拖慢了每一条指令的执行周期,流水线的段划分就会收到很大的影响。针对这种
多周期指令的特殊问题,我们通常采用以下两种方法解决:
- 最简单的实现方法自然就是在执行阶段停留几个周期,但是这样做肯定就会导致很多的空操作。但是这个实现简单,当然效率就不高了。
- 现在普遍采用的方法就是采用一个独立于流水线的特殊硬件单元来处理较为复杂的操作,这样性能会更好一些。比如专门浮点运算处理单元。将特殊指令发射到特殊的硬件处理单元,这个单独的硬件处理单元也可以采用流水线化的处理方式进行处理,但是一般来说这个特殊的硬件处理单元会有自己的时钟周期。原来的流水线继续执行流水线指令,两个硬件并发执行。(当然如果存在数据相关,后面的指令就可能得等待当前的指令执行结束了)

虽然我们把处理器做成了流水线的方式,但是经过这种方式产生的指令执行方式还得是要跟顺序处理器的执行结果要一致。这就需要解决流水线带来的问题:流水线冒险。

流水线冒险

流水线冒险就是相邻的指令之间存在相关依赖性。流水线冒险包括1.数据相关,下一条指令用到该条指令的结果。2.控制相关,该条结果确定下一条指令的位置(跳转指令)。

用暂停的方式处理数据相关

这种方法就是暂停流水线,结果跟顺序结果一样。(产生气泡操作bubble,跟nop空操作一样)

用转发来避免数据冒险

当前指令的执行结果应该在指令执行结束之后才能写回到寄存器或者存储器中,但是如果流水线的下一条指令就用到了该结果,这里采用转发或者叫做数据旁路技术把当前的指令执行结果传递到流水线较早阶段。也就是没等这条指令执行结束,下一条指令就可以默认该结果有值了。一般来说,产生结果的只有执行和访存阶段,转发的方式就是从执行结果和访存阶段的结果直接添加一条回到译码阶段的回路。

第一种方法简单效率不高,第二种方法效率高但是有些问题也不能完全解决数据冒险问题。例如load加载冒险,因为访存阶段在比较后面,如果第二条指令直接使用load的结果,在译码阶段执行时不能完成转发。所以只有使用转发和暂停的联合方式,才能解决所有的冒险问题。

用预测技术解决控制冒险

当遇到跳转指令的时候使用预测技术对其进行预测,预测正确,流水线继续执行。预测错误,恢复现场,删掉流水线中的指令。ret指令是个特例,当执行ret指令时,必须要暂停,因为它没法进行预测下一次地址是什么了。

异常处理

顺序结构异常处理就是halt停机,然后向操作系统报告异常就行了。
流水线处理的时候,如果流水线中的多条指令同时出现异常,那么只向操作系统报告流水线中最深的指令引起的异常,就是运行最多的指令。当预测指令出现异常时,应该等到预测正确与否才报告是否异常。如果一条指令引起了异常,那么它之后的指令不应该影响系统状态。比如说CC的状态,所以不仅要暂停后续指令改变状态的操作,还需要专门的异常处理程序对状态进行恢复。

存储接口

从主存中读取需要3~20个周期,可以等待,暂停流水线。
但是当需要的数据不在主存中,而在磁盘中,因为读取一个磁盘的操作需要几百个时钟周期,所以不能等待。这个时候采用的操作系统采用缺页中断的处理方式进行读取。
总结来说主存读取用暂停,磁盘读取用中断方法。

[1]http://blog.csdn.net/qyee16/article/details/6664377

本页内容版权归属为原作者,如有侵犯您的权益,请通知我们删除。

Linux学习---文件查找 - 2016-07-22 18:07:56

grep, egrep, fgrep  :文本查找 文件查找 locate 全系统查找,非实时,模糊匹配。查找时根据全系统文件数据库进行的。 系统在每天的计划任务时间生成数据库。 updatedb  手动生成文件数据库。 速度快。 find 实时查找,精确。速度慢。 遍历指定目录中所有文件完成查找。 支持多种查找标准。 find   PATH   查找标准   找到后的处理动作           路径默认:表示当前目录           标准默认:指定路径下所有文件           动作默认:为打
在上一篇文章中,详细讲述了Spring中注册AOP解析器的过程。在这篇文章中,将进一步讲解Advisors的创建过程。 Spring中注册AOP解析器的所有操作都是针对AnnotationAwareAspectJAutoProxyCreator进行的。AnnotationAwareAspectJAutoProxyCreator是实现AOP的根本。首先观察AnnotationAwareAspectJAutoProxyCreator的类层次结构。 图1 AnnotationAwareAspectJAutoPr
 总体来说 设计模式 分为三大类:创建型模式、结构型模式和行为型模式。 博主的上一篇文章已经提到过创建型模式,此外该文章还有设计模式概况和设计模式的六大原则。设计模式的六大原则是设计模式的核心思想,详情请看博主的另外一篇文章:  Java经典设计模式之五大创建模式(附实例和详解) 。 接下来我们看看结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。其中适配器模式主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式。其中的对象的适配器模式是各种结构
大话设计模式 1 面向对象的好处 可维护、可重复、可扩展 。 2 包含的角色 简单工厂模式包含三个角色: 工厂类Factory :工厂类是用来制造产品的。因此,在Factory中有一个用于制造产品的Create函数或者Generate函数之类的函数。这个函数能够根据“标识符”的不同生成不同的ConcreteProduct,当然这些ConcreteProduct都是继承自AbstractProduct的。 抽象产品类AbstractProduct :抽象产品是从其他具体产品抽象出来的。抽象产品类只有一个。
Linux常用命令 (1)ls 查看当前目录下所有目录和文件 ls -l会将目录和文件竖着排,并且可以提供文件数据 上图最左边以“d”开头的是目录,以“-”开头的是文件。后面是文件和目录的权限,后面是占了多少空间的大小,然后是创建人和所有人是谁,然后是实际当中所占空间,后面是创建的时间,最右边是目录或文件的名字。 ls -m会适合左右屏幕宽度将目录和文件列出 后面的不常用的不再介绍,用到可以去查 (2)cd 打开目录 cd /打开根目录 cd /dev 打开dev目录(绝对路径) cd /之后再cd dev

Web Service学习总结 - 2016-07-22 18:07:07

Web service到底是什么, 在什么情况下你应该使用Web service:      研究一下当前的应用开发程序 ,你会发现一个绝对的倾向:人们开始偏爱基于浏览器 的客户端应用程序。这当然不是因为客户端能够提供更好的用户界面,而是因为它能够避免花在桌面应用程序 发布上的高成本。发布桌面应用程序 成本很高,一半是因为应用程序安装和配置的问题,另一半是因为客户端和服务器之间通信的问题。      传统的Windows客户应用程序使用DCOM来与服务器进行通信和调用远程对象。配置好DCOM使其在一个大型
咱们不搞一开始就一大堆理论知识介绍,怕把人讲懵了...... 咱们换一个思维方式——"从现象看本质",先说说我们看到了什么,再从看到的现象中提出问题,最后深入寻找答案。 我们看到的 cookie 我自己创建了一个网站,网址为 http://ppsc.sankuai.com 。在这个网页中我设置了几个 cookie : JSSESSIONID , PA_VTIME , skmtutc , test 。 在 chrome 浏览器中打开这个网站,进入开发者模式,点击 Resources 栏 - 选择 cooki
MyBatis 的配置文件包含了影响 MyBatis 行为甚深的设置(settings)和属性(properties)信息。 文档顶层结构: configuration 配置           properties 属性           settings 设置           typeAliases 类型命名           typeHandlers 类型处理器           objectFactory 对象工厂           plugins 插件           envir
大话设计模式 1 策略模式UML图 2 策略模式的概念 策略模式(Strategy) :它定义了算法家族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化,不会影响到算法的客户。【DP】 3 策略模式和简单工厂模式的比较 简单工厂模式需要让客户端认识 两个类 ,而策略模式和简单工厂结合的用法,客户端就只需要认识 一个类 就可以了,耦合度更低。 4 策略模式解析 策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,他可以以相同的方式调用所有的算法,减少了
在Java多线程应用中,队列的使用率很高,多数生产消费模型的首选数据结构就是队列。Java提供的线程安全的Queue可以分为 阻塞队列和非阻塞队列 ,其中阻塞队列的典型例子是BlockingQueue,非阻塞队列的典型例子是ConcurrentLinkedQueue,在实际应用中要根据实际需要选用阻塞队列或者非阻塞队列。 注:什么叫 线程安全 ?这个首先要明确。 线程安全的类  ,指的是 类内共享的全局变量的访问必须保证是 不受多线程形式影响的 。如果由于多线程的访问(比如修改、遍历、查看)而使这些变量结