计算机组成原理复习

前言

这篇博客主要是听中国慕课上面的哈工大老师刘宏伟讲授的《计算机组成原理》而做的笔记。

首先是我看完这套教程之后,解答了的问题:

  • 冯诺依曼有5个组成单位,它们是?
  • 磁盘是属于哪个组成单位?
  • mdr和mar分别是什么寄存器,它们在哪里?
  • 一次中断究竟是怎么发生的?
  • 磁盘中扇区在最外侧的磁道和最内侧的磁道上,数量有区别吗?

缺点:没有GPU的介绍,也没有现在的SSD介绍,有点遗憾。

第一章 计算机系统概论

计算机系统简介

主要浓缩在这张图了:

image-20201108183144726

其次计算机也由两部分组成:硬件和软件。而《计算机组成原理》则是主要讲述硬件的。

主要的脉络就是先讲述下系统总线、然后讲述下存储器和IO,最后深入展开讲一讲CPU。

计算机的基本组成

这部分最重要的就是冯诺依曼体系结构的介绍了。冯诺依曼体系结构的特点:

  • 计算机由五部分组成,分别是运算器、控制器、存储器、输入设备和输出设备。
  • 指令和数据以同等的地位存储在存储器中,可以按照地址寻址。
  • 指令和数据都是按照二进制来表示的。
  • 指令由操作码和操作数组成。
  • 存储程序。
  • 运算器为中心。

上面一共六点,基本只需要记得第一条中的五部分即可。

image-20201108183959382

这里需要注意,存储器分成主存和辅存,主存就是内存,辅存可以理解为是硬盘。也就是硬盘+输入设备+输出设备构成了IO设备。

如何访问主存?

主存可以看成主要有三部分组成——MAR、MDR和存储体。存储体就是内存最主要的,用来存放数据的部分。而MAR是地址寄存器,也就是在里面存放了内存的地址。而MDR则是存放了对应的数据。

image-20201108190918161

运算器是如何工作的?

image-20201108191004706

其实就是对应四则运算,所以其实只需要两个寄存器+一个运算器就够了,但是由于乘法会造成溢出(老师讲课的时候没说加法溢出,很奇怪),以及除法需要余数和商,所以又有了MQ这个寄存器。

控制器是如何工作的?

image-20201108191158033

PC指向内存中(应该是先看看cache)对应的数据,IR则是对应的指令。

协同工作

下面以从内存中(忽略cache)获取一个数为例子,分步描述各个过程:

image-20201108191438406
  1. 首先PC中默认存放了需要取数字的地址,所以PC会把自己存放的地址送给MAR。
  2. MAR会根据自己的地址,到存储体里面去查找内容。
  3. 找到对应的内容后(该内容其实是一条指令),把内容放到MDR里面。
  4. 把MDR里面的指令放到IR里面。到此取指令就完成了。
  5. CU直接分析IR里面的指令。这一步就完成了译码。
  6. 最后开始执行指令。由于是取数指令,所以目前IR里面有相应的内存的地址,那么就是把这个地址给MAR
  7. 接着存储器会找到对应的内容。
  8. 把对应的内容给MDR。
  9. DMR把找到的数据给ACC寄存器,现在数字就到了寄存器中,取数指令彻底完成。

计算机硬件的主要技术指标

无非就是两个——速度和容量

第二章 计算机的发展及应用

没啥好说的。跳过。

第三章 系统总线

总线的基本概念

为什么要有总线?根据冯诺依曼体系,计算机至少会有五个部分,如果这五个部分两两互联,那也至少有10条线。而在实际的计算机里面,我们不可能让所有的部分都两两互联,这不现实,也不方便扩展。所以就有了总线。

总线在英文中是bus,其实可以把主板想象成一座城市,然后总线就是里面的公交车,所有的数据,也就是乘客,必须坐着这个公交车从一处到达另外一处。

总线上数据的传输可以分成串行和并行,串行就是一个比特一个比特的发送,而并行则是一次同时发送多个。理论上并行的速度应该是串行的N倍,但实际上,由于需要同步等问题,其实还是串行来的快。

那么总线是如何连接各个部件的呢?一种最简单的方法就是把所有的设备都连接上总线,但是这样效率实在是太低了,于是就有了以CPU为中心的总线结构:

image-20201108194402056

但是这里内存无法和IO设备直接通信,导致了CPU需要和IO设备进行交互,而IO设备对于CPU来说实在是太慢了,所以也不太适合,目前常见的应该是以内存为中心的下面这种:

image-20201108194508276

这样CPU可以直接和内存进行数据传输,而不需要和IO设备进行争抢。同时CPU、内存和IO设备两两都可以直接通信。

总线的分类

总线其实有三种,一种是在芯片之间的片内总线(不在讨论范围之内),另外一种是系统总线,是用来在系统各个部件之间进行数据传输的,还有一种是通信总线,用来在计算机系统之间进行数据传输的。我们主要关注系统总线。

系统总线:数据总线(数据双向传输)、地址总线(地址只可能从一方到另外一方)和控制总线。

总线性能及性能指标

总线本质上是印刷在电路板上面的一条条黄色的线,然后这些线穿过了一个一个插槽(包括CPU插槽、IO插槽等),这样你可以把对应的设备插到插槽上面,就能让设备接入到总线上。

总线的性能指标可以有以下分类:

  • 总线宽度:就是有多少根数据线
  • 标准传输率:每秒能够传输的最大的字节数
  • 时钟同步:是同步的还是异步的
  • 总线复用:地址线和数据是可以复用的
  • 信号线数:地址线、数据线和控制线的总和
  • 总线控制方式:突发、自动、仲裁、逻辑、计数
  • 其他能力:负载能力

总线标准:主要有我们熟知的PCI和USB(Universal Serial Bus)这两个。

总线结构

这部分还是比较复杂的,但是只要记住:CPU和内存之间是有系统总线直接相连,然后其他设备分成快速和慢速两级,分别进行连接。

总线控制

这一部分是本章的核心。

因为总线需要连接很多设备,而且只支持一对设备通过总线进行通信,所以必须要有序进行通信。我们把对总线有控制权的设备称为主设备(或者叫主模块),而对响应从主设备发来的总线命令的叫做从设备(也叫从模块)。

那么是谁有资格来发送数据呢?有一个单独的部件(总线控制部件)来统筹管理,那么就称为集中式;还有分布式,这个不在讨论范围之内。集中式又有三种,分别是链式查询、计数器定时查询、独立请求查询。

链式查询

image-20201108201432361

如果有一个设备希望自己能够占用总线发送数据,那么它可以看看BS,如果总线不忙,那么就可以通过BR发送请求到总线控制部件,但是总线控制部件它不知道是谁发送了信号给它,于是它通过BG发送同意的信号,这个信号会根据物理的顺序一个部件一个部件往下传,直到真正发送请求的那个设备收到了,那么它就会发送一个总线忙,表示已经占用了,接下来就可以进行操作了。

计数器定时查询

image-20201108201700612

计数器其实跟上面的链式很像,只不过它是轮询机制,而不是传递机制。

独立请求

上面两种的问题就是,总线控制部件完全不知道是哪个设备希望占用总线,这样它不得不一个一个去询问,所以独立请求就是在此基础上进行改进:

image-20201108201830339

可以从图中看到,虽然此时可以明确知道是哪个设备发送了请求,但是额外的开销就是每个设备都需要多两条线。

总线通信

现在已经可以通过机制来占有总线了,接下来就是主从模块之间如何进行通信的问题了。主要有四个过程:

  • 申请分配阶段:主模块申请,总线仲裁决定
  • 寻址阶段:主模块向从模块给出地址命令
  • 传数阶段:主模块和从模块交换数据
  • 结束阶段:主模块撤消有关信息

有四种方式来进行数据的传输。

同步通信

需要有一个定长的时间周期,规定了每个时间周期内,主从模块各进行什么操作。如果对应的时间周期主从模块没有进行对应的操作,就会失败。

异步通信

基本就是发了请求之后,等待从设备回复,而不需要严格的时间周期。

半同步通信

基本就是同步和异步的结合。因为假设CPU是主模块,硬盘是从模块,那么它们俩数据差距太大了。主模块发完地址和命令之后,如果读取到对应的wait,那么就会等待一个时间周期,接着重复,直到wait消失,再从从模块读取数据。

分离式通信

上面三种通信的方式缺点都非常明显,就是从模块在准备数据的时候,其实此时总线是空闲的,完全没必要占用。所以分离式就让所有的人都能占用总线,这样当从模块在准备数据的时候就可以不用占用总线,当数据完成之后,从模块再去占用,充分提高了总线的利用率和效率。

第四章 存储器

概述

更正一个多年的误区:存储器包含了内存和硬盘,还包含了一些其他的部件(寄存器、cache等)。

存储器按照存储介质可以分成:半导体存储器(内存)、磁表面存储器(磁带)、磁芯存储器和光盘存储器(光盘)。

按照存取的方式分类可以分成:存取时间和物理地址无关(内存)以及存取时间和物理地址相关(磁带、磁盘)。

按照在计算机内部的作用可以分成:主存储器(RAM和ROM)、Flash Memory(大部分的U盘)、高速缓冲处理器(cache)还有辅助存储器(硬盘)。

最后,存储器按照速度的快慢:寄存器、cache、主存、磁盘、光盘和磁带(磁带应该没人用了吧)

主存储器

概述

之前提到过最最简单的主存储器的结构:

image-20201108190918161

但是实际上,MAR和MDR是在CPU芯片上面的(最早确实是在主存里,只不过现在集成到了CPU上面),就像这个样子:

image-20201109213201739

其中可以看到,只有MAR会把自己的内容通过地址总线给主存,因为只有CPU需要从主存特定的地址内取内容,所以是一个单向的。

接着是大端序和小端序,很简单跳过。然后是对主存的速度的衡量,有两种速度,分别是读取一次的时间和写入一次的时间,以及连续两次独立的存储器操作。按照常识来说,肯定是读/写一次主存的耗费的时间比较久,但是实际上确是两次的间隔比较长。

半导体存储芯片简介

主存储器内部其实是有很多很多的小片片,然后会有对应的线来决定是哪个片被选中了(也可能多个片被选中):

image-20201109214011063

线选法:可以把内存理解成一个数组,那么如果假设地址线有10条,那么理论上可以有1024个元素,对应到物理中也就是需要1024根线,这个如果地址线一增多,就会导致真实的线的数量急剧膨胀,在那么小的空间里做不下这么多线。

重合法:重合法基本就是把内存看做是一个矩阵,还是假设地址10条,那么可以分成5条和5条,就是一个32x32的矩阵,分别由其中的五根地址线给出横坐标和纵坐标,这样就可以有效减少线的数量(从1024根减少到64根)。

RAM

RAM可以分成静态RAM和动态RAM。但是由于我没学过数电模电,所以只能勉勉强强看懂一点点,个人觉得这也不是重点,就决定跳过了。

动态RAM是利用电容来保存信息的,这就有个缺点,电容经过一段时间会衰弱,所以需要进行刷新。而刷新可以使用集中刷新(会有一部分时间完全不可用,因为要用来刷新),分散刷新(对性能消耗大)还有两者结合的方法。

所以,从根本上来说,由于动态RAM的电路结构简单,所以动态RAM集成度高,引脚少,价格便宜,但是速度慢——反正就是静态RAM除了快,都是缺点。

内存条用的是动态RAM,而cache用的是静态RAM。

ROM

  • 最早的只读存储器,是真正的只读,真的不能改了:MROM。
  • 第一次改进就是允许用户写一次:PROM。这个可以利用高电流把熔丝烧断来达成。
  • 然后进一步改进,现在只读也可以多次进行信息擦除了:EPROM。也是一种特殊的电路,通过特殊的电压可以改变里面存储的信息。
  • 多次性编程,应该是比上面那种更加方便了:EEPROM(价格稍微高)
  • 最后来到了闪存,就是U盘。

存储器和CPU连接【重点】

首先是存储器的位扩展:1K x 4位扩展成 1K x 8位,可以让两个存储器都接在地址线上,然后再把读出的数据通过数据线传回去,比如四根数据线连到了存储器A,另外四根连到了存储器B,这样8根数据线就都有了数据,从原来的4位扩展到了8位。

image-20201109223741559

然后是字扩展。1K x 8位扩展成2K x 8位。很简单,相当于原来是10条地址线变成了11条,那把多余的一条作为选择线来用就好了。

image-20201109223814360

最后是同时扩展。所以简单说就是,CPU会通过地址线把地址传送给存储器,然后存储器通过数据线把数据传回给CPU。

存储器的校验

简单来说就是汉明码的使用。没什么好说的,跳过。

提高速度

CPU目前的速度已经远超内存了,所以内存速度成为了瓶颈,我们需要想方设法提高内存的速度。目前使用的是低位交叉,就是假设有0x12345678这个数据,那么可以把12放到第一个内存芯片中,34放到第二个芯片……这样可以利用总线的分离式通信,大大加速内存的使用。

其他的手段还包括开发物理性能上更强劲的内存、使用类似流水线的技术等。

高速缓冲存储器

经过上面的概述,知道了SRAM的速度快,但是成本高,所以其实是比较适合用来做缓冲的。内存和cache之间数据的交换用的是来作为单位的。总体的流程如下:

image-20201111141204728

其中还需要考虑写的问题。因为cache和主存存在一致性问题,所以有两种方法,分别是:

  • 写直达法(Write–through):就是既往cache里写,又往主存中写。
  • 写回法(Write – back):只往cache中写入数据,只有当cache中的数据被替换出去的时候,才写回到主存中。

现代的cache还进行了改进,比如其实有多级cache等。现在大部分的CPU中,L1级的cache是分成指令cache和数据cache的,且每个CPU是独享L1的,而L2级别的cache则是在cluster之间共享;L3则是所有的都共享,同时L3直接与内存通过bus相连。

为了方便记忆,直接把L1L2记成是CPU独享的就行了,L3是共享的。CPU从cache中拿的最小的数据是Byte,而cache从主存中最少拿64字节,而主存从硬盘里一般都会拿4096字节。

地址映射算法

由于cache的容量小,所以肯定需要一个映射的算法把主存中的数据放到cache中来。大体上有两种,一种是通过直接映射,也就是某个块的内存只能映射到cache中的某个位置,这样的好处是判断cache中存不存在目标数据会非常容易,但是缺点是空间利用率不够;另外一种是全相连映射,就是可以把内存中的任意的数据放到cache中任意的位置,这么一来虽然空间利用率高了,但是每次需要判断cache中数据存在与否,需要全部扫描一遍,时间上慢了。其实还有第三种,叫组相连映射,就是上面两种方法的折中。

替换算法

课程里介绍了两种,一种是FIFO,另外一种是LRU。

三级缓存

这部分我自己加的。明明L1L2L3都是静态RAM,但是它们的速度还是有不小的差异呢?参考数据

L1:3个时间周期

L2:11个时间周期

L3:25个时间周期

大体来说有三个原因:

  • 容量越大的SRAM访问时间就越长,这个牵扯到物理了,没办法解释
  • 它们距离CPU的距离有远近
  • 制程不同。也不理解…

辅助存储器

辅助存储器在我的理解中有三种,分别是硬盘、软盘和光盘。不过软盘我是连见都没见过,光盘也几乎不怎么用(光驱都没了)

机械式硬盘有一个很有意思的点,之前一直被我搞错,那就是每个磁道上的扇区数目,是一致的吗?答案是如果是比较旧的硬盘,那是一致的,也就是越到外面信息的密度越低;如果是现在新式的磁盘,那是不一致的,外面的磁道的扇区要多。而且计算机都是把数据放到外层,然后再慢慢往里面放,显然放到内侧的数据的读写速度会比外层的慢。

磁盘和主机进行通信的模型如下所示:

image-20201113103018118

可以看到最为重要的就是磁盘控制器和磁盘驱动器这两个东西。

驱动器

驱动器本身其实也没什么好说的,就是让盘片转动起来,然后利用机械臂移动,通过磁头读取/写入数据。

控制器

控制器是通过总线和主机进行通信的,它接受主机这一侧的命令,并转换成驱动器的命令。

软盘和光盘真就没啥了解的意义了,因为基本已经绝迹了。

第五章 输入输出系统

概述

最早期的计算机,CPU和IO设备之间是通过串行的方式进行的,然后有了中断和DMA的方式,再然后有通道的结构和对应的IO处理机来处理。

IO设备和主机进行联系,可以通过将主存的一部分划分给IO设备,这样的话原来的指令都不需要进行修改;也可以启用一套专门的IO指令来操控IO设备。

外部设备

内容极其多,但是不是很重要,所以简单过一遍就可以了。

image-20201113111134172

这图在之后会有相应的介绍。外部设备主要包括鼠标、键盘、显示器、打印机、磁盘(磁盘是外部设备,但是它不是输入输出设备,类似java的语言IO包看上去是在操作硬盘,所以可能会误以为磁盘是IO设备,其实不是的!)等,更加细致的划分有:

  • 输入设备:主要有鼠标、键盘和触摸屏
  • 输出设备:显示器、打印机
  • 其他:A/D D/A转换器,把数字信号转换成模拟信号进行传输。

I/O接口

为什么要有接口,就是为了能够让CPU能够用统一的方式控制设备,大体结构如下所示:

image-20201114095720791

可以看到IO接口里面是有一个data buffer register的,可以用来缓冲数据,同时可以看到由于IO接口里面有命令寄存器和译码器,所以它能够直接翻译从CPU处得到的命令。

程序查询方式

最开始的查询方式,思路很简单,CPU发送命令给IO设备,如果IO设备没有准备好,CPU就进行等待;如果IO设备准备完成,那么CPU就把数据从IO设备通过总线的方式放到主存里面。

程序中断方式

使用中断的方法比上面的方法提高了并发性,可以让CPU不需要等待慢速的IO设备,而是直接让设备在完成的时候进行通知(触发中断,当然CPU可以设置关闭中断),然后调用中断处理程序去处理。具体到电路上,IO设备需要有中断请求触发器和中断屏蔽触发器;其次可能会有多个设备同时发中断,所以这些IO设备需要通过一个排队器来组织;最后还需要一个设备能够把对应的设备的中断号翻译成中断向量的地址,它能够接受排队器的输出,并将其转化成对应的中断向量的地址,那里存放了对应的中断服务程序或者是一条JMP指令用来跳转到对应的中断服务程序。

由于IO设备可能在任何时候准备好数据,所以中断可能在任何时候发生,而CPU仅仅会在每条指令的结束前,主动去查询有没有中断发生。具体来说就是CPU会发送一个中断查询,如果有设备已经把数据准备好了,那么会发送中断请求,此时CPU会发送中断响应给设备,此时设备会使用一个设备编码器,把对应的向量地址发回给CPU,这样CPU就能到对应的地址处去执行中断服务程序了。

那么在执行中断程序的时候,怎么避免中断程序不被中断呢——很简单,关闭中断即可。但是这里有一个我之前一直理解错的地方,就是当CPU检测到中断发生的时候,会通过硬件(也就是默认行为,无法修改)来把中断关闭掉,同时让PC寄存器指向中断向量,这一步是无法避免的,它也不是指令,而是硬件实现的。所以如果希望中断服务程序不被中断,那么只需要服务程序完成的时候打开中断即可;而如果希望能够被更高级别的响应,那么马上开启中断即可。

DMA方式

上面的两种方式,就算是内存和IO设备进行信息交换,CPU也不得不介入进来,这样一来就会浪费CPU的时间,而DMA方式可以几乎不使用CPU进行(因为实际中肯定不能完完全全摆脱CPU的嘛)数据的交换。

DMA与内存进行数据交换,可以通过三种方式:一种直接让CPU停止访问主存、一种是周期挪用(简单来说就是CPU不需要访问主存的时候,DMA就把那个几个存取周期给过来)、还有一种是他们之间交替访问。

DMA的数据传送可以分成三个阶段,首先是数据的预处理,这个阶段需要CPU的帮助,简单来说就是设置要传送目的地址、内存的地址、要传送多少数据量等。然后就交给DMA进行数据的处理了。处理完成之后还需要使用中断处理程序来进行数据校验等工作。

DMA接口和CPU之间的连接,可以通过之前的总线的链式连接,也可以通过独立连接。

DMA比之前的处理速度快,最最重要的原因就是它是硬件层面上的,而其他两种是软件层面上的。

最后,DMA接口上可以在物理上连接多个设备,在逻辑上可以连接单个设备,也可以连接多个设备,但是真正传输的时候,只能有一个设备

第六章 计算机的运算方法

这一章的内容其实是视频教程的第二部分,详解了计算机内部的数字是怎么表示的、计算是如何进行的,这部分对我而言目前没什么用,暂时跳过。

本章主要包括了什么是无符号数和有符号数、数的定点表示和浮点表示定点运算浮点四则运算和CPU中的算术逻辑单元是如何工作的。

第七章 指令系统

机器指令

指令一般是操作码+地址码的组合,而在x86下面的指令的操作码长度是不固定的,而在RISC下的指令的操作码长度则是固定的。

最为简单的变长操作码原理也很简单,假设指令的操作码可以是4位、8位、12位和16位,那么因为4位可以表示16个数字,那么只需要让1111开头的这个作为扩展的标识位,就可以进行扩展了。

地址码则可以有不少,最多可以有4个地址码,分别是第一操作数的地址、第二操作数的地址、结果的地址和下一条指令的地址。不过下一条指令的地址现在都是由PC给出的,而且结果的地址其实可以放到其中的一个操作数的地址里面,所以目前看到绝大部分的指令还是两个操作数及以下的,甚至有NOP这种不需要地址码的指令。

操作数类型和操作类型

操作数本身可以是地址(无符号整数)、数字(包括十进制、浮点数和定点数)、字符和逻辑数。

为了能够既节省空间,并且能够在一个周期内完成数据的存取,目前都是采用的地址对齐的技术。

操作类型一共有五种:

  • 数据传送,如mov
  • 算术操作和逻辑操作:如add、and这种的
  • 移位操作
  • 跳转操作
    • 无条件跳转
    • 条件操作
    • 调用和返回
    • trap操作,如最熟悉的int 80
  • 输入和输出

寻址方式

寻址方式非常好理解。按照大类来说,分成指令寻址(目前全部从PC中获取就好了)和数据寻址,数据寻址种类繁多,因为可以从内存中取、从寄存器中取,也可以是直接寻址和间接寻址,还有相对PC寻址和堆栈寻址,总体来说还是很简单的。

指令格式举例

8086使用的是变长的指令编码方式,即一条指令的长度在1~6字节之间,而且只支持最高2个地址。

RISC 技术

通俗来说就是目前的x86指令太多了,但是有80%的指令其实平时用的很少,这些指令会拖累系统(虽然我也没了解为什么多余的指令会让速度变慢,可能跟电路设计有关系,而且RISC的寻址方式少、指令集简单,感觉也有一定的影响),所以有了RISC,这里面只有最常见的那些指令,然后用这些最常见的指令自己拼凑出复杂的功能。

第八章 CPU的结构和功能

这一章和上面一章一起基本就是从大体上了解了CPU的总体功能。

CPU 的结构

从最开始的冯诺依曼五大体系结构已经看到了,现代CPU其实占据了两大部分——运算器和控制器。运算器就是ALU,就是用来计算的,而控制器就是CU,用来完成取指令(严格来说其实还有IR和PC)、分析指令、执行指令,还要能够管理总线、处理中断等,详细的CU会在最后一部分介绍。下图总结得就很不错:

image-20201115172940596

ALU可以参考第六章(当然我没看)、中断系统本章之后会分析、CU则是第九章和第十章的主要内容,所以这里简单介绍下寄存器,寄存器大体也可以分成用户可见的寄存器,通用寄存器、数据寄存器、地址寄存器和条件码寄存器,还有用户不可见的寄存器(当然也没办法改),像之前的MAR和MDR,就是这种不可见的类型。

指令周期

image-20201115182922725

这就是一个完整的指令周期,可以发现,每条指令的最后,都会需要去判断到底有没有中断的发生。

指令流水

指令流水也很容易理解,原来的指令都是串行的,就算只有取指令和执行指令这两个步骤,那在执行指令的时候取指令的部件就空闲,所以为什么不让它忙起来呢?这就是流水线的思想。

当然如果执行指令的时间大于了取指令的时间,那么可以引入指令缓冲区,来暂时存放将要执行的指令。流水看上去美好,实际中也会遇到问题:比如某一条指令是条件跳转,那么只有当它上一条指令完成之后才能跳转,这个时候流水就没用了,实际中依靠猜测的方法来提升效率,这里不展开。

中断系统

其实中断系统在之前IO设备里面提到过了,而且是同一个东西,所以这里就补充一些细节。

首先需要知道是哪个设备触发了中断,这个可以让每个设备对应一位,然后组成一个中断请求标记寄存器,CPU根据这个寄存器的值来判断是哪个设备发生了中断。而这个寄存器的值,是由INTR来组成的,INTR的实现可以分散在各个接口中,也可以集成在CPU里面。与此同理的还有排队器,既可以在各个接口中,也可以在CPU中。当然判断中断的优先级这个也可以通过软件来实现。

其次是中断服务程序的地址的查找,之前是可以通过排队器的输出,输入到一个向量地址形成部件(硬件)中,并且输出对应的地址。当然这个也可以通过软件来实现。

这里再来详述一遍系统迪调用情况,因为我们一般是通过系统调用来间接使用int 80指令,所以从系统调用开始:系统会通过查询eax寄存器的内容,知道是哪个系统调用。然后通过修改CS:IP,这样就成功进入到了内核态。会把用户态的所有寄存器保存到内核栈里面。就成功把现场保护好了,然后就可以执行对应的程序了。

补充系统调用的过程

在32位系统中用的int 0x80进行系统调用的流程是:

  • 首先CPU一条一条执行指令,执行到系统调用函数时,系统调用函数通过int 0x80指令进入系统调用入口程序,并且把系统调用号放入%eax中,如果需要传递参数,则把参数放入至多六个寄存器里面(所以系统调用不可能超过六个参数)。
  • 进入系统调用入口程序(System_call)后,它首先把相关的寄存器压入内核堆栈(每一个进程都有两个堆栈,一个是用户态下的,一个是内核态下的,且内核态下的每次使用都会清空),这个过程称为保护现场。
  • 保护现场的工作完成后,开始检查系统调用号(在eax寄存器内的数值)是不是一个有效值,如果不是则退出。
  • 接下来根据系统调用号开始调用系统调用处理程序(这是一个正式执行系统调用功能的函数),从系统调用处理程序返回后,就会去检查当前进程是否处于就绪态、进程时间片是否用完,如果不在就绪态或者时间片已用完,那么就会去调用进程调度程序schedule(),转去执行其他进程。
  • 如果不执行进程调度程序,那么接下来就会开始执行ret_from_sys_call,顾名思义,这这个程序主要执行一些系统调用的后处理工作。比如它会去检查当前进程是否有需要处理的信号,如果有则去调用do_signal(),然后进行一些恢复现场的工作,返回到原先的进程指令流中。至此整个系统调用的过程就结束了。

第九章 控制单元的功能

这章和下一章主要介绍了CPU里面的CU的功能和设计,个人觉得不是很重要。

视频里详细介绍了CU是如何控制整个取值、执行指令的,这里就不赘述了。

这里学到的新知识是,为什么CPU的主频在大多数情况下越高越好,明明是一个频率单位,为什么能够决定性能呢?

因为CPU的快慢本质上就是执行指令的速度,而指令的执行需要几个阶段,就是取指令、执行指令这些,这些过程每一个叫做一个机器周期,然后机器周期还由若干个时钟节拍组成的,所以如果时钟节拍能够尽可能短,那么指令就可以尽可能快,所以基本上主频越高、CPU执行指令速度越快,就说CPU性能更强。当然这是建立在每条指令的机器周期中的节拍数相同的前提下。

第十章 控制单元的设计

这里是如何实现的地方,我直接跳过了。

总结

这套视频教程看下来,还是非常不错的,能让我对硬件有了一个全新的认识。尤其是对中断的理解,我之前一直以为是设备在完成的时候主动向设备发送中断请求,现在看来不是的。而且还顺手解决了之前对于为什么内存要叫主存的疑惑。了解了系统总线是怎么工作的、DMA是什么,它是怎么加速的、分清楚了RAM和ROM之间的区别,以及为什么RAM为什么要叫随机存储器,还深入看了一条指令它究竟是怎么执行的,清楚了流水线到底是怎么工作的。