前言:
本文是笔者初学计算机组成原理的笔记和一些心得,难免会有部分疏漏和错误,还请各位读者积极指出,不吝赐教。
有一些内容是笔者认为对自己暂时没那么重要的部分,就没有放上去,具体的内容可以查看相关的书籍。
还有要补充的就是,计算机网络的学习记录将会分为上中下三部分,这一部分,也就是上,主要是涵盖了从计算机概述到存储系统的初步内容。
也欢迎访问我的博客,其中也发布了这篇文章conqueror712.github.io/
事不宜迟,我们开始吧!
课程内容:
- 计算机概述
- 性能指标、层次结构
- 数据表示与运算器
- 数据的表示、运算器
- 浮点运算方法
- 存储器
- 存储器系统、存储器控制器
- Cache、虚拟存储器
- 指令系统
- 指令集架构、寻址方式
- 典型指令
- 中央处理器
- CPU模型机、指令执行过程、微程序控制器
- 硬布线控制器
- 总线
- 总线分类、总线仲裁
- 总线定时
- 外围设备
- 磁盘、显示
- 输入输出系统
- 程序查询、中断、DMA
- 通道方式
零、导论
一、计算机系统体系结构
一般来说,组成一词比体系结构要更加偏向底层;
计算机的发展历史:
第一台电子数字计算机:ENIAC(1946) (电子管)
冯诺依曼结构
以运算器为核心
"存储程序"的概念是指将指令以二进制代码的形式事先输入计算机的主存储器,然后按其在存储器中的首地址执行程序的第一条指令,以后就按该程序的规定顺序执行其他指令,直至程序执行结束。
第一台采用冯诺依曼结构的计算机:EDVAC
现代计算机结构
以存储器为核心
CPU = 运算器 + 控制器(合二为一)
注意,这里的主机和很大的那个主机箱子不是一个概念,这里的主机只包括CPU和主存。
主要硬件结构的简要介绍
主存储器
包括存储体、MAR(存储地址寄存器Memory Address Register)、MDR(存储数据寄存器Memory Data Register)
读取一个位于存储体内的信息的时候,顺序是->MAR->存储体->MDR->
存储体内部的大致结构如下:
存储字长的例子 以及 字和字节的区别:B与b是不一样的,字与字节是不一样的,字节是死的,字是活的。
运算器
控制器
完成一条指令:PC取指令->IR分析指令->CU执行指令
。其中前两步也被称为取指操作,最后一步也被称为执行操作。
除此之外,还有条件码寄存器(Status Register),用里面的一个一个标记位,存放CPU进行算术或者逻辑运算的结果。
计算机的工作过程示例:
指令的执行分为必经步骤和有选择性的步骤。
需要特别说明的是,现代的计算机往往会把MAR和MDR集成到CPU里面去,所以有可能有的题目会画在一起。
还有一点:在高级语言中的if
else
for
实际上会被编译成cmp(compare)
和jne(jump if not equal)
,所以本质上是goto
;
函数调用的原理:Stack LIFO
总结:
计算机系统的多级层次结构
编译程序:
将高级语言编写的源程序全部语句一次全部翻译成机器语言程序,而后再执行机器语言程序(只需翻译一次)解释程序:将源程序的一条语句翻译成对应于机器语言的语句,并立即执行。紧接着再翻译下一句(每次执行都要翻译)。
计算机的性能指标
储存器的性能指标
需要注意的一点是:在实际情况中MAR的位数不一定能计算出存储器的真正大小。理论值是峰值。
MAR位数反应存储单元的个数(最多支持的个数),例如32位MAR,最多存储单元个数:2^32
MDR位数 = 存储字长 = 每个存储单元的大小
故总容量 = 存储单元个数 * 存储字长
,单位是bit
,若转换成字节则/8
;
K: 2^10
M: 2^20
G: 2^30
T: 2^40
CPI (Clock cycle Per Instruction):执行一条指令所需的时钟周期数;
不同的指令,CPI不同。甚至相同的指令,CPI也有可能发生变化;
所以通常提到CPI的时候都是指的平均值;
执行一条指令的耗时: 指令数 * CPI * CPU时钟周期(ClockCycleTime)
IPS (Instructions Per Second):每秒执行多少条指令;
IPS = 主频 / 平均CPI
有时,也会在IPS FLOPS前面加上K M G T,有别于刚才的存储大小;
K = Kilo = 1e3
M = Million = 1e6
G = Giga = 1e9
T = Tera = 1e12
FLOPS (Floating-point Operations Per Second):每秒执行多少次浮点运算
数据通路带宽:
数据总线一次所能并行传送信息的位数;(各硬件部件通过数据总线传输数据)
吞吐量(率):指系统在单位时间内处理请求的数量
它取决于信息能多快地存入内存,CPU能多快地取指令,数据能多快地从内存取出或存入,以及所得结果能多快地从内存送给一台外部设备。这些步骤中的每一步都关系到主存,因此,系统吞吐量主要取决于主存的存取周期。
响应时间:指从用户向计算机发送一个请求,到系统对该请求做出相应并获得他所需要的结果的等待时间。
通常包括CPU时间(运行一个程序所花费的时间)与等待时间(用于磁盘访问、存储器访问、I/O操作、操作系统开销等时间)。
值得一提的是,一般来说,我们会定义性能 = 1 / 响应时间
功耗问题:
功耗 ≈ 1/2 × 负载电容 × 电压的平方 × 开关频率 × 晶体管数量
所以有了通过并行提高性能的方式。
综上所述,我们可以用一个基准程序来测量计算机处理速度,也就是所谓的跑分软件
计算机的一些基础名词:
机器字长
计算机一次整数运算所能处理的二进制位数
微处理器
微处理器是在单个硅片上实现的CPU,围绕其构建的计算机被称为微机。
硬盘与储存系统
如今,半导体硬盘 即固态SSD开始取代传统的机械硬盘,这有助于硬盘的性能(主要是访问时间)。
如果不能将程序储存在合适的储存器中,CPU的速度再快也毫无用处。
**便宜的Flash储存器(俗称闪存)**使得MP3、数码相机、电子书和ipad等数码产品获得了巨大成功。
图灵完备
如果一台计算机能够模拟图灵机,那么它就是图灵完备的。
如今的计算机都是如此了。
总线
一些计算机系统使用总线扩展接口或桥接技术,以此来在不同类型的总线之间交换数据。(USB Type-C转接就是如此)
信息
信息这一词表示计算机中的指令和数据
如右图,双向箭头代表这条通路商的信息是双向的;
分为读周期和写周期,会让信息流的方向不同。
寄存器
寄存器是CPU内部用来存放一个单位的数据的存储单元。
由多个**触发器(Flip-Flop)和锁存器(Latches)**组成的简单电路。
寄存器通常用它所保存的数据的位数来描述(8位 16位 32位 64位)。
寄存器和存储器中的字存储单元没有本质区别;
实际差别在于,寄存器位于CPU内,它的访问速度远远快于访问CPU外的存储器。
时钟
绝大多数数字电子电路都带有一个时钟,用以生成连续的间隔固定的电脉冲流。
之所以被称作时钟,是因为可用这些电脉冲来计时或确定计算机内所有事件的顺序。
时钟提供了脉冲流,所有的内部操作都是在时钟脉冲的触发下进行的;
时钟的频率是决定计算机速度的一个因素,目前一般为3.2GHz(32亿)。
事件由时钟信号触发的数字电路被称作同步的,因为它们由时钟信号来同步。
有些事件则是异步的,因为它们可以在任何时间发生。
例如,如果移动一下鼠标,它会向计算机发送一个信号,这是一个异步事件(先动,后发);
然而,计算机可以在每个时钟脉冲检测鼠标的状态,这是一个异步事件。
后续还会学到真正的计算机如何在完成一条指令之前就开始执行一条新的指令(我觉得它叫并行)。
计算机会将数据和指令放在不同的存储器中(或使用不同的总线传输数据和指令);
这样的结构叫做 哈佛体系机构。
光刻机
原理简要示意图:
一层一层更改顶部的化学覆盖层,然后用光来照射...
普适计算
简单释义:计算是无处不在的
也被称为功耗感知的计算(power-aware)——低功耗计算
特征:趋同性
实例:eBook中的E-ink,即电子墨水
存储程序计算机的概念
计算机的存储器应当视为一个存放信息的表格或目录(有序的)。
人类的记忆往往不是有序的,是神经元之间的复杂连接构成的非线性结构,这种结构并不利于计算机检索,故计算机采用连续的储存方式。
寄存器传输语言RTL
由于使用文字描述计算机的操作很不方便,故有了RTL,这样可以更加容易地定义计算机内发生的操作。
注意:RTL并不是一种计算机语言,它只是一种用来定义计算机操作的符号。
RTL可以很方便地区分存储单元的地址和它的内容,例如:
[15] = Max_Run
含义是:地址为15的存储单元保存了变量Max_Run的值
[15] ← [15] + 1
含义是将地址为15的存储单元的值+1,并将结果写回地址为15的存储单元
其中 ← 符号的含义是数据传送
自计算机诞生,直至20世纪70年代,计算机采用的运行过程都是单线程顺序的,而今天的计算机已经可以做到并行和乱序地完成内部操作了。
在当时,机器上执行一条指令需要至少两次访存:
- 读取指令
- 从存储器中读出指令需要的数据 || 将它之前的指令产生的或修改过的数据写回存储器
这一过程也被称为读取/执行周期的两阶段模式。
这一情况当时人们用冯 · 诺依曼平瓶颈来表明CPU与存储器之间的通路是存储程序计算机的制约因素之一,下面是一个可视化的例子:
两地址指令:
采用两地址指令来进行两数之和操作会破坏其中一个地址所存储的数的原始值,不难理解。
单地址指令:
由于指令中只提供了一个操作数地址而指令至少需要两个地址,处理器不得不使用一个不需要显式地址的第二操作数。第二个操作数来自于CPU内一个叫累加器的寄存器,由于所有的操作数都要流过它累加器,导致现在很少用了。
计算机的分类
- 存储器 - 存储器型
- 寄存器 - 存储器型
- 寄存器 - 寄存器型
计算机系统概览
计算机科学家们将存储器是做一个巨大的通过地址访问的数组。
存储墙一词,用来说明如今存储性能远落后于处理器性能,最终会制约处理器的性能。
存储层次:
- 寄存器存放处理器的工作数据;
- Cache是缓存常用数据的快速存储器,是决定计算机性能的关键模块;
- Cache系统与计算机的地址总线和数据总线相连,监听着CPU与存储器之间的事务;
- 有些Cache分多级,查找顺序为一级 → 二级 → ... → 主存;
- DRAM存放工作数据块(动态随机访问存储器Dynamic Random Access Memory);
- 易失性半导体:掉电的时候其中的数据都会丢失;
- 硬盘用来保存程序和数据;
值得一提的是,虽然硬盘的容量是寄存器的4000万倍,但是速度却比寄存器慢2000万倍。
总线
总线将计算机或外部设备的两个或多个功能单元连接在一起并允许他们相互交换数据,例如CPU与显卡之间的总线。
仲裁器
一些系统使用仲裁器来决定允许在总线冲突的时候哪个设备继续工作,其余设备等待之。
宽度
一般用并行数据通路的数量来定义总线的宽度;
例如一条64位宽的总线一次能够传送64(8byte)bit的信息;
带宽
总线带宽是衡量信息在总线上的传输速率的一项指标;
单位一般是b/s或者B/s;
在保持数据传输率不变的情况下增加总线的宽度,可以提高带宽;
延迟
延迟是从发出数据传输请求到实际数据传输的时间间隔;
总线延迟通常包括传输开始之前进行总线仲裁的时间;
二、数据的表示与运算
进制、编码与乘除法
每个二进制数位称为一个bit
进位计数制
需要注意的是:有的十进制小数无法用二进制精确表示,如0.3
BCD码
Binary-Coded Decimal 用二进制编码的十进制 数字电路梦幻联动
- 8421码 (需要掌握加法运算)
- 余3码
- 2421码
首先我们来看8421码,用4位二进制来表示0~9十个数,显然是有冗余,因为2^4 = 16,多了6种状态,不过这不重要。
假设我们要表示985,那么用8421码来存储就是:1001 1000 0101,8421码是一种有权码,每一位的权值固定。
8421码进行相加,实际上也是对应位相加,不过在实际做题中可以投机取巧,转化成十进制算完了再转回来()。
很重要的一点是,当加法得到的结果超出了8421码的表示范围时,也就是落在了[1010, 1111]
内,
那么计算机会自动把结果+6
以进位,最后得到的位于原位的四位二进制数就是原本的数的个位(很神奇吧)。
对映射的方式稍作修改就会得到别的编码方式
例如余3码就是在8421码的基础上再+3
,即+0011
,在余3码中,每一位并没有一个固定的权值,称为无权码。
字符与字符串
ASCII码:
汉字编码:94 * 94
GB 2312-80
(只是其中一种方式)
值得一提的是,汉字要输出的话需要用汉字字形码,说白了就是像素画。
带有汉字的字符串,有两种存储方式:大端模式和小端模式;
汉字占两个字节
奇偶校验码
在数据的传输过程中,有可能会发生错误,例如把01
传输成了00
,这种错误被称为位错误;
为了避免这种错误,或者说想要知道错误发生了,我们需要用多一些bit位来存储信息;
例如3bit映射到4个合法状态,就有了4种冗余的非法状态,当接收方获得非法状态的信息时,就能知道发生了错误。
信息 | A | B | C | D |
---|---|---|---|---|
编码 | 100 | 001 | 010 | 111 |
由若干位代码组成的一个字叫码字,比如上表中的100, 001都是;
将两个码字逐位进行对比,具有不同的位的个数称为两个码字间的距离;
一种编码方案可能有若干个合法码字,各合法码字间的最小距离称为**"码距"**,用d
表示;
- 当
d=1
时,无检错能力; - 当
d=2
时,有检错能力; - 当
d>3
时,若设计合理,可能具有检错、纠错能力;
偶校验的硬件实现:
各信息进行异或(模2加)运算,得到的结果即为偶校验位。
只因校验的硬件实现类似,不过是反过来了。
海明校验码
设计思路:将信息位分组进行偶校验,有多个校验位,校验位能携带多种状态信息(对与错,错在哪);
校验位的个数:2^k >= n + k + 1
,信息位n,校验位k,n+k位中任何一位都可能出错,1种正确状态;
海明码求解步骤:(详细过程待补充)
- 确定校验位数量
- 确定校验位的分布
- 求校验位的值
- 检错纠错
海明码有1位纠错,2位检错能力
循环冗余校验码
又称CRC码
主体思想:除法的余数
- 数据发送、接收方约定一个“除数”
- K个信息位和R和校验位作为“被除数”,添加校验位后需保证除法的余数 == 0
- 收到数据后,进行二进制除法检验余数是否为0
- 若余数 != 0 则出错,进行纠错or重传
计算过程:
- 确定K、R以及生成多项式对应的二进制码(生成多项式的系数就是除数)(R = 生成多项式的最高次幂)
- 移位:信息码左移R位,低位补0,也就是在原本的信息码后面填上R个0即可
- 相除:对移位后的信息码,用生成多项式进行模2除法,产生余数
- 检错和纠错:如果最后得到的余数为000,则没有出错
检错:
模2除之后得到的余数的循环节为7,可以对应从1k~7k的出错位(K∈N+)(并不是二进制);
一般来说,CRC码只用于计算机网络的检错,其纠错功能一般不用;
- 可检测出所有奇数个错误;
- 可检测出所有双比特的错误;
- 可检测出所有小于等于校验位长度的连续错误;
定点数与浮点数:如何用有限的Bit位表示尽可能多的信息
对于定点数:
首先我们看看BCD码的缺陷:浪费位数,没法同时表示很大和很小的数
对于浮点数:
我们有科学计数法,用来节省空间;
IEEE标准:(定义了两个基本的格式)
- 用32Bit表示单精度的浮点数
float || float32
- 用64Bit表示双精度的浮点数
double || float64
用单精度类型举例(双精度类似):
s = 符号位 | e = 指数位 | f = 有效数位 |
---|---|---|
1 Bit | 8 Bit | 23 Bit |
特别地,对于指数位,我们用1254映射到-126127上,0和255另有用处;
于是,浮点数就可以表示成:(-1)^s * 1.f * 2^e
浮点数的二进制转化
s + e + f
逐个转化成二进制然后拼接即可
浮点数的加法和精度损失
浮点数加法:(利用半加器和全加器就可以实现)
- 先对齐 再运算
- 对齐指数位原则:
e = max(e1, e2)
- 对其指数位后,有效位应做相应左移右移
在右移的过程中,最右侧的有效位被丢弃掉了,这一步是丢失精度的原因所在。
Kahan Summation算法解决精度丢失
解决"大数吃小数"的问题;
public class KahanSummation {
public static void main(String[] args) {
float sum = 0.0f; float c = 0.0f;
for (int i = 0; i < 20000000; i++) {
float x = 1.0f;
float y = x - c;
float t = sum + y;
c = (t-sum)-y;
sum = t;
}
System.out.println("sum is " + sum);
}
}
/*
其实这个算法的原理其实并不复杂,
就是在每次的计算过程中,都用一次减法,
把当前加法 计算中损失的精度记录下来,
然后在后面的循环中,把这个精度损失放在要加的小数上,再做一次运算。
*/
算术逻辑单元ALU
加法器、乘法器与ALU的改进
半加器:
通过一个异或门计算出个位,通过一个与门计算出是否进位,我们就通过电路算出了 一个一位数的加法。
于是,我们把两个门电路打包,给它取一个名字,就叫作半加器(Half Adder)。
全加器:
由于半加器只能处理1位的加法问题,所以很容易想到,如果要进行多Bit的加法运算,则需要多个半加器组合工作。
我们用两个半加器和一个或门,就能组合成一个全加器。
有了全加器,我们要进行对应的两个 8 bit 数的加法就很容易了。我们只要把 8 个全加器串联起来就好了。
值得一提的是,实际CPU里面的加法器,会更复杂一些,被称为超前进位加法器,这里先不做过多介绍。
至于ALU,则是由加法器再进行组合设计封装的产物了。
NOW, WE HAVE A NEW LEVEL OF ABSTRACTION!
乘法器:
虽然我们可以多次使用加法器来达到乘法运算的效果,但是毕竟时间空间材料都很宝贵,于是乘法器就应运而生了。
首先需要了解的是:二进制乘法
很简单,一张图就能解释:
操作只有2种:复制 or 置0,最后再全部加起来即可
实际上我们并不需要把每一位的运算结果都记录下来,那样的话实在是太浪费材料了,我们只需要维护一个动态的结果即可;
但是这样也有一个显著的缺点:慢!
复杂度有O(N),N为位数;
所谓时间换空间,空间换时间...
电路设计改进——并行加速方法:
实际上就是让多位同时计算,会获得O(logN)的时间复杂度,但相应的,代价是需要更多的晶体管开关来存放中间计算结果。
等待前面的步骤的等待时间,被称为门延迟(Gate Delay)。
我们只要把进位部分的电路完全展开就好了。
我们的半加器到全加器,再到加法器,都是用最基础的门电路组合而成的。
门电路的计算逻辑,可以像我们做数学里面的多项式乘法一样 完全展开。
在展开之后呢,我们可以把原来需要较少的,但是有较多层前后计算依赖关系的门电路,
展开成需要较多的,但是依赖关系更少的门电路。
三、存储系统
存储器的基本概念
**存储介质:**磁芯、磁表面、半导体、光...
存取方式:
- 随机存取——RAM ROM
- 串行访问:
- 顺序存取:磁带
- 直接存取:磁盘
性能指标:
- 存储容量——存储字数 * 字长
- 单位成本——每位价格 = 总成本 / 总容量
- 存储速度——数据传输率(主存带宽) = 数据宽度 / 存储周期
存储器的层次结构
- Cache - 主存层次:硬件实现,解决速度不匹配的问题
- 主存 - 辅存层次:硬件 + 操作系统实现,解决容量问题,逐渐形成虚拟存储系统
寄存器非常快,但也非常小;
CPU Cache使用SRAM(Static Random Access Memory静态随机存取存储器)
**静态:**通电状态数据保持存在,断电丢失
SRAM内1Bit数据需要6~8个晶体管,导致存储密度不高,空间有限,但是因为电路简单,故访问速度很快。
L1 L2 L3三层高速缓存,L1分为指令缓存和数据缓存
L1往往嵌在CPU核心内部,很快
L2不在核心内部,慢一些
L3是多个CPU核心公用的,尺寸更大,访问速度更慢
内存的芯片DRAM(Dynamic Random Access Memory动态随机存取存储器)
相比SARM,密度↑,容量↑,价格↓,数据访问电路复杂度↑,刷新电路复杂度↑,访问延时↑
DARM需要不断**"刷新"**才能保持数据被存储起来。
1Bit只需要一个晶体管和一个电容,数据存在电容里,电容会不断漏电,所以需要定时刷新充电才能保护数据。
SSD(Solid State Drive固态硬盘) HDD(Hard Disk Drive硬盘):最大最慢
各个存储器只和相邻的一层存储器打交道,并且随着一层层往外,容量↑,速度↓,价格↓;
局部性原理:(时间局部性 空间局部性)
高速度 大容量 低价格兼得
时间局部性:如果一个数据被访问了,那么它在短时间内还会被再次访问;
空间局部性:如果一个数据被访问了,那么和它相邻的数据也很快会被访问;
这样根据访问频率来决定将数据存储到速度快的存储器还是慢的就可以了。
LRU(Least Recently Used)缓存算法
缓存命中率(Hit Rate):访问的数据中 可以在我们设置的内存缓存中找到的占有多大的比例;
未完待续...
开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 7 天,点击查看活动详情