赞
踩
本文是对王道计算机考研《计算机组成原理》课程的总结,主讲咸鱼学长讲的确实清晰。
由于我们学校已经开设过汇编和计算机体系结构,所以计组的笔记内容会比较精炼,高屋建瓴,不适合无基础人听。
如果有不理解的,可以回去看看我前面的CSAPP笔记和汇编语言笔记(不过我感觉还是没啥必要,我这篇文章更多的是总结性质,不适合入门学习)
CSAPP笔记:
第一卷:程序结构与执行——信息表示、指令、处理器、性能优化、储存层次
第二卷:在系统上运行程序——链接、异常控制流、虚拟内存
第三卷:程序间的交流与通信——系统级IO、网络编程、并发编程
汇编语言笔记:
汇编语言笔记——微机结构基础、汇编指令基础
汇编语言笔记——汇编程序开发、汇编大作业
汇编语言笔记——接口技术与编程
第二章学了运算器,第三章学了储存器,第四章(指令系统)和第五章(CPU)就是要学控制器,后面67章学习IO。
扩展原理:通过前缀来区分不同的长度。判定的时候就像是一颗树一样:先判断前4为是否为1111,如果是1111,则判断中间4位,如果还是1111,继续判断,直到确定前缀是1111 1111 1111,那么指令就是0地址类型的。
本质上就是一个哈夫曼树,前缀匹配,和计网的CIDR子网划分异曲同工。CIDR中,IP分为网络号+主机号,而在这里,操作码=去掉地址码以后剩下的部分=前缀+指令实际可变部分,就是下图中去掉红色X后左边的部分。
和计网简直一模一样,实际CPU在识别指令的时候,会进行前缀匹配,匹配1直到遇到第一个0为止,通过1的数量确定是0地址还是123地址指令。这个判断过程遵循最长前缀原则,会尽量匹配长的前缀。
扩展操作码的本质:
用上一层操作码剩下来的组合,当做本层操作码可能的前缀。
如此就可以将两种操作码区分开来了,三地址操作码前缀为0000到1110,留下了1111当做二地址的扩展,那么只要非1111,则一定是三地址。
n代表一节的长度,这里为4,三地址留下了一个1111前缀,那么在下一层中,去掉一个地址并入操作码位数,就又会多出来 2 n 2^n 2n种组合
上一层每预留m个前缀,下一层总共可用的组合就是m× 2 n 2^n 2n种
PC在取出指令的一瞬间(应该在译码阶段),就会结合指令长度进行自增变化,所以其实PC永远指向下一条指令,这就是顺序寻址
我们通常会写成PC=PC+1,然而自增没有那么简单:
除此之外,还有跳跃寻址
。跳转指令的直接作用就是在执行阶段修改PC值,注意,此前PC已经自增一次了,所以修改PC值是基于自增结果的。即,目标相对于下一个指令的偏移量
这就是偏移的本质,无论PC在哪,目标=PC+(目标-PC),这就一定能跳过去,所以在偏移跳转的情况下,JMP后面跟的值是这个(目标-PC)的差值。本质上和直接跳没啥区别,可能就是电路的区别。做题的时候,两种情况都可能给出。
结合上学期学的汇编来看,指令寻址的方式是特别多的,如何区分一个地址是什么寻址方式呢?那就让地址=寻址特征+形式地址。其中,寻址特征表明了你是用什么方式去解读紧跟的形式地址的。
假设我们要访问一个操作数A
立即寻址
就是把A写在立即数,很明显位数受限
因此就要把他放在内存里,但是这样就要访存一次,即进行一次直接寻址
我不想访存,又不想受到位数限制,那你就放在寄存器里,即寄存器寻址
寄存器寻址其实就是减少一次访存,变成访问一次寄存器,同样的效果,用寄存器就可以减少一次访存,速度大大提升
上述过称再加一环,就是一次间接寻址
,要访存两次。
同理,使用寄存器间接寻址可以减少一次访存。
下图给的访存次数忽略了取指的那一次访存。
总结:
基址寻址。
变址寻址。
相对寻址。
指令中可以隐含操作数,不一定要用地址码指出。有的在寄存器(比如下面的ACC),有的在堆栈(比如push和pop指令)
堆栈的方向,一般是栈顶朝着0地址走,也就是说扩容是SP指针减小。
平时说的堆栈都是软堆栈,在我印象里,汇编语言中的浮点指令,有一些就使用的硬堆栈,是一组浮点寄存器。
这一块因为我学过汇编,所以就过得很快。
ESI中,S=Source,I=Index。EDI中,D=Destination
需要注意的是,我似乎没有发现间接寻址的指令,实际上就是如此,因为间接寻址可以用寄存器间接寻址实现,有寄存器你不用,非要用间接寻址那不是傻吗。
需要注意一下偏移量的指令。我们前面学过基址寻址之类的,我们结合这片知识来理解一下汇编中的偏移量指令。
以mov eax,[ebx+ecx*32+4]
举例:
控制结构依赖于跳转指令,跳转指令和算术指令搭配使用。
算术指令,包括cmp之类的,会修改标志位(PSW,或者FLAG寄存器)。跳转指令读取标志位,进而决定是否跳转。
选择语句
不需要你去写,你能看懂了这是个选择结构,并且知道哪一部分是IF,哪一部分是else就行了。if和else不一定哪个在前哪个在后,得仔细看看条件。
循环语句
,for循环如下。while其实也类似,都可以写出来,会看就行。
实际应用中,loop语句更加简洁,可以直接实现for循环,loopz可以实现while循环。
我们且不论参数传递,局部变量,寄存器保护等栈帧具体操作,我们只关注一下调用和返回过程中,栈帧整体是怎么切换的。
首先要知道三个点:
切换过程如下:
下图为调用的具体全过程
运算器内部,以ALU为核心,辅助有寄存器组与控制器件,他们需要和ALU连接,而最直接的思路是使用专用数据通路
。
为了不出现冲突,需要搭配多路选择器或者三态门实现同一时间只有一个寄存器的输入输出。三态门可以通过信号控制数据流向,以及是否可以流通,是很常用的控制器件。
这种方式缺点是成本大,而且线太多,布线很难。
解决的办法是使用CPU内部的单总线
。
给寄存器输入一个out信号,那么就可以把数据送到总线上,输入in信号,就可以把数据放到寄存器里。虽然一根总线可能会导致数据冲突,但是只要搭配暂存寄存器以及控制信号,就不会有问题。
重点理解一下暂存寄存器
的逻辑。为什么要暂存?本质就是,ALU是一个组合逻辑器件,是实时计算的,必须保证两端同时有稳定的输入,才能给出稳定的结果。但是我们的数据总线同时只能有一个数据,所以另一个稳定数据得寄存器给出。
从下图可以看出,如果让ALU的输入和总线直接相联,那么在总线输入变化的一瞬间,ALU的输入也会跟着同步变化。我们把一个数据送到总线上后,要先把这个数据存起来,才能把另一个数据送到总线上,不然原来的数据会被破坏。同理,输出的数据,在放到总线前,肯定也要先暂存,等总线上的数据无效了,我们再把这个数据放上去。
具体到运算器,输入端的暂存寄存器
,用于暂存来自总线的一个数据。输出端的暂存寄存器,用于暂存计算的最终结果,增加了移位功能后,变成了移位器
。举个例子ADD R0,dword ptr[100h]
。这个例子需要三步:
可以看到,总线上同时只允许一个数据存在,所以需要严格使用寄存器暂存,配合时钟来实现有条不紊地计算。
以上过程,虽然是5步,但是实际上只有两个时钟周期,一个用于访存,一个用于计算结果的输出,剩下的操作都是CPU发出信号,穿插在其中
再说其他的寄存器:
整体连线如下图。
指令寄存器(IR)
储存指令。
译码器(ID)
负责翻译操作码,翻译结果送到微操作信号发生器
里执行,这是控制器的核心。
微操作,其实是若干组控制信号,分批次送出。CPU里的组合逻辑早就摆在那里了,你只需要控制哪些数据可以导通,就可以产生对应的效果,结合时钟,就可以分几步执行完一套微操作。微操作发生器需要很多辅助信息:
微操作发生器,以一定的步骤向外发出指挥信号,外面的电路导通,执行,得到结果。比如我们要访存,那就让AdIRout=1,MARin=1,此时就是把我们的地址码送到了MAR里,来一个时钟就可以进行访存了。
CPU:
换一种表示方式如下图,CPU=ALU+CU+寄存器+中断系统。
寄存器中,上图橙色标出的是用户可见寄存器,凡是用户可以通过指令直接修改的,都算。比如cmp指令修改PSW,jmp修改pc,其他那几个更不用说,直接mov。
结合流水线的知识,我们知道CPU的执行其实是可以划分阶段的,理论上,每个阶段应该时间差不多,这样才不会出现浪费。
我们不论5级流水线,但从指令执行时间来看,其实取指是很费时间的,要访存,译码反而只是一个组合逻辑,所以取指+译码放一起。执行可能很复杂,所以单独成立一个阶段,至于附带的写回,时间也很快。
注意,这个阶段不是时钟周期,要区分三个概念:指令周期,CPU周期(机器周期),CPU时钟周期(时钟周期)
一般来说,一个指令周期内部,机器周期数量不等。一个机器周期内部,时钟周期数量不等。
取指周期
+执行周期
。加法指令比较常规间址周期
。间接寻址因为多一次访存,所以给访存单独设置一个机器周期中断周期
。正常来说,都会留一个时间检查中断,除非CPU被OS关了中断。把上面这几类指令结合起来,变成所有指令通用的流程图,如下:
使用4个触发器储存当前处于的状态,是one-hot编码,含义如下:
总的来说,只要分给一个机器周期,就都可能有访存周期,这个是操作中最费时间的。不同周期的访存目的不同,但是都有可能。
取指周期:
总的来说,要5时钟周期(我猜的)
间址周期:
执行周期比较复杂,无统一数据流。
直接看中断周期,中断周期的执行逻辑和call指令一样:
数据流动方向整体有三类:
这些数据都要经过数据通路。有三种方案:
区分一下:
下面给出三种数据流的执行情况。
写题
的时候,两个注意点:
逐一解释:
来个例子,MemR就是我们CU给出的读取信号。PC自增可以直接写到访存取指那个地方,因为只要访存了,原来的PC就没用了,同步自增就好。
单总线同一时间只能进行一个数据的传输,而多总线结构可以同时进行n个数据的传输,只要这些数据在逻辑上可以并行传输,多总线就可以做到。
此外,想实现这种并行传输,专用通路结构也是一种思路,任何两个需要流动数据的部件之间,都加一条通路,通过信号控制是否流通。很朴实很粗暴的思路,编写命令也很简单,缺点是线太多了,而且不好布线。
一个例子如下。如果要进行加法指令的话,除了取数据的一轮指令以外,还有就是激发C7,C6,把数据送到ALU,然后再触发寄存器储存结果。
更多的例题,后面再讲,暂时略过,TODO
控制器是如何实现控制的呢?
假设已经铺好数据通路,那么控制器说白了就是要在特定指令的特定机器周期的特定节拍,发出特定的控制信号。
只要信号按照逻辑给出,CPU内部的电路就会按部就班地处理数据,执行指令。所以关键在于,在什么时机给出控制信号,以及控制信号以什么形式给出。
硬布线控制器和微程序控制器是两种思路。前者是纯粹使用硬件实现,后者要搭配ROM,以储存程序的思路去实现。
首先思考一下时机问题。当前发出什么微命令,由指令操作码,机器周期,节拍信号,机器状态条件综合给定。
0-k号微命令,每个微命令会有一个单独的布尔函数
:
C i ( 操作码,机器周期,节拍信号,机器状态 ) C_i(操作码,机器周期,节拍信号,机器状态) Ci(操作码,机器周期,节拍信号,机器状态)
所以有可能会出现两三个微命令并行执行的情况,也就是我们的微指令。
怎么确定这个布尔函数呢?直观的举个例子。首先,可以看到布尔函数由3大部分组成,分别对应3个机器周期(其实最多可能有4个)。每一个部分,都要参考节拍信号和状态信号(图中没给出),最后在括号里补上指令操作码。
可以看出,一个布尔函数要考虑4个方面的信号,因此需要先列出一张表,罗列出每个指令在每个机器周期的每个节拍都有可能执行哪个微命令,也就是写一张自变量到所有微命令的综合真值表
,最后变成若干布尔函数。
具体要一步一个脚印:
这一步只管列出就行,按顺序,不用管到底需要几个时钟周期。
取指和间址周期中断周期都是一样的,而不同指令的执行周期也各不相同
三个原则:
执行周期的时序安排如下,看看得了,不用记。
理论上要一个大表,实际上可以按照机器周期分开。
先看FE阶段:
前面的就正常,最后两行是额外的。有的指令,FE完了就直接EX,但是有的还需要先进入间址周期IND,因此在FE表的最后两行还加上了对于机器周期状态的修改微命令,修改的原则就是看I状态位
,而I状态位又取决于指令的地址特征位
。
对于非访存指令,就不会有间址周期,而对于可能访存的,如果其I位为1,那么就要发出进入间址周期的命令。
再看IND阶段:
非访存指令没有IND,所以操作时间表里没有1。而访存指令就都有。注意最后一行,IND阶段是否要转入EX阶段,要看间接寻址过程是否已经完毕,有的指令可能会进行多次间址。只有等IND位=0,我们才能转入EX阶段。
再看EX阶段:
最后,如何从这三张表里,综合出一个微操作对应的布尔函数呢?
看下图,三个表,对应三大项。每一项的格式=机器周期·节拍·条件·(若干指令)
最后可以进行化简,这就是一个布尔函数。如此,把所有布尔函数都写出来,整体来看,就是若干输入,若干输出,最后进行电路综合就可以。
前面的硬布线,是将控制逻辑固化在硬件里了,通过输入实时产生输出。我们可不可以提前把输出储存在ROM里,只要给一个输入,我们就把对应的输出逐个提取出来执行呢?这就是微程序的思想。
程序>指令=微程序>微指令>微命令=微操作
宏观上,一个软件程序,执行的过程就是CPU和内存的交互,CPU一条一条取出内存的指令,执行。对于一条指令来说,它也是一个微程序,CU内部也有类似于CPU的东西,把一条一条的微指令从ROM取出,输出控制信号。
控制信号储存在微指令的操作控制字段,具体怎么存,怎么解释,就是后话了。而那个顺序控制字段,是为了便于微指令逐条执行以及跳转的,也是后话。
下图为基本结构:
CU相当于一个微型计算机。整体来看,CU外部给一个指令操作码OP,然后经过内部的运算,按照节拍分批输出控制信号。
CU内部相当于一个微缩版本的CPU与内存,属实是套娃了:
实际上,因为每个指令的取指,间址,中断的微指令段都是一样的,不如就公用,然后EX段各不相同,所以实际上CM的结构如下:
不考虑取指,单纯讨论EX微指令段
具体的执行过程,则流程如下:
其实其他段,也是类似的执行过程,感觉就是一个有限自动机
,就是个小CPU。熟悉了EX段以后,我们宏观上梳理一下连续执行若干条指令的过程中,这几个段如何切换:
总的来说,就是FE刷新OP,然后用新的OP执行EX,之后再跳到FE刷新OP,执行EX······执行路径为->FE->(IND)->EX->(INT)->
最后讨论一下微程序个数的问题。
我们最开始觉得,一个指令对应一个微程序,一个微程序就是一段。但是实际上,我们把微程序的公用部分拆出来放在前面,构成3个额外的微程序,所以理论上应该是至少n+1个微程序,考虑到间址周期和中断周期,那么就是n+3个微程序。
为了兼容前面的说法,我们从物理和逻辑的角度理解微程序:
所以,一条指令确实对应一条微程序,但是物理上来看,微程序个数不等于指令数。
一个微程序有多条微指令组成,一条微指令由多个相容的微命令(微操作)组成
微指令的格式主要就是操作控制这个字段如何设计,如何去解释为微命令输出到CPU?
总的来说,分为三种思路,水平型就类似于CISC,一条微指令多个操作,而垂直型类似于RISC,通过多条微指令组合构成复杂操作
直接编码很简单粗暴,使用nbit来分别表示n个微命令,需要哪个微命令,哪个位就是1。
缺点是控存又大又贵,INTEL的控存就很大,如果用直接编码就会更大,所以应该简化。简化思路就是,有一些微命令不可能同时出现(互斥的微命令),那就把他们放到一起用译码器解析。
每一个译码器可以表达k+1种状态,k个互斥命令,1指的是啥也不干的状态
下图为两种思路的对比。
给定一个下地址,如何生成下一条CMAR呢?
首先要分析微操作序列,然后安排节拍。这就行了,后面就和硬布线完全不一样了。微程序不需要你去写表,不需要综合,每个指令互不干扰,而硬布线是互相影响的,你直接把每个指令安排好的节拍写进CM的微程序段就可以。
举个例子:
对于取指微程序,先调整顺序,写出节拍安排,之后插入两类特殊节拍:
显然,微程序比硬布线多了一倍的节拍,确实慢多了。
EX和FE的类似,每条微指令后同样要插入一个通过下地址字段获取CMAR的节拍。
做完以上工作后,就需要确定微指令格式
最后编写微指令码点,写到CM中。
硬布线控制器是一个逻辑器件,所以一个节拍对应的微操作控制信号马上产生。微程序是储存起来的,需要消耗比硬布线多一倍的时钟周期读取,所以慢很多。
虽然微程序更慢,但是其编写难度小,而且易于扩充,比如下面说的EPROM就可以实现轻松的扩展指令集。而且微程序还可以继续套娃,变成毫微程序,不过感觉比较麻烦了。
同一时间,不同指令使用不同功能部件,所以可以同时有多条指令执行。
下图给出了总耗时估算的思路:
总耗时=前n-1条指令执行时间(流水线模式)+最后1条指令(非流水线)
流水线的表示方法有两种,过程图和时空图看起来有点像。
过程图
仅仅代表时间顺序,是横着看的,一条横线代表一条指令的全过程,其长度就是这条指令的生命周期。过程图又叫甘特图。
时空图
类似于os里面访问内存页置换算法FIFO模式那个图。
我们是竖着看的,用一道竖线,竖线上对应CPU的各个部件的占用情况,竖线从左往右移动,代表着时间推移,可以看到,随着时间推移,不断有新的指令从下面进来,一条指令所处的阶段在不断地被推上去,直到推出去。
过程图也就看一乐,使用时空图才便于计算流水线性能指标,有吞吐率,加速比,效率。
时空图中,总时间=装入时间+(中间时间+排空时间)
。装入时间只执行一条指令,而其他时间都是每个时钟周期执行一条指令。
首先要理解流水线的结构:周期一致,使用流水线寄存器暂存结果,时钟触发后流水线整体前进一次。具体数据通路需要熟悉。
假设cache每次都命中,这样就不会出现因为访存导致的流水线断流。
影响指令流的因素和解决方法大致如下,之后一一解读:
首先是资源冲突
。说白了就是每一个CPU资源都是临界资源,是互斥的。对于寄存器的资源冲突,采取延迟指令的方法解决。对于取指和访存的资源冲突,采用资源冗余的方法解决,分别弄一个储存器,互不干扰。
数据冲突
。这个又叫做数据冒险,学过体系结构的同学会很熟悉,当时可比这个复杂多了,数据旁路是最常使用的一种方法。
对于阻塞方法,空指令NOP是编译器插入的,CPU只需要顺着执行指令流就可以,是软件插入。bubble是cpu插入的,CPU检测到数据冒险则开始暂停。
最后,编译优化其实就是循环展开,调整指令顺序是循环展开的基本思想,搭配资源冗余可以实现k×k路展开。
控制冲突
。比如一条条件转移指令,他的比较结果只有在EX阶段完毕,进入MEM阶段的时候才能得到,那么到此为止流水线已经装入3条指令,这三条指令可能就是两个分支的其中一支。
如果预测对了,那么就不需要额外操作,如果错了,就需要给这三条指令加bubble消除的同时再装入正确的指令。
为了提高这个过程的效率,就有很多方法用于辅助,有提高预测准确率,有加快条件码生成,有把两个分支的指令全部预取。
这一章比较抽象,理解理解得了。
流水线按照使用级别分类,就是说结构上的细分程度。我们前面说的五段式流水线,是在CPU级别
上划分流水线,往细了我们还可以在部件功能级
划分,就是把一个部件内部划分为多个阶段,往宏观了还可以在处理机间
划分流水线,不同处理机分工实现流水线。
流水线按照完成功能分类,就是说它能做的事情。单功能流水线功能固定,每次执行用的部件都是一模一样的,比如我只支持浮点数加法。而指令流水线就是多功能流水线,可以执行不同的指令,对应的部件各不相同。
流水线按照连接方式分类,就是说不同部件是否可以执行不同任务。单功能流水线肯定是静态流水线。多功能是动态的,比如取指阶段执行的是浮点加法指令,而访存阶段执行的是写内存指令。
流水线按照有无回路分类。我们的数据旁路流水线就是有回路的。
再来说一下多发技术,多发技术基于我们前面说的流水线分类。
首先是超标量,这是并发执行技术,基于空间上的资源冗余。
不可以调整两束超标量指令之间的执行顺序,但是可以调整一束超标量指令内部的执行顺序,反正是并行的,有的CPU就支持乱序发射。
超流水技术其实是将一个机器周期分段,比如EX周期内部分成3个时钟周期。和超标量技术对标,一个是空间的冗余,另一个是时间上的重复利用。
最后就是这个超长指令字,非常抽象,是编译时进行的优化。比如编译器发现有三条指令的EX阶段可以放在一起并行执行,那么就把这三条指令合并成一条超长指令,然后超长指令字CPU就会在EX阶段把任务分配给三个EX部件。
精讲指的是,我们要结合一些指令,具体研究其执行和数据流动过程,所以下面这个图中的锁存器要更加清晰,
梦回流水线CPU设计的那段难过的日子:
接下来依次解读不同的指令:
运算类指令。如下图,在MIPS指令集中,运算类指令里面不存在内存的直接寻址,只允许用立即数和寄存器,运算指令和内存毫无关系,所以M阶段一定是空段。
但是注意,M阶段虽然是空段,但是并不代表啥都不干。M阶段的周期中,数据从EX阶段的ALU结果锁存器流入M阶段的一个锁存器,这个锁存器就是用来对齐时钟周期的。
再看LOAD和STORE指令,在RISC指令集中,只有这俩指令和内存有关系。这里默认DataCache一定命中了。
LOAD指令是唯一一条用满5个部件的指令,其他指令都有空的。
STORE指令中,Store锁存器终于派上了用场。在EX周期内,A和imm送到ALU计算,B送到Store锁存器中。之后M阶段写入内存就可以。
STORE指令没有W周期,不需要修改寄存器的值。
涉及到PC的指令比较特殊,条件转移指令和直接跳转指令都是不需要写回寄存器的,只需要把PC改过来以后,后面的阶段就都不需要操作了,本质上是一样的。
那么就有两个问题:
最后来一道例题:
问题1比较简单,就是数据相关。问题二涉及到流水线的结构本身,即锁存器。
在ID阶段插入bubble的时候,不可以取新指令,防止指令寄存器被覆盖。只有等当前指令ID阶段结束后,下一条指令才可以进来。
有兴趣可以回顾一下体系结构那篇对应的文章,会有更深入的理解(现在我是看不动了)。
SISD
是最简单的,就是我们前面说的CPU。
那这个I和D的意义是什么呢?指令流和数据流,指令流就是一串连续的指令序列,数据流同理。
为什么要强调连续呢?假如一个单进程双线程CPU核心执行两个任务,两个线程对应两串指令序列,但是他们是交错运行的,同时仍然只是一串在执行,所以仍然是SISD。
什么叫数据流的连续呢?就是说来源于一个储存器的,就是一条数据流。
我们顺着推导SIMD
,既然是MD,那么就肯定有多个储存器,这才能产生多个数据流,这些储存器是局部储存器,每个局部储存器都对应一个ALU,MDR,MAR。
这些运算部件与储存部件,被一个CU整体管控,共享一个主储存器。
后面说的向量处理器,其实是SIMD的一个加强版。
MIMD
其实主要就是现代的CPU,多核处理器。L1L2缓存属于核心,而LLC(一般是L3)缓存所有核心共享。
MIMD除了多处理器系统
外,还有一种多计算机系统
。多计算机系统一般用于分布式场景。
其区别在于,LOAD指令只能访问当前计算机的储存,而要想访问其他计算机,就要先把数据传到当前计算机的主存。
至于MISD
,这种计算机不存在。
除了基本的四种处理器以外,还有一个向量处理器,向量处理器是SIMD的进阶。
这种处理器是比较高级的,ALU是向量的,寄存器组也是向量的,指令也是向量指令。因为数据读写量大,所以主存都是多端口的交叉储存器。
最后就是多核处理器和共享内存多处理器,其实就是MIMD,只是说法不同,侧重不同。多核侧重于核心是多个,而共享内存侧重于多核心共享一个内存。
软件多线程需要进行线程切换,需要保存和恢复寄存器组状态,代价较大。
硬件多线程代表着资源的冗余,真正的并行。因此需要多个寄存器组,多个IR,多个执行部件。
实际中,硬件多线程有三种方式
对于硬件多线程,你的猜想可能是左边的IR放线程1的指令,右边的IR放线程2的指令,实际并非如此。
细粒度方式下
,同一时间执行一个线程的两条指令。这不是和软件多线程一样了吗?确实很像,因为线程之间是不并行的,但是我们不需要切换寄存器组
,所以开销很小,这是硬件多线程的本质。
粗粒度多线程
的区别仅仅在于切换时机不一样,细粒度是每周期切换,而粗粒度只有在流水线阻塞才切换,至于代价为什么大,记住就行,不必深究。
同时多线程
才是我们真正理想的多线程,实现了真正的线程级并行。
计网局域网里面学过总线拓扑,其实总线和局域网一个道理,原理很简单。
定义
:总线是一组能为多个部件分时共享
公共信息的传输线路
这样的结构同时只允许一个写(加压),可以多个读,其具有两大特点:
之所以用总线,是因为外部设备逐渐增加,专用连线太死板,不可以灵活地增减设备。
总线的特性和计网的物理层一模一样,说白了计网物理层的线缆本来就是总线的一种(通信总线)
串行总线和并行总线很难说谁好谁坏,各有千秋吧。
串行总线成本低,抗干扰。缺点就是数据的拆分与装配(串并转换)需要专门的部件。
并行总线成本高,没那么抗干扰,优点是逻辑上便于实现。
最开始并行总线是要更快的,毕竟同时传输的数据量是串行的n倍,但是随着总线频率不断上升,并行总线抗干扰的劣势就凸显了,频率提不上去(否则就会干扰),而串行总线可以肆无忌惮地提升频率,速度就被磨平了,所以谁快谁慢说不准,未来串行总线或许会取代并行总线。
片内总线,我们之前学CPU的时候,内部连接有两种方式,一是单总线方式,二是专用通路结构。无论是哪种,都是片内总线。
补充:数据通路是逻辑上的通道,而总线是物理媒介。
系统总线。数据总线是双向的,而地址总线一定是CPU发出,所以是单向的。控制总线是双向的,这是因为主存/外设也可以向CPU反馈信号。
通信总线(外部总线)。网线就是一种。这里有一个疑点,是不是可以在计算机外部插拔的都算外部总线,比如数据线?TODO
系统总线负责连接计算机部件,非常复杂,所以也有很多种设计思路。
首先是单总线
。注意,单指的是一组,包括数据线,地址线,控制线,而不是一根。
优点就是简单,缺点就是速度慢,尤其是会有很大的速度差,慢速设备(IO)占领了总线后,会浪费总线性能,这也是速度慢的根源。
为了平衡速度,就有了双总线
+通道
的模式。
通道可以理解为慢速设备的一个cache,是一个阉割版的CPU,通道使用IO总线
统一管理慢速设备,把慢速数据集中起来,然后统一送到主存总线
,不浪费总线性能。
这种模式是最快的。
此外,所谓的突发传送
,原理就是局部性原理,因为空间局部性,所以CPU给一个地址,内存一次性读出连续的数据送到CPU,就很合理。
双总线模式下,所有的IO设备都通过通道管理,走IO总线,然而有一些高速IO设备的速度就被拖慢了,比如硬盘。
这些设备速度比较特殊,直接挂在内存总线会拖慢速度,但是挂载IO总线又浪费,所以三总线
模式给高速IO设备专门开了一个DMA总线,通过DMA部件直接送到内存中。
其实去掉高速IO设备后,剩下的IO设备传输的数据量是很少的,用通道就又浪费了,不如直接挂在IO总线上直连CPU,响应速度更快,也不拖累整体速度。
以上就是三总线的思路。其优点在于能够平衡各部件的效率,让IO效率更高,缺点就是会拖慢内存总线,降低系统工作效率。
这是因为,同一时间只能有一条总线执行。比如内存是单通道的,主存总线与DMA总线不能同时对主存进行存取,更深层次的原因比较复杂,就记住这个就可以了。
现代CPU结构,把北桥集成到CPU中,然后设置南桥芯片,让PCI通道挂载在南桥上,让PCIE跳过南桥挂在北桥上。
总之,距离CPU越近,速度越快。
八大指标。重点注意两个周期的理解。
传输周期(总线周期)
,完成一次总线传输需要的时间。
时钟周期
一般是受到CPU时钟控制,有时候也受CPU北桥控制(分频)
这两个周期的关系,可以是一个传输周期=多个时钟周期,可以是一个传输周期=一个时钟周期,可以是多个传输周期=一个时钟周期。
第三种怎么理解呢?传统的数据是在上升沿传输,而有一种技术(似乎叫SDDR技术),可以在上升沿和下降沿都传输一次,所以一个时钟周期对应两个传输周期。
之后的频率就是周期的倒数了。
至于位宽,带宽,注意是用工作频率
乘。而且这个是信号率,不是有效信号率。
总线复用。可以节省成本,但是速度下降,比如如果有数据+地址线,那么一个时钟周期就可以把数据和地址都传过去,但是复用以后就需要两个周期。
信号线数=所有总线的信号线之和。
最后来道例题。注意,传送一次数据需要用到一个传输周期,而我们题目告诉的是时钟周期,要进行转化。
比较怪的地方在于,发送一个首地址就得1个时钟周期,但是发送一次数据只要半个时钟周期,为什么这里不统一?TODO
这一节比较关键,我们前面说的都是宏观的指标和特性,为什么会有这种特性,还要落脚到如何传输这个问题上。
总线传输要经历4个阶段,在传输阶段,主从双方如何配合节奏,这就是总线定时,这实际上是一种传输协议。
接下来来依次看一下吧。
直接看时序图。
同步通信的前提是主设备已经申请到了总线使用权。之后经历如下4个时钟周期:
这里忽略总线仲裁的时间,那么一次完整的传送实际上就是上面那4个周期,构成一个总线周期。一个总线周期传送一次数据,部件之间不需要相互等待,只需要用一个总时钟调控就可以。
优点是速度很快,逻辑简单。
缺点在于,这种同步是强制同步,没有考虑设备本身的承载力,如果主存(被读的)频率比较低,那么有可能数据还没有准备好,而且也没有时间检验数据正确性。
因为不可靠,所以适用于总线长度较短的情景,信号比较可靠。因为是强制同步,所以最好让所接部件的存取频率比较接近。
异步通信没有一个统一的时钟,时钟蕴含在传输的信息之中。分为三种模式:
异步方式的优点在于灵活可靠,可以自适应时间配合,缺点是更复杂更慢。
把同步和异步结合起来就是半同步。
基于同步模式,假设数据没准备好,从设备就发出一个WAIT信号,此时主设备就会增加WAIT周期,保持住地址和命令信号。
等WAIT信号撤了,下一个时钟周期把数据放上数据线,这个总线传输周期就可以继续推进了。
说白了,就是让给从设备增加了一个异步通信机制,一旦数据没准备好,就进行一次异步的反馈(WAIT信号),结果就是总线传输周期的长度自适应可变。
其实半同步仍然是有缺陷的,虽然引入WAIT信号可以保证数据读取准备好了再读取,但是在等待数据准备的时间内,总线是空闲的。
所以分离式通信分为两段占用期和一段释放期:
在分离式中,从设备也具有申请总线的能力,而且具有记忆主设备的能力,所以也算是一种主设备。
分离式通信算是这几种通信方式的集大成者,比异步快,比同步稳定,总线的利用率还高。
总线仲裁,似乎说不考,那我就不看了,简单感觉一下就是,这玩意似乎不消耗时钟周期,怪,反正我没看到题里算时钟周期算过仲裁的时间。
总线标准有空可以看看,有助于理解计算机部件。
IO设备在计算机外部,包括我们插的硬盘其实也算是外部设备,和外部设备的交流就是IO。
IO要经过IO接口,这个接口实际上承担了IO控制器的作用,一般来说,IO控制器都是集成在主板上的(南桥)
这一节把os那边的知识迁移过来,从宏观层次介绍不同IO控制方式的逻辑,后面会针对性的从硬件角度逐一精讲。
上下两图介绍了程序查询方式和程序中断方式。这两种方式下,数据都是要经过CPU才能到内存的。
为了便于分析,将IO分为两个阶段:
在程序查询方式
中,IO准备的过程中,CPU被阻塞,不断查询。IO传送的时候,CPU用于传送数据。也就是说,程序查询方式中,IO准备和IO传送都要持续占用CPU。
在中断方式
中,IO准备的过程中,CPU去干别的了。只有在IO传送的过程中,CPU才执行中断处理程序。中断方式把CPU从IO准备过程中解放了出来。
中断方式只是解放了IO准备阶段,如果IO传送阶段比较费时间(比如大文件IO),就会持续占用CPU,DMA方式
将CPU从IO传送阶段中解放了出来。
DMA具体原理是将控制和传输分离。控制字是通过IO总线发出的,具体数据传输是通过DMA总线转移的。
再来进一步分析一下DMA工作的时序。
如上,CPU就实现了一个块的读写,对于一个大文件,其由若干块组成,CPU即使通过DMA方式也要执行若干次中断,所以说DMA方式是解放了IO传送阶段,但是不是很彻底。
或者说,DMA方式只是解放了一个块的IO传送
如果文件很多,DMA方式就受不了了,这种情景常见于云盘服务器,大量的高速IO请求下,即使DMA也要产生大量的CPU中断,所以需要进一步解放。
通道
方式应运而生,通道是一个阉割版的CPU,但是比DMA要更牛逼。
DMA一次任务是传送一块,而通道可以直接执行内存中关于IO的指令流,即预先编辑好的通道程序
,从而执行一整个任务,比如一个大文件的读写。
总的来说,你会发现从中断方式,到DMA,到通道,这三者无非就是增大IO控制器发出中断请求的间隔,从一个字,到一个块,到一个文件。
IO硬件已经说了很多了,前面我们主要说了控制器和总线,后面会将外部设备。
IO软件里先说一下IO指令吧。IO指令其实也属于CPU指令集,也有操作码
,告诉CPU对IO接口的操作。命令码
具体细化,指明IO接口要对外部设备做什么操作,目标吗
指出要对哪个IO设备操作。
IO软件里要重点说一下驱动程序。不同厂家的硬件不一样,所以同一个功能对应的命令吗各不相同,此时就需要驱动程序把同一个OS操作转换成自家的命令吗。
比如都是亮灯指令,A键盘的命令吗和B键盘命令吗就是不一样的,这就需要软件通过驱动来把OS的同一个操作转换成自家的硬件指令。
简称外设。一听这个词我可就来劲了,炫一炫我组的两把键盘:keydous nj80+极地狐轴+只此青绿键帽,keydous nj68+冰静轴+带蓝敦煌键帽。
外设知识可真是没少学,很实用很有意思。
先说说显示器参数。
灰度级和色深有联系。灰度级=颜色数量,所以8bit色深的RGB= 2 24 2^{24} 224灰度级(24位)
VRAM的储存容量=一张图片的容量=一张图片的像素点×每个像素点的bit数
上面只是VRAM的最小容量,VRAM实际会很大,因为要还要存放即将渲染的图片,所以现在的显卡显存都挺大。对于集显来说,就占用内存当显存。
说回显示器分类。
LED和LCD。LCD的单元是液晶,LED的单元是发光二极管。现在手机使用LCD屏幕的很少,虽然这个很护眼,但是画面还是差一些,更多的使用的是OLED屏幕,属于LED大类,LED的强势点就在画面。
CRT显示器最为原始,可以从中解读出一些原始的显示器的显示原理。
CRT显示器的原理是个考点,单独拿出来记录一下。
最原始的显示器是字符显示器
,其显示原理并不像现在的显示器一样自由,是吧一个屏幕分成若干方块,每个方块里放一个点阵,点阵可以显示字符。点阵的显示基于位代码矩阵。矩阵里面1代表点阵亮。
这种亮暗信息构成了一个字,一个位代码矩阵对应一个字,而这种bit位矩阵可以用16进制储存,即字模信息(字形码)
其实字符显示器只是一种思想,细节上可以衍生出各种各样的显示器,比如CRT显示器,其显示过程如下
你会发现,其实显示原理大差不差,只需要变一下转换电路,就可以转换成LED,LCD的控制信号。
如果想要字符的显示更加自由,不被拘束在某一个方框中,可以再增加位置信息,总之就是这么个思路。
图中的分类都是针对CRT显示器的。
也就是说CRT显示器可以分成字符显示器,图形显示器(矢量图形,对颜色之类的没有要求,重在形状),图像显示器(显示内容丰富多彩,色彩要求高)。
还可以分成光栅扫描显示器和随机扫描显示器。
击打式打印机,物理击打会留下痕迹,仿制的时候要同时仿制油墨和痕迹,难度大,公家单位用的多。
非击打式使用喷墨+静电控制技术。
针式打印机其实就是击打式打印机的一种。
喷墨打印机和激光打印机容易混淆,激光打印机的控制是通过激光实现的,激光通过一系列操作在纸上留下字形的静电,最后仍然要喷墨。
所以可以理解为,激光打印机是喷墨的加强版,字形更清晰。
数据寄存器起到缓冲作用,可以平衡内外速度,同时这个缓冲也需要串并转换电路的支持。
状态寄存器的值会随着IO状态而改变,这个是内部电路负责实现的。
控制寄存器负责储存控制信号,时钟信号。
控制寄存器是正向的,状态寄存器是反向的。
IO接口的核心是那个控制芯片,而其两端分别连接主机和外部设备,连接主机的叫内部接口,连接外部的叫外部接口。
比如一个USB模块,外面有好多USB接口,但是内部接口就只是一条金手指,内外之间有一个控制芯片,里面就是下图的电路。
挺复杂的。
你可以把IO控制器理解为一个简陋的CPU,其中控制逻辑模块就对应CU,与每一个部件都有双向交流。
三条线的作用:
举一个打印的例子:
需要补充的是,之所以把控制和命令两个寄存器做到一起,是因为两个在时间上是错开的,所以不冲突。
如果有多个IO设备,如何指明呢?
一种思路是通过地址线指明外设,但是这就和寄存器地址冲突了,所以要分两次。
另一种思路是把外设信息融入到寄存器地址中,具体做法是给每一个设备分配一组寄存器,然后给每一个寄存器都编一个地址,这样一个地址就可以同时指定寄存器种类和外设了。下面会具体给出案例。
可以看到,只要我指定了一个寄存器,就附带指明了其对应的IO控制器(甚至还可以附带指明其对应的外设,如果IO控制器对应的外设有很多)。总之原理是一样的。
统一编制常见与在risc指令集中,指令少,所以就用地址区分。其LOAD和STORE指令不仅可以访问内存,还可以访问IO端口(寄存器)
独立编制常见于CISC指令集中,通过指令来区分,我是要访问内存,还是访问IO端口。
统一编制的缺点在于,一旦确定了IO端口的地址范围,就不可以再更改,说白了就是绝对地址。
下面这些优缺点,我感觉本质上就是统一编制牺牲了内存,成全了IO,更简单,灵活,慢,而独立编制死板,复杂,快。
需要说的是,统一编址里面的“所有访存指令可以直接访问端口”,这个优点对于RISC来说并没有卵用,本来也就两条,真正有用的是CISC架构,所以有的CISC也是用的统一编制,就是为了这个灵活性。
数据传送方式分类是针对外设和接口。主机和接口一侧,如果使用串行总线,那么也是串行传送的,不一定总是并行。
主机访问方式分类是针对主机和接口。其实就是按照CPU获取状态字的方式分类。
IO状态分为可用和不可用,以打印机举例,打印或者未激活都是不可用,打印完当前字符就是可用。
轮询就是使用IN指令循环阻塞CPU,不断获取状态字,直到获取到了可用状态,就退出循环执行后面的任务。
补充一下,下图中启动外设需要写入控制字,其实结束外设也需要写入控制字。在进行具体的打印任务时,是一个大循环,其中轮询是小循环。CPU发出IO命令后状态字变为忙,CPU进入轮询循环。IO完毕后,外设会反馈,将状态字修改为空闲,此时轮询小循环检测到空闲,退出轮询,CPU继续发送IO命令。
不考虑处理时间,指的是不考虑CPU将数据从DBR挪到CPU寄存器的时间。
轮询间隔是可以自行设置的,比如每秒30次,这个就是所谓的采样率,合理设置轮询间隔,可以减少无意义的轮询次数。
每32bit就查询一次,这个说法听起来有点奇怪,CPU怎么知道传输满了32bit呢?实际上CPU不知道,32bit的意思是,在我已知传输速率以后,我人为指定一个时间间隔,可以保证这个时间间隔内IO设备已经把DBR(32bit)填满了,我这个时候询问一次就行。
选择合理的轮询间隔可以减少CPU的消耗,两次轮询之间CPU可以去干别的。这就是所谓的定时轮询
。
而我们最开始学的轮询,是独占轮询
,这种模式CPU不停地轮询,不去干别的,忙等。
指令周期的末尾有一个中断阶段,用于检查是否有中断请求信号。如果CPU检测到有中断请求
,那么就进入中断响应
流程,决定响应哪一个中断。确定了中断后,开始具体执行中断,分为准备,执行,善后三个阶段。
这里放一个8259A的结构图,这个给有接口技术基础的同学作参考,后面的讲解也会按着这个图来。
中断是把CPU当前在做的事情打断了(原子操作)。有时候CPU也不希望被打断,所以就有关中断
来屏蔽中断(PSW中的IF=0)。
原子操作以关中断为基础,进入原子操作前关中断,执行完毕后开中断
然而是否能屏蔽还是取决于中断请求的种类。
具体的中断请求标记储存在INTR中(在8259A中叫IRR),置1代表在请求中断。
在指令周期的中断阶段,CPU会发出命令检查IRR中是否有中断,进行中断流程。
已经有中断请求的前提下,CPU首先检查是否关中断,关中断则无视中断请求信号。
如果开中断,则会进行中断判优,选定中断。中断判优分为软硬件两种方法:
这个硬件排队器确实挺有意思,这里仅作补充:当前优先级的只要是1,那么取反传递给下一个的必然是0,与后必然是0,到此为止这个中断的输出结果就已经确定了,后续的也是如此。
反过来,如果是0,那么取反传递给下一个的就是1,不影响下一个。
总的来说就是,输入=1则顺着屏蔽后续所有,0则不影响后续。当然,这种排队比较死板,就是连在左边的优先极高,事实上后面更复杂的排队(中断屏蔽字)应该也是以这个为基础的。
这个优先级是以什么原则设定的呢?
选定中断后,8259通过INT引脚向CPU发送中断请求,如果CPU没有关中断,则会通过INTA引脚进行第一次反馈。
此时进入中断处理阶段,被处理的中断,其IRR对应位置0,把ISR对应位置1,表明正在处理。
执行的时候,先执行中断隐指令
,进行预处理,然后执行中断服务程序
。等执行完毕后,CPU进行INTA第二次反馈,8259把ISR复位,一次中断彻底完毕。
中断隐指令过程如下:
找入口使用中断向量表,向量表是固定的,指向中断处理程序。ISR通过硬件电路可以生成向量地址,通过数据总线送到CPU里,CPU再通过地址找到中断服务程序入口。
关中断可前可后,因为它和另外两个操作是同时的。
再说中断服务程序。注意,中断服务程序≠中断程序本身,仍然有保护和恢复的额外操作。
前面讲的是单重中断,一个中断在执行的时候,不可以被再次中断。本节介绍多重中断。
多重中断的核心在于中断屏蔽字,此时前面的IMR就派上用场了,IMR储存当前正在执行的中断源的中断屏蔽字。
中断屏蔽字
:当前中断正在执行
的时候,要屏蔽的中断
中断屏蔽技术本身是用来屏蔽的,在单中断场景中没有意义,因为已经关中断了,无差别屏蔽。只有在多重中断场景中中断屏蔽字才有用,可以用于手动调整优先级:
优先级小于等于自己的都屏蔽,设为1。优先级高于自己的设为0
来道例题。
到此为止,必要的知识已经讲完,再讲点细节的。在多重中断中,ISR里面可能有多个1,起到了记忆的作用。
当最高1对应的中断执行完毕后,ISR清除最高位的1,如果此时ISR还有1,那么顺位执行当前最高优先级的中断。
了解了中断的原理和过程后,我们书接上回,直接给流程图,有几个注意点:
来道例题:
第一小问,如果要严格揪着算的话,最后一个字符取出来以后,CPU还需要5条指令进行中断结束的操作。其实加不加都可以,公式写清楚就行,只是理解方式的不同,你可以认为取出了数据,IO就结束了,也可以严格点,等最后一个中断结束才算IO结束。
第二小问,CPU有一部分时间是在干别的,即启动到请求的这一段时间,剩下的时间才是CPU真正用于IO的工作时间,这个时间没有歧义。
无论是定期轮询,还是中断方式,这种额外的损耗频率都比较高,以字为单位。DMA方式将这种损耗进一步降低,以块为单位进行中断,而在一个块的内部,损耗仅仅是挪用一个内存存取周期(三总线窃取情况)。
如下给出一个单总线结构的DMA的大致工作流程:
接下来细化一下,研究一下DMA结构。看起来挺复杂,实际还好。
和上面的简略图相比,额外有一个控制/状态逻辑,这个负责DMA的总控。
DMA请求触发器,如果数据就绪,则触发器=1。
触发器=1后,经过控制状态逻辑就可以向CPU申请总线使用权,走得是HRQ线,如果CPU允许,则通过HLDA线通知DMA,你可以用总线了。之后就是数据传送。
等WC=0后,会发送溢出信号到中断机构,进而申请DMA结束中断。
如此就传完了一个块,如果再想传一个块,那CPU就再启动一次DMA
上面说的都是单总线结构,CPU决定总线控制权,在DMA使用总线的时候,CPU啥也干不了。
三总线结构下,当DMA向内存传数据时,虽然CPU不可以向内存传数据,但是CPU还可以用IO总线进行其他IO操作。
这种方式还有一个优点,就是传输一个块的数据过程中,CPU完全不需要管,单总线情况下还得负责回应DMA的总线申请请求。
说实话下面这三种方式,DMA周期挪用碾压前两种。
第一种方法还不如中断和定期轮询,失去了DMA的意义。第二种方式同样如此,一次性切割了主存50%的周期,如果CPU或者DMA在自己的周期内部不去访存,那么就是浪费了。
使用窃取方式,以DMA优先,可以充分提高主存利用率,有点动态调控的感觉。
注意,三总线情况下,CPU在DMA访存时,只是不能写存了,还可以干别的。
对比着理解。
首先看轮询和中断方式。
独占查询就是忙等,没有优势。但是定时查询和中断的关系,好比同步方式和异步方式,同步采用时钟,定期检查,而异步使用通知的方式。在这段时间之外,CPU都是可以去工作的,这两种模式孰优孰劣,不好评价。
同步模式下,虽然一次查询费时间比较少,但是整体上容易踩空,需要设置合理的间隔,太长了数据被覆盖,太短了数据还没准备好,浪费CPU周期。异步会产生中断程序的额外消耗,中断隐指令和中断服务程序的准备和退出都很费时间。
再说DMA和中断方式。
DMA整体上是碾压中断的,尤其是在传送大文件的时候。中断唯一的优势就是更加灵活,处理异常的单位是一个字,而DMA只有一个块传完了才能上报结果。
所以实际使用中,低速设备采用中断方式,保证可靠性,而高速设备使用DMA方式,提速。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。