尚硅谷《计算机组成原理》计算机408考研、考试不挂+考研拿下!计组

计算机组成原理

引言 考研大纲概述

全国硕士研究生计算机学科专业基础综合(408)考试,包含数据结构、计算机组成原理、操作系统和计算机网络4大专业基础课程,满分150分。其中,计算机组成原理45分,占总分的30%。

计算机组成原理的考察目标为:

  1. 掌握单处理器计算机系统中主要部件的工作原理、组成结构以及相互连接方式。

  2. 掌握指令集体系结构的基本知识和基本实现方法,对计算机硬件相关问题进行分析,并能够对相关部件进行设计。

  3. 理解计算机系统的整机概念,能够综合运用计算机组成的基本原理和基本方法,对高级编程语言(C语言)程序中的相关问题进行分析,具备软硬件协同分析和设计能力。

计算机组成原理,主要讲的就是计算机系统的“组成”,以及其中各部件的具体实现和连接的原理。


第一章 计算机系统概述

1.1 本章大纲要求与核心考点

1.1.1 大纲内容

(一) 计算机系统层次结构

  1. 计算机系统的基本组成

  2. 计算机硬件的基本结构

  3. 计算机软件和硬件的关系

  4. 计算机系统的工作原理

    “存储程序"工作方式,高级语言程序与机器语言程序之间的转换,程序和指令的执行过程。

(二) 计算机性能指标

吞吐量、响应时间;

CPU时钟周期、主频、CPI、CPU执行时间;

MIPS、MFLOPS 、GFLOPS、TFLOPS、PFLOPS、EFLOPS、ZFLOPS。

1.1.2 核心考点

本章是计算机组成原理的概述,一般会考察相关概念,以选择题为主,也可能结合后续章节以综合题的形式来考察性能的分析。掌握本章的概念,是整个组成原理课程的基础。 ​ 计算机性能指标在历年真题中出现的频次较高,需要重点掌握。

1.1.3 真题分布
考点考查次数
单项选择题综合应用题
计算机系统层次结构60
计算机性能指标92


1.2 计算机系统简介

1.2.1 计算机系统的概念和分类

计算机系统由“硬件”和“软件”两大部分组成。

  • 所谓“硬件”即指计算机的实体部份,它由看得见摸得着的各种电子元器件、各类光、电、机设备的实物组成,如主机、外设等等。

  • 所谓“软件”,它是看不见摸不着的,由人们事先编制成具有各类特殊功能的信息组成。通常把这些信息, 诸如各类程序寄寓于各类媒体中, 如RAM、ROM、磁带、磁盘、光盘、甚至纸带等。它们通常被作为计算机的主存或辅存的内容。

计算机的软件通常又可以分为两大类:系统软件和应用软件。

  • 系统软件又称为系统程序,主要用来管理整个计算机系统,监视服务,使系统资源得到合理调度,确保高效运行。它包括:标准程序库、语言处理程序(如将汇编语言翻译成机器语言的汇编程序;将高级语言翻译成机器语言的编译程序)、操作系统(如批处理系统、分时系统、实时系统)、服务性程序(如诊断程序、调试程序、连接程序等)、数据库管理系统、网络软件等等。

  • 应用软件又称为应用程序,它是用户根据任务需要所编制的各种程序。如科学计算程序,数据处理程序,过程控制程序,事务管理程序等等。

1.2.2 计算机的发展

(一) 硬件的发展

1943 年,第二次世界大战进入后期,因战争的需要,美国国防部主导建造了第一台计算机ENIAC(Electronic Numerical Integrator And Computer,ENIAC),它的全称是"用电子管组成的电子数字积分机和计算机”。

从此以后,计算机的发展经历了电子管、晶体管、集成电路的世代发展,体积越来越小、性能越来越强,并从军事领域迅速扩展应用到生活生产的各个行业,成为了现代信息社会不可或缺的基础设备。

硬件技术对计算机更新换代的影响

时间硬件技术速度(次/秒)
1946~1957电子管40, 000
1958~1964晶体管200, 000
1965~1971中、小规模集成电路1, 000, 000
1972~1977大规模集成电路10, 000, 000
1978~现在超大规模集成电路100, 000, 000

摩尔定律

集成电路出现之后,芯片集成度不断提高,从在一个芯片上集成成百上千个晶体管的中、小规模集成电路,逐渐发展到能集成成千上万个晶体管的大规模集成电路(LSI)和能容纳百万个以上晶体管的超大规模集成电路(VLSI)。

微芯片集成晶体管的数目增长非常迅速,其规律被总结为“微芯片上集成的晶体管数目每3年翻两番”,这就是所谓的“摩尔定律”。

摩尔定律的另一个常见表述是:每平方英寸电路板上的晶体管数量,每18个月翻一倍。

(二) 软件的发展

(1)编程语言的发展

软件的编写离不开编程语言。编程语言的发展经历了机器语言、汇编语言和高级语言三个阶段。我们现在使用的编程语言一般都是高级语言。

(2)操作系统的发展

随着计算机的发展及应用范围的扩大,逐渐形成了软件系统。而其中最重要的一类软件,是为了提高计算机性能和资源利用率而设计的,这就是“操作系统”。

操作系统的发展经历了批处理系统、分时系统、实时系统、PC操作系统、网络操作系统、分布式操作系统等多个阶段。目前我们的个人电脑使用的操作系统主要有:Windows、Mac OS、Linux。


1.3 计算机系统的层次结构

1.3.1 计算机系统的基本组成

(一) 计算机硬件

  1. 冯·诺依曼计算机

冯·诺依曼在研究EDVAC计算机时提出了 “存储程序”的概念,“存储程序”的思想奠定了现代计算机的基本结构,以此概念为基础的各类计算机通称为冯•诺依曼计算机,其特点如下:

  • 采用“存储程序”的工作方式。

  • 计算机硬件系统由运算器、存储器、控制器、输入设备和输出设备5大部件组成。

  • 指令和数据以同等地位存储在存储器中,形式上没有区别,但计算机应能区分它们。

  • 指令和数据均用二进制代码表示。

  • 指令由操作码和地址码组成,操作码指出操作的类型,地址码指出操作数的地址。

 

  1. 计算机的功能部件

  • 存储器:分为主存和辅存,中央处理器可以直接访问的程序和数据存放在主存中。

  • 运算器:完成对信息或数据的处理和运算,如算术和逻辑运算。

  • 控制器:完成对计算机各部件协同运行的指挥控制,即保证指令按预定的次序执行,保障每一条指令按规定的执行步骤正确执行,还要处理各项紧急事件。

  • 输入设备:用来输入原始数据和程序,如键盘、鼠标。

  • 输岀设备:用来输出计算机的处理结果,如显示器和打印机。

一般将运算器和控制器集成到同一个芯片上,称为中央处理器(CPU)。CPU和主存储器共同构成主机,而除主机外的其他硬件装置(外存、I/O设备等)统称为外部设备,简称外设。

(二) 计算机软件

  1. 软件的分类

软件按其功能分类,可分为系统软件和应用软件。

  1. 三个级别的计算机语言

 

(1) 机器语言

机器语言由二进制编码组成,它是计算机唯一可以直接识别和执行的语言。

(2) 汇编语言

汇编语言是用英文单词或其缩写代替二进制的指令代码,更容易为人们记忆和理解。汇编语言程序必须经过汇编操作,转换为机器语言后,才能在计算机硬件上执行。

(3) 高级语言

高级语言(如C、C++、Java等)程序需要先经过编译程序编译成汇编语言程序,再经过汇编操作 成为机器语言程序。高级语言程序也可直接通过解释的方式“翻译”成机器语言程序。

由于计算机无法直接理解和执行高级语言程序,因此需要将高级语言程序转换为机器语言程序,通常把进行这种转换的软件系统称为翻译程序。翻译程序有以下三类:

  • 汇编程序(汇编器):将汇编语言程序翻译成机器语言程序。

  • 解释程序(解释器):将高级语言源程序中的语句按执行顺序逐条翻译成机器指令并立即执行。

  • 编译程序(编译器):将高级语言源程序翻译成汇编语言程序或机器语言程序。

1.3.2 计算机硬件的基本结构

典型的冯·诺依曼计算机是以运算器为中心的,如下图所示。其中,输入、输出设备与存储器之间的数据传送都需通过运算器。图中实线为数据线,虚线为控制线和反馈线。

 

现代的计算机已转化为以存储器为中心,如下图所示。图中实线为控制线,虚线为反馈线,双线为数据线。

 

图中各部件的功能是:

  • 运算器用来完成算术运算和逻辑运算,并将运算的中间结果暂存在运算器内;

  • 存储器用来存放数据和程序;

  • 控制器用来控制、指挥程序和数据的输入、运行以及处理运算结果;

  • 输入设备用来将人们熟悉的信息形式转换为机器能识别的信息形式,常见的有键盘、鼠标等。

  • 输出设备可将机器运算结果转换为人们熟悉的信息形式,如打印机输出、显示器输出等。

由于运算器和控制器在逻辑关系和电路结构上联系十分紧密,尤其在大规模集成电路制作工艺出现后,这两大部件往往制作在同一芯片上,因此,通常将它们合起来统称为中央处理器(Central Processing Unit) , 简称CPU。把输入设备与输出设备简称为I/O设备(Input/Output equipment) 。

这样, 现代计算机可认为由三大部分组成:CPU、IO设备及主存储器(Main Memory,MM) 。CPU与主存储器合起来又可称为主机, I/O设备叫作外部设备。

 

  • 主存储器是存储器子系统中的一类,用来存放程序和数据, 它可以直接与CPU交换信息。另一类叫辅助存储器, 简称辅存, 又叫外存。

  • ALU(Arithmetic Logic Unit) 算术逻辑运算单元,用来完成算术逻辑运算。

  • CU(Control Unit) 控制单元, 用来解释存储器中的指令, 并发出各种操作命令来执行指令。

ALU和CU是CPU的核心部件。I/O设备也受CU控制,用来完成相应的输入、输出操作。可见,计算机有条不紊地自动工作,都是在控制器统一指挥下完成的。

1.3.3 计算机软件和硬件的关系

硬件实现的往往是最基本的算术和逻辑运算功能,而其他功能大多通过软件的扩充得以实现。对某 一功能来说,既可以由硬件实现,也可以由软件实现。从用户的角度来看,它们在功能上是等价的。这 一等价性被称为软、硬件逻辑功能的等价性。

由于“软件”的发展,它不仅可以充分发挥计算机的“硬件”功能,提高计算机的工作效率,而且已经发展到能局部模拟人类的思维活动,因此在整个计算机系统内,“软件”的地位和作用已经成为评价计算机系统性能好坏的重要标志。当然,“软件”性能的发挥,也必须依托“硬件”的支撑。因此,概括而言,计算机性能的好坏,取决于 “软”、 “硬”件功能的总和。

1.3.4 计算机系统的多级层次结构

从用户的角度看,人们在操作系统提供的运行环境下,首先用高级语言编写程序(称为源程序),然后将源程序翻译成汇编语言程序,再将其翻译成机器能识别的机器语言程序(称为目标程序),最后用微程序解释每条机器指令。这样,就构成一个常见的计算机系统的5级层次结构,如下图所示:

 

从计算机系统的5级层次结构来看,可以将硬件研究的对象归结为微程序机器 M0 与传统机器 M1,也就是实际机器。而软件研究的对象主要是操作系统及其以上的各级虚拟机器。通常将除硬件系统外的其余层级称为虚拟机器,包括操作系统机器 M2、汇编语言机器 M3 和高级语言机器 M4。简单来说,虚拟机器就是由软件实现的机器。

相邻层级之间的关系,下层是上层的基础,上层是下层的扩展。随着超大规模集成电路技术的不断发展,部分软件功能可以由硬件来实现,所以软/硬件交界面的划分也不是绝对的。

1.3.5 计算机系统的工作原理
1. 从源程序得到可执行程序

用高级语言编写好一段程序之后,需要经过一系列“翻译“过程,才能得到计算机能够执行的机器代码。比如,我们用C语言写了一个简单的 hello world 程序,源程序文件命名为 hello.c,用GCC编译器可以将它翻译成一个可执行目标程序 hello。具体的过程可以分为4个阶段,如下图所示:

 

(1)预处理阶段:预处理器(cpp)对源程序中以 ”#“ 开头的命令进行处理,输出结果是一个以 ”.i“ 为扩展名的文件 hello.i。例如 ”#include“ 就会将后面的头文件内容插入程序文件中。

(2)编译阶段:编译器(ccl)对预处理后的源程序进行编译,生成一个汇编语言源程序 hello.s。汇编语言源程序中的每条语句,都用文本格式描述了一条机器语言指令。

(3)汇编阶段:汇编器(as)将 hello.s 翻译成机器语言指令,把这些指令打包成一个”可重定位目标程序“ hello.o,它是一个二进制文件,用文本编辑器打开会显示乱码。

(4)链接阶段:链接器(ld)将多个可重定位目标程序和标准库函数合并成一个可执行目标程序。上面的例子中,链接器将 hello.o 和标准库函数 printf 所在的可重定位目标模块 printf.o 合并,生成可执行程序 hello。最终生成的可执行程序被保存在磁盘上。

2. 存储程序的基本思想

“存储程序”的基本思想,就是将程序和数据一样,存放在主存中;运行时通过地址访问到程序的内容,解析出对应的指令进行执行。

 

  • 程序执行前,先将第一条指令的地址存放在程序计数器(PC)中;

  • 将PC的内容作为地址访问主存,取出指令;

  • 在每条指令执行过程中,都需要计算下一条将执行指令的地址,并送至PC。如果当前指令是顺序执行的,则下一条指令地址是PC的内容加上当前指令的长度;如果是跳转指令,则下一条指令的地址是指定的目标地址;

  • 当前指令执行完毕后,再根据PC的值作为地址访问主存,取出的是下一条将要执行的指令。

这样,计算机就可以周而复始地自动执行程序中的每一条指令了。

3. 计算机硬件组成的细化说明

为了更清楚地了解计算机的工作过程,我们需要将计算机的组成部件进一步细化,如下图所示。

 

除了之前已经列出的核心部件 ALU、CU 以及主存的存储器M,CPU和主存储器中还必须配置一些寄存器(Register),用来存放特定的信息。下面我们分别进行简单介绍。

(1)主存储器

主存储器(简称主存或内存)包括了存储体M、各种逻辑部件以及控制电路等。存储体由许多存储单元组成,每个存储单元又包含若干个存储元件;每个存储元件能存放一位二进制代码(0或者1)。这样,每个存储单元可以存储一串二进制代码,这就被称为一个”存储字“;存储字的二进制位数称为”存储字长“。

主存中的每个存储单元有一个唯一的编号,叫做存储单元的”地址“(Address)。主存的工作方式就是按照存储单元的地址,来实现对存储字各位的存(写入)取(读出)。这种存取方式叫做”按地址访问存储器“。

为了实现按地址访问的方式,主存中还必须配置两个寄存器:MAR 和 MDR。

  • MAR(Memory Address Register,存储器地址寄存器):用来存放想要访问的存储单元的地址,它的位数决定了能访问的存储单元的最大个数。

  • MDR(Memory Data Register,存储器数据寄存器):用来存放从存储体单元中取出,或者准备向存储体单元存入的数据,它的位数和存储字相等。

当然,如果想要完整地实现一个存取数据的操作,还需要 CPU 给主存加上各种控制信号,比如读命令、写命令以及地址译码驱动信号等。随着硬件技术的发展,主存现在都是统一制作的大规模集成电路芯片,所以一般都将MAR 和 MDR 集成在 CPU 芯片中。

(2)运算器

运算器包括了一个算术逻辑单元(ALU)和最少三个寄存器。

  • ACC: Accumulator,累加器;

  • MQ: Multiplier-Quotient Register,乘商寄存器;

  • X: 操作数寄存器

这三个寄存器在完成不同的算术运算时,所存放的操作数也各不相同。具体的情况如下表所示:

加法减法乘法除法
ACC被加数及和被减数及差乘积高位被除数及余数
MQ————乘数及乘积低位
X加数减数被乘数除数

不同机器的运算器结构也有所不同,有的机器用 MDR 取代 X 寄存器。

(3)控制器

控制器由控制单元(CU)和程序计数器(PC)、指令寄存器(IR)组成。

PC: Program Counter,程序计数器,用来存放当前将要执行指令的地址。它与主存的 MAR 之间之间有一条直接通路,且具有自动加1的功能,也就是可以自动形成下一条指令的地址。

IR: Instruction Register,指令寄存器,用来存放当前的指令。IR 的内容来自主存的 MDR,包含了操作码和地址码。IR 中的操作码 OP(IR)会送至 CU,可以记作 OP(IR) → CU,用来分析指令;而地址码 Ad(IR)作为操作数的地址送至存储器的 MAR,可以记作 Ad(IR) → MAR。

CU 是控制器的核心组件,用来分析当前指令所需完成的操作,并发出各种微操作命令序列,从而控制所有被控对象。控制器是计算机的神经中枢,由它指挥各部件自动协调地工作;完成一条指令操作,需要取指、分析和执行3个阶段。

(4)I/O

I/O 系统包括各种 I/O 设备及其相应的接口。每一种 I/O 设备都由 I/O 接口与主机联系,它接收 CU 发出的各种控制命令,并完成相应的操作。

4. 计算机硬件的工作过程

总结一下,当计算机接收到机器语言程序后,硬件的工作过程分为以下几步:

  • 把程序和数据装入主存储器;

  • 从程序的起始地址运行程序;

  • 按照程序的首地址从存储器中取出第一条指令,经过译码等步骤控制计算机各功能部件协同运行,完成这条指令的功能,并计算下一条指令的地址;

  • 用新得到的指令地址继续读出第二条指令并执行,直到程序结束为止。每条指令都是在取指、译码和执行的循环过程中完成的。

我们现在以从主存中取数据的指令为例,详细分析一下它的执行过程:

(1)取指令: PC → MAR → M → MDR → IR

根据 PC 取指令到 IR。将 PC 的内容送至 MAR,将 MAR 的内容送至地址线,同时控制器将读信号送至读/写信号线,从主存指定存储单元读出指令,并通过数据线送至 MDR,再传送至 IR。

(2)分析指令: OP(IR) → CU

指令译码并送出控制信号。控制器根据 IR 中指令的操作码,生成相应的控制信号,送到不同的执行部件。这里 IR 中是取数指令,所以读控制信号被送到总线的控制线上。

(3)执行指令:Ad(IR) → MAR → M → MDR → ACC

取数操作。将 IR 中指令的地址码送至 MAR,将 MAR 的内容送至地址线,同时控制器将读信号送至读/写信号线,从主存指定存储单元读出操作数,并通过数据线送至 MDR,再传送到 ACC 中。

(4)每取完一条指令,还必须计算下一条指令的地址,为取下一条指令做准备:(PC)+ 1 → PC

1.4 计算机性能指标

衡量一台计算机的性能是由多项技术指标综合确定的,既包含硬件的各类性能,又包括软件的各种功能,这里主要讨论硬件的技术指标。

1.4.1 主要性能指标
  1. 机器字长

机器字长,是指 CPU 一次能处理数据的位数,也就是 CPU 内部用于整数运算的数据通路的宽度。字长通常就等于 CPU 的通用寄存器宽度,也就是 CPU 内用于整数运算的运算器位数,它反映了计算机处理信息的能力。我们平常所说”一台16位或32位的机器“,这里的16、32就是指字长。

字长越长,数的表示范围也越大,精度也越高。机器的字长也会影响机器的运算速度。倘若CPU字长较短, 又要运算位数较多的数据, 那么需要经过两次或多次的运算才能完成,这样势必影响整机的运行速度。当然,机器字长对硬件的造价也有较大的影响。它将直接影响加法器(或ALU) 、数据总线以及存储字长的位数。

  • 指令字长:一条指令中包含的二进制代码的位数。

  • 存储字长:一个存储单元中存储的二进制代码的长度。

指令字长和存储字长,都必须是字节(Byte)的整数倍。指令字长一般取存储字长的整数倍:如果指令字长等于存储字长的2倍,那么取一条指令就需要2个机器周期;如果指令字长等于存储字长,那么取指周期就等于机器周期。

  1. 数据通路带宽

数据通路带宽,是指数据总线一次所能并行传送信息的位数,它关系到数据的传送能力。这里所说的数据通路带宽是指外部数据总线的宽度,它与 CPU 内部的数据总线宽度(机器字长)可能不同。

  1. 存储容量

存储器的容量,包括主存容量和辅存容量。我们一般主要关心主存容量。

主存容量是指主存中所能存储信息(二进制代码)的最大容量,通常以字节数来衡量。

存储容量 = 存储单元个数 × 存储字长

在主存储器中,MAR 的位数反映了存储单元的个数, MDR 的位数则反映了存储字的长度。例如, MAR 为16位, 表示 216 = 65536, 也就是说对应的存储体内有65536个存储单元(一般称为64K内存, 1K=1024);而如果 MDR 为32位, 那么主存的存储容量为 216 × 32 = 221 = 2M 位(1M=220)。

现代计算机中常以字节的个数来描述容量的大小,一个字节(Byte)被定义位8位二进制代码。所以上述存储容量是 2M 位(bit),也可用 218 字节(Byte,简写为 B)表示,记作 218 B 或 256KB。

同理,辅存容量也可用字节数来表示,例如,某机辅存(如硬盘)容量为 128 GB(1G = 1KM = 230 )。

  1. 运算速度

计算机的运算速度与许多因素有关,如机器的主频、CPU 的结构、执行什么样的操作、主存本身的速度(主存速度快,取指、取数就快)等等都有关。

1.5.2 专业术语解释

吞吐量:系统在单位时间内处理请求的数量,主要取决于主存的存取周期。

响应时间:从用户向计算机发送一个请求,到系统对该请求做出响应并获得所需结果的时间。通常包括 CPU 时间(计算机执行程序的时间)和等待时间(用于磁盘访问、存储器访问、I/O操作等的时间)。

主频(CPU 时钟频率):机器内部主时钟的频率,它是衡量机器速度的重要参数。对统一型号的计算机,主频越高,完成指令的一个步骤所用的时间越短,执行指令的速度越快。通常以赫兹(Hz)为单位。

CPU 时钟周期:节拍脉冲的宽度或周期,也就是主频的倒数,它是 CPU 中最小的时间单位。

CPU 时钟周期 = 1 / 主频

CPI:Clock cycle Per Instruction,执行一条指令所需的时钟周期数。对一个程序或一台机器来说,CPI 指的是该程序或该机器指令集中所有指令执行所需要的平均时钟周期数。

CPU 执行时间:运行一个程序所花费的时间。

CPU 执行时间 = (指令数 × CPI)/ 主频

对于同一个程序,CPU 的执行时间就代表了 CPU 的性能,它主要取决于三个要素:主频、CPI 和 指令数。这三者是相互制约的。不同的机器可以有不同的指令集,更改指令集可以让程序的指令数更少,但 CPI 可能就会增大;同时可能引起 CPU 结构的调整,从而造成主频的降低。

现在机器的运算速度,普遍采用单位时间内执行指令的平均条数来衡量,并用 MIPS(Million Instruction Per Second)作为计量单位, 即每秒执行百万条指令。比如,某机每秒能执行200万条指令, 则记作2 MIPS。

MIPS:Million Instructions Per Second,每秒执行百万条指令的数目。

MIPS = 主频 /(CPI × 106

FLOPS:FLoating-point Operations Per Second,每秒执行浮点运算的次数。

MFLOPS:百万次浮点运算每秒。 MFLOPS = 浮点操作次数 /(执行时间 * 106

GFLOPS:十亿次浮点运算每秒。 GFLOPS = 浮点操作次数 /(执行时间 * 109

TFLOPS:万亿次浮点运算每秒。 TFLOPS = 浮点操作次数 /(执行时间 * 1012

PFLOPS:千万亿次浮点运算每秒。 PFLOPS = 浮点操作次数 /(执行时间 * 1015

EFLOPS:百京次浮点运算每秒。 EFLOPS = 浮点操作次数 /(执行时间 * 1018

ZFLOPS:十万京次浮点运算每秒。 ZFLOPS = 浮点操作次数 /(执行时间 * 1021

需要注意,在计算机中,描述存储容量、文件大小时,K、M、G、T 等数量单位通常用2的幂次表示,比如 1 KB = 210 B;而在描述速率、频率等概念时,通常用10的幂次表示,比如 1 kb/s = 103 b/s。

1.5 章节练习

一、单项选择题
  1. 【2009真题】冯 · 诺依曼计算机中指令和数据均以二进制形式存放在存储器中,CPU 区分它们的依据是 ( )。

A. 指令操作码的译码结果 B. 指令和数据的寻址方式

C. 指令周期的不同阶段 D. 指令和数据所在的数据单元

答案: C

  1. 【2015真题】计算机硬件能够直接执行的是 ( )。

I. 机器语言程序 II. 汇编语言程序 III. 硬件描述语言程序

A. 仅 I B. 仅 I、II C. 仅 I、III D. I、II、III

答案: A

  1. 【2016真题】将高级语言源程序转换为机器级目标代码文件的程序是 ( )。

A. 汇编程序 B. 链接程序 C. 编译程序 D. 解释程序

答案: C

  1. 【2019真题】下列关于冯 · 诺依曼结构计算机基本思想的叙述中,错误的是 ( )。

A. 程序的功能都通过中央处理器执行指令实现 B. 指令和数据都用二进制表示,形式上无差别

C. 指令按地址访问,数据都在指令中直接给出 D. 程序执行前,指令和数据需预先存放在存储器中

答案: C

  1. 【2020真题】下列给出的部件中,其位数(宽度)一定与机器字长相同的是 ( )。

I. ALU II. 指令寄存器 III. 通用寄存器 IV. 浮点寄存器

A. 仅 I、II B. 仅 I、III C. 仅 II、III D. 仅 II、III、IV

答案: B

  1. 【2010真题】下列选项中,能缩短程序执行时间的是( )。

I. 提高 CPU 时钟频率 II. 优化数据通路结构 III. 对程序进行编译优化

A. 仅 I 和 II B. 仅 I 和 III C. 仅 II 和 III D. I、II、III

答案: D

  1. 【2011真题】下列选项中,描述浮点数操作速度的是( )。

A. MIPS B. CPI C. IPC D. MFLOPS

答案: D

  1. 【2012真题】假定基准程序 A 在某计算机上的运行时间为 100s,其中 90s 为 CPU 时间,其余为 I/O 时间。若 CPU 速度提高 50%,I/O 速度不变,则运行基准程序 A 所耗费的时间是 ( )。

A. 55s B. 60s C. 65s D. 70s

答案: D

  1. 【2013真题】某计算机的主频为 1.2 GHz,其指令分为4类,它们在基准程序中所占比例及CPI如下表所示。

指令类型所占比例CPI
A50%2
B20%3
C10%4
D20%5

该机的 MIPS 是 ( )。

A. 100 B. 200 C. 400 D. 600

答案: C

  1. 【2014真题】程序 P 在机器 M 上的执行时间是 20s,编译优化后,P 执行的指令数减少到原来的70%,而 CPI 增加到原来的1.2倍,则 P 在 M 上的执行时间是 ( )。

A. 8.4s B. 11.7s C. 14s D. 16.8s

答案: D

  1. 【2017真题】假定计算机 M1 和 M2 具有相同的指令集体系结构(ISA),主频分别为 1.5GHz 和 1.2 GHz。在 M1 和 M2 上运行某基准程序 P,平均 CPI 分别为 2 和 1,则程序 P 在 M1 和 M2 上运行时间的比值是 ( )。

A. 0.4 B. 0.625 C. 1.6 D. 2.5

答案: C

二、综合应用题
  1. 说明机器字长、指令字长、存储字长的区别和联系。

答案:

机器字长:计算机能直接处理的二进制数据的位数,机器字长一般等于内部寄存器的大小,它决定了计算机的运算精度。

指令字长:一个指令字中包含二进制代码的位数。

存储字长:一个存储单元存储二进制代码的长度。

它们都必须是字节的整数倍。指令字长一般取存储字长的整数倍,如果指令字长等 于存储字长的2倍,就需要2次访存来取出一条指令,因此,取指周期为机器周期的2倍;如果指令字长等于存储字长,则取指周期等于机器周期。早期的计算机存储字长一般和机器的指令字长与数据字长相等,故访问一次主存便可以取出一条指令或一个数据。随着计算机的发展,指令字长可变,数据字长也可变,但它们都必须是字节的整数倍。

  1. 用一台 40MHz 的处理器执行基准程序,它所包含的混合指令数和响应所需的时钟周期见下表。求有效的 CPI、MIPS 和程序的执行时间(程序的指令条数为 I)。

    指令类型CPI指令混合比
    算术和逻辑160%
    转移412%
    高速缓存命中的访存218%
    高速缓存失效的访存810%

答案:

CPI 是执行一条指令所需的平均时钟周期数。本程序中包含4种指令,根据它们不同的占比,CPI 就是这4种指令的数学期望:

CPI = 1 × 60% + 4 × 12% + 2 × 18% + 8 × 10% = 0.6 + 0.48 + 0.36 +0.8 = 2.24

MIPS 是每秒执行的百万条指令数。已知时钟频率为 40MHz,也就是每秒有 40M 个时钟周期,所以:

MIPS = 40 × 106 /(2.24 × 106 )≈ 17.9

程序的执行时间 T = 平均每条指令执行时间 × 指令条数,而平均每条指令执行的时间,就是 CPI × 时钟周期:

T = CPI × 时钟周期 × 指令条数 = 2.24 ×(1 / 40MHz)× I = 5.6 × 10-8 × I


第二章 数据的表示和运算

2.1 本章大纲要求与核心考点

2.1.1 大纲内容

(一)数制与编码

  1. 进位计数制及其数据之间的相互转换

  2. 定点数的表示和运算

(二)运算方法和运算电路

  1. 基本运算部件:加法器、算数逻辑部件(ALU)

  2. 加/减运算:补码加/减运算器,标志位的生成

  3. 乘/除运算:乘/除运算的基本原理,乘法电路和除法电路的基本结构

(三)整数的表示和运算

  1. 无符号整数的表示和运算

  2. 有符号整数的表示和运算

(四)浮点数的表示和运算

  1. 浮点数的表示:IEEE 754标准

  2. 浮点数的加/减运算

2.1.2 核心考点

本章内容是考研考察的一个重点和难点,往往会有综合应用题出现。

需要重点掌握的内容包括:

  • 真值、机器数,定点数的表示及原理

  • C 语言中的整型数据,有符号数与无符号数、不同字长整数之间的类型转换

  • ALU 的基本组成,标志位的产生,定点数的运算及相关电路,溢出概念与判断方法

  • IEEE 754标准浮点数的表示和特点,浮点数的加/减运算方法

  • C 语言中的浮点型数据,浮点型与整型、浮点型之间的类型转换,隐式类型转换

  • 数据按边界对齐方式的存储,数据按大端和小端方式存储

2.1.3 真题分布
考点考查次数
单项选择题综合应用题
定点数的表示与运算108
IEEE 754标准浮点数,浮点数的运算103
C语言中各种数据的转换32
数据按边界对齐方式的存储,数据按大小端方式存储40


2.2 数制与编码

计算机的应用领域极其广泛,但不论其应用在什么地方,信息在机器内部的形式都是一致的,采用的是二进制的表达,即均为0和1组成的各种编码。

2.2.1 进位计数制及其相互转换

(一)进位计数制

进位计数制简称“进制”,是人为定义的一种带进位的计数方法,可以用有限的数字符号表示所有的数。定义好的数字符号的个数,称为基数;当计数超出基数个数时,就需要向前进位。基数为n的进位计数制,就被称为“n进制”,特点是“逢n进一”。

我们日常生活中最常见的是十进制,使用0~9十个阿拉伯数字,逢十进一;而计算机系统底层的信息,使用的是二进制,也就是只有0和1两个数字,逢二进一。在计算机系统中,也经常使用八进制和十六进制来表示数据。下表是十进制数、二进制数、十六进制数对照表。

书写时,可在十六进制数后面加上“H”,如17DBH 或(17DB)16;八进制数后面加上“O”,如372O或(372)8;若在数的后面加上“B”,如10101100B,即表示此数为二进制数,或写成(10101100)2

十进制数、二进制数、八进制数、十六进制数对照表

十进制数二进制数八进制数十六进制数十进制数二进制数八进制数十六进制数
0000000016100002010
1000011117100012111
2000102218100102212
3000113319100112313
4001004420101002414
5001015521101012515
6001106622101102616
7001117723101112717
80100010824110003018
90100111925110013119
100101012A2611010321A
110101113B2711011331B
120110014C2811100341C
130110115D2911101351D
140111016E3011110361E
150111117F3111111371F

计算机系统为什么要采用二进制?

  • 使用有两个稳定状态的物理器件就可以表示二进制数的每一位,制造成本比较低。

  • 二进制的1和0正好与逻辑值“真”和“假”对应,为计算机实现逻辑运算提供了便利。

  • 二进制的编码和运算规则都很简单,通过逻辑门电路能方便地实现算术运算。


(二)不同进制数的相互转换

任意一个数 N,可以用 r 进制表示成下面的形式:

N =(dn-1dn-2 … d1d0.d-1d-2 … d-m

= dn-1rn-1 + dn-2rn-2 + … + d1r1 + d0r0 + d-1r-1 + d-2r-2 + … + d-mr-m

= ∑ diri

其中,r 为基数;d 为系数,di 代表第 i 位上的数,可以是 0 ~ (r-1) 中的任意一个数字;ri 叫做第 i 位上的权值。n、m 分别代表 N 的整数部分和小数部分的位数。

(1)二进制和八进制、十六进制间的转换

二进制数数位较多,书写不方便,在计算机系统中一般需要进行“缩写”。由于 23=8, 24=16,从而3位二进制数就对应着一个8进制数、4位二进制数对应着一个16进制数;对于一个小数而言,以小数点为界,整数部分从小数点左侧第一位起向左数,小数部分从小数点右侧第一位起向右数,不够就补0。这样二进制数和八进制数、十六进制数就可以非常方便地互相转换了。

例如,将二进制数1110011101.0010111转换为八进制数为:

左侧补0                  分界点                右侧补0

↓ ↓ ↓

001 110 011 101 . 001 011 100

1 6 3 5 . 1 3 4

所以 (1110011101.0010111)2 = (1635.134)8

同样道理,转换为十六进制数为:

0011 1001 1101 . 0010 1110

3 9 D . 2 E

所以 (1110011101.0010111)2 = (39D.2E)16

  • 二进制转换为八进制:每数三位就转换成对应的八进制数,位数不够则补0。

  • 二进制转换为十六进制:每数四位就转换成对应的十六进制数,位数不够则补0。

  • 八进制转换为二进制:每位都转换成对应的3位二进制数。

  • 十六进制转换为二进制:每位都转换成对应的4位二进制数。

(2)任意进制数转换为十进制数

任意进制数的各位数码与它的权值相乘,再把乘积相加,即得到相应的十进制数。这种转换方式称为 按权展开法

例如,将二进制数 11011.101 转换为十进制数为:

(11011.101)2 = 1 × 24 + 1 × 23 + 0 × 22 + 1 × 21 + 1 × 20 + 1 × 2-1 + 0 × 2-2 + 1 × 2-3

= 27.625

另一种方法是“按基数重复相乘/除法”,需要分整数部分和小数部分分别转换。

整数部分从高到低,将每一位乘以基数值、再加上后一位,进行“重复相乘”:

(11011)2 = (((1 × 2 + 1) × 2 + 0 ) × 2 + 1) × 2 + 1 = 27

小数部分从低到高,将每一位除以基数值、再加上前一位,进行“重复相除”:

(0.101)2 = ((1 ÷ 2 + 0) ÷ 2 + 1 ) ÷ 2 + 0 = 0.625

(3)十进制数转换为二进制数

将十进制数转换为二进制数,一般采用 基数乘除法。整数部分和小数部分分别处理,最后将整数部 分与小数部分的转换结果拼接起来。

  • 整数部分的转换规则:除2取余,最先取得的余数为数的最低位,最后取得的余数为数的最高位,商为0时结束。 (即除2取余,先余为低,后余为高)

  • 小数部分的转换规则:乘2取整,最先取得的整数为数的最高位,最后取得的整数为数的最低位,乘积为0或精度满足要求时结束。(即乘2取整,先整为高,后整为低)

例如,将十进制数 123.6875 转换为二进制数。

整数部分:

除2得商 余数

2 |123 … 1 最低位

2 |61 … 1

2 |30 … 0

2 |15 … 1

2 |7 … 1

2 |3 … 1

2 |1 … 1 最高位

2 |0

所以 (123)10 = (1111011)2

小数部分:

乘积取小数 乘2得积 取整数部分

0.6875 × 2 = 1.375 1 最高位

0.375 × 2 = 0.75 0

0.75 × 2 = 1.5 1

0.5 × 2 = 1 1 最低位

所以 (0.6875)10 = (0.1011)2

综合整数和小数部分,得到 (123.6875)10 = (1111011.1011)2

另一种方法是“减权定位法”,利用记忆好的2的幂次的十进制表示,从原始数中依次减去所含最大的2的幂次,就可以快速得到对应的结果。例如,对于十进制数123:

十进制数 位权 转换后的结果

123 26 25 24 23 22 21 20

➖ 64 26 1

59

➖ 32 25 1

27

➖ 16 24 1

11

➖ 8 23 1

3

➖ 2 21 1

1

➖ 1 20 1

0

所以 (123)10 = (1111011)2

这种方法一般在转换很大的十进制数时比较方便。

2.2.2 真值和机器数

在计算机中,如果不加特别的定义,用二进制存储的数都是非负数,不需要加正负号,也就是“无符号数”。

对有符号数而言,符号的“正”、“负”机器本身是无法识别的;不过由于“正”、“负”恰好是两种截然不同的状态,我们可以用“0”表示“正”,用“1”表示“负”,这样符号也被数字化了,并且规定将它放在有效数字的前面,即组成了有符号数。

例如,一个有符号的小数:

+ 0.1011 在机器中表示为 0 1 0 1 1

- 0.1011 在机器中表示为 1 1 0 1 1

再比如,一个有符号的整数:

+ 1100 在机器中表示为 0 1 1 0 0

- 1100 在机器中表示为 1 1 1 0 0

把符号“数字化”的数称为机器数,而把带“+”或“-”符号的数称为真值。一旦符号数字化后,符号和数值就形成了一种新的编码。

  • 真值:正、负号加某进制数绝对值的形式,即机器数所代表的实际值。

  • 机器数:一个数值数据的机内编码,即符号和数值都数码化的数。常用的有原码和补码表示法等,这几种表示法都将数据的符号数字化,通常用“0”表示“正”,用“1”表示“负”。

在计算机中,小数点不用专门的器件表示,而是按约定的方式标出。根据小数点位置是否固定,可以分为两种方法表示小数点的存在,即定点表示和浮点表示。

另外需要考虑的问题是:在运算过程中,符号位能否和数值部分一起参加运算?如果参加运算,符号位又需作哪些处理?这些问题都与符号位和数值位所构成的编码有关。

在现代计算机中,通常用定点补码整数表示整数,用定点原码小数表示浮点数的尾数部分,用移码表示浮点数的阶码部分。

2.2.3 定点数及其编码表示

小数点固定在某一位置的数为定点数,有以下两种格式。

 

当小数点位于数符和第一数值位之间时,机器内的数为纯小数;当小数点位于数值位之后时,机器内的数为纯整数。采用定点数的机器称为定点机。数值部分的位数n决定了定点机中数的表示范围。

在定点机中,由于小数点的位置固定不变,故当机器处理的数不是纯小数或纯整数时,必须乘上一个比例因子,否则会产生“溢出”。

1. 无符号整数的表示

当一个编码的全部二进制位均为数值位时,相当于数的绝对值,该编码表示无符号整数。在字长相同的情况下,它能表示的最大数比带符号整数大。例如,8位无符号整数的表示范围为 0 ~ 28-1,也就是能表示的最大数为255;而8位带符号整数的最大数是127。通常,在全部是正数运算且不出现负值结果的情况下,使用无符号整数表示。例如,可用无符号整数进行地址运算,或用它来表示指针。

2. 带符号数的表示

最高位用来表示符号位,而不再表示数值位。

(1)定点整数

约定小数点在有效数值部分最低位之后。数据 x = x0x1x2…xn (其中 x0 为符号位,x1 ~ x是数值的有效部分,也称尾数),在计算机中的表示形式如图所示:

 

(2)定点小数

约定小数点在有效数值部分最高位之前。数据 x = x0.x1x2...xn(其中 x为符号位,x1~xn 是尾数,x1 是最高有效位),在计算中的表示形式如下图所示:

 

事实上,在计算机中,并没有小数点的表示,只是认为约定了小数点的位置:小数点在最右边的就是定点整数,在最左边的就是定点小数。它们原理相同,只是由于小数点位置不同而可以表示不同范围的数。我们这里重点只考虑定点整数就可以了。

3. 原码、补码、反码和移码

对于有符号的定点数,真实底层的机器数怎样表示,跟选择的编码方式有关。计算机中常用的编码方式有原码、补码、反码和移码。

  1. 原码表示法

用机器数的最高位表示数的符号,其余各位表示数的绝对值。纯小数的原码定义如下:

 

式子中 x 为真值,[ x ] 表示原码机器数。

类似,纯整数的原码定义如下:

 

原码的性质:

  • 由符号位与数的绝对值组成,符号位是0为正、1为负

  • 简单直观,与真值的转换简单

  • 0有 ±0 两个编码,即 [+0] = 00000 和 [-0] = 10000

  • 原码加减运算规则比较复杂,乘除运算规则简单

  1. 补码表示法

纯整数的补码定义为:

 

这里 n 为整数的位数,真值 x 和补码机器数 [ x ] 互为以 2n+1 为模的补数。如果字长为 n+1,那么补码的表示范围为 -2n ≤ x ≤ 2n - 1,比原码多表示了一个数 -2n

补码的性质:

  • 补码和其真值的关系:[x] = 符号位 × 2n+1 + x

  • 0的编码唯一,因此整数补码比原码多1个数,表示 -2n

  • 符号位参与补码加减运算,统一采用加法操作实现

  • 将 [x]补 的符号位与数值位一起右移并保持原符号位的值不变,可实现除法功能

例如,当 x = + 1010 时(n = 4),

[x] = 0, 1010

而当 x = - 1010 时,

[x] = 2n+1 + x = 100000 - 1010 = 1, 0110

补码和真值的转换:

  • 真值转为补码:对于正数, 与原码的转换方式一样;对于负数,符号位为1,其余各位由真值“取反加1”得到。

  • 补码转为真值:若符号位为0,真值为正,跟原码的转换一样;若符号位为1,真值为负,其数值部分(绝对值)各位由补码“取反加1”得到。

变形补码是采用双符号位的补码表示法,其定义为

 

变形补码用于算术运算的ALU部件中,双符号位00表示正,11表示负,10和01表示溢出。

  1. 反码表示法

负数的补码可采用“数值位各位取反,末位加1”的方法得到,如果数值位各位取反而末位不加1,那么就是负数的反码表示。正数的反码定义和相应的补码(或原码)表示相同。

反码表示存在以下几个方面的不足:0的表示不唯一(即存在±0);表示范围比补码少一个最小负 数。反码在计算机中很少使用,通常用作数码变换的中间表示形式。

原码、补码、反码三种编码表示总结如下:

  • 三种编码的符号位相同,正数的机器码相同。

  • 原码和反码的表示在数轴上对称,二者都存在 ±0 两个零。

  • 补码的表示在数轴上不对称,0的表示唯一,补码比原码和反码多表示一个数。

  • 负数的反码、补码末位相差1。

  • 原码很容易判断大小。而负数的补码和反码很难直接判断大小,可采用这条规则快速判断:对于一个负数,数值部分越大,它的绝对值就越小,所以真值就越大(更靠近0)。

  1. 移码表示法

移码是在真值 x 上加上偏置值 2构成的,相当于 x 在数轴上向正方向偏移了若干单位。

 

移码定义为:

 

移码的性质:

  • 0的表示唯一, [+0] = 2n + 0 = [-0] = 2n - 0 = 100...0

  • 符号位“1”表示正,“0”表示负,这与其他机器数正好相反。

  • 一个真值的移码和补码仅差一个符号位,[x] 的符号位取反即得 [x],反之亦然。

  • 移码全0时,对应真值的最小值- 2n ;移码全1时,对应真值的最大值 2n -1。

  • 保持了数据原有的大小顺序,移码大真值就大,便于进行比较操作。

  • 移码常用来表示浮点数的阶码。它只能表示整数。

2.2.4 C 语言中的整型数据类型
  1. C 语言中的整型数据简介

C 语言中的整型数据就是定点整数,一般用补码表示。根据位数的不同,可以分为 字符型(char)短整型(short)整型(int)长整型(long)

C 语言中的整型数据,可以分为 无符号整型有符号整型 两种类型,在定义时只要加上 signed/unsigned 就可以明确指定了。

char 是整型数据中比较特殊的一种,其他如 short/int/long 等都默认是带符号整数,但 char 默认是无符号整数。无符号整数(unsigned short/int/long)的全部二进制位均为数值位,没有符号位,相当于数的绝对值。

signed/unsigned 整型数据都是按补码形式存储的,在不溢出条件下的加减运算也是相同的,只是 signed 型的最高位代表符号位,而在 unsigned 型中表示数值位,而这两者体现在输出上则分别是%d 和%u。

  1. 有符号数和无符号数的转换

C 语言允许在不同的数据类型之间做类型转换。C 语言的强制类型转换格式为“TYPE b = (TYPE) a”, 强制类型转换后,返回一个具有TYPE类型的数值,这种操作并不会改变操作数本身。

先看由 short 型转换到 unsigned short 型的情况。考虑如下代码片段:

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#008855">short</span> <span style="color:#000000">x</span> <span style="color:#981a1a">=</span> <span style="color:#981a1a">-</span><span style="color:#116644">4321</span>;
​
<span style="color:#008855">unsigned</span> <span style="color:#008855">short</span> <span style="color:#000000">y</span> <span style="color:#981a1a">=</span> (<span style="color:#008855">unsigned</span> <span style="color:#008855">short</span>)<span style="color:#000000">x</span>;</span></span>

执行上述代码后,x = -4321, y = 61215,得到的 y 似乎与原来的 x 没有一点关系。不过将这两个数转化为二进制表示时,我们就会发现其中的规律。

通过本例可知:强制类型转换的结果是保持每位的值不变,仅改变了解释这些位的方式。有符号数转化为等长的无符号数时,符号位解释为数据的一部分,负数转化为无符号数时数值将发生变化。同理,无符号数转化为有符号数时,最高位解释为符号位,也可能发生数值的变化。

  1. 不同字长整数之间的转换

另一种常见的运算是在不同字长的整数之间进行数值转换。

先看长字长变量向短字长变量转换的情况。考虑如下代码片段:

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#008855">int</span> <span style="color:#000000">x</span> <span style="color:#981a1a">=</span> <span style="color:#116644">165537</span>, <span style="color:#000000">u</span> <span style="color:#981a1a">=</span> <span style="color:#981a1a">-</span><span style="color:#116644">34991</span>;                      <span style="color:#aa5500">//int型占用4字节</span>
​
<span style="color:#008855">short</span> <span style="color:#000000">y</span> <span style="color:#981a1a">=</span> ( <span style="color:#008855">short</span> )<span style="color:#000000">x</span>, <span style="color:#000000">v</span> <span style="color:#981a1a">=</span> ( <span style="color:#008855">short</span> )<span style="color:#000000">u</span>;            <span style="color:#aa5500">//short型占用2字节</span></span></span>

执行上述代码后,x= 165537, y=-31071, u =-34991, v = 30545。x、y、u、v 的十六进制表示分别 是0x000286a1 0x86a1 . 0xffff7751、0x7751。由本例可知:长字长整数向短字长整数转换时,系统把多余的高位部分直接截断,低位直接赋值,因此也是一种保持位值的处理方法。

最后来看短字长变量向长字长变量转换的情况。考虑如下代码片段:

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#008855">short</span> <span style="color:#000000">x</span> <span style="color:#981a1a">=</span> <span style="color:#981a1a">-</span><span style="color:#116644">4321</span>;
​
<span style="color:#008855">int</span> <span style="color:#000000">y</span> <span style="color:#981a1a">=</span> (<span style="color:#008855">int</span>)<span style="color:#000000">x</span>;
​
<span style="color:#008855">unsigned</span> <span style="color:#008855">short</span> <span style="color:#000000">u</span> <span style="color:#981a1a">=</span> (<span style="color:#008855">unsigned</span> <span style="color:#008855">short</span>)<span style="color:#000000">x</span>;
​
<span style="color:#008855">unsigned</span> <span style="color:#008855">int</span> <span style="color:#000000">v</span> <span style="color:#981a1a">=</span> (<span style="color:#008855">unsigned</span> <span style="color:#008855">int</span>)<span style="color:#000000">u</span>;</span></span>

执行上述代码后,x = -4321, y = -4321, u = 61215, v = 61215。x、y、u、v 的十六进制表示分别是0xef1f. 0xffffef1f、0xef1f、0x0000ef1f。所以,短字长整数向长字长整数转换时,仅要使相应的位值相等,还要对高位部分进行扩展。如果原数字是无符号整数,则进行零扩展,扩展后的高位部分用 0填充。否则进行符号扩展,扩展后的高位部分用原数字符号位填充。其实两种方式扩展的高位部分都可理解为原数字的符号位。

从位值与数值的角度看,前3个例子的转换规则都是保证相应的位值相等,而短字长到长字长的转换可以理解为保证数值的相等。


2.3 运算方法和运算电路

2.3.1 基本运算部件
1. 运算器的基本组成

运算器由算术逻辑单元(ALU)、累加器(AC)、状态寄存器(PSW)、通用寄存器组等组成。

  • 算术逻辑单元:完成加、减、乘、除四则运算,与、或、非、异或等逻辑运算。

  • 累加器:暂存参加运算的操作数和结果的部件,为 ALU 执行运算提供一个工作区。

  • 状态寄存器:也称作标志寄存器,用来记录运算结果的状态信息。

  • 通用寄存器组:保存参加运算的操作数和运算结果。

2. 逻辑门电路和逻辑运算(复习)

用半导体元器件可以构建出基本的逻辑门电路(与、或、非),能够表示基本的逻辑运算。

 

通过对与门、或门、非门的组合,可以构建出更加复杂的逻辑电路,进行各种复杂的组合逻辑运算。

 

逻辑运算中的“与”类似于算术中的乘法,“或”类似于算术中的加法,两者组合在一起时,与运算的优先级要更高。逻辑运算满足以下的规则:

 

3. 全加器(复习)

全加器(Full Adder,FA),是用逻辑门电路实现两个二进制数相加并求出和的组合线路,这称为一位全加器。一位全加器可以处理低位进位,并输出本位加法进位。多个一位全加器进行级联可以得到多位全加器。

一位全加器的真值表如下所示,其中 A 为被加数,B 为加数,相邻低位传来的进位数为 Cin,输出本位和为S,向相邻高位输出的进位数为 Cout

 

根据真值表,很容易写出一位全加器的输出表达式:

 

所以,一位全加器可以利用两个异或门、两个与门和一个或门来实现:

 

4. 并行加法器

对于 n 位加法器,可以用 n 个全加器(实现两个本位数加上低位进位,生成一个本位和一个向高位的进位)串接起来实现逐位相加,位间进行串行传送,称为 串行进位加法器

 

 

这样,一位全加器的输出表达式可以写为:

 

在串行进位链中,进位按串行方式传递,高位仅依赖低位进位,因此速度较慢。

为了提高加法器的速度,必须尽量避免进位之间的依赖。引入进位生成函数和进位传递函数,可以使各个进位并行产生,这种以并行进位方式实现的加法器称为 并行进位加法器

在全加器的表达式中可以看到,进位信号 Ci 由两部分组成:

  • AiBi 与低位无关,可以称为“本地进位”,记作 di

  • (Ai + Bi) Ci-1 与低位进位 Ci-1 有关,可以称为“传递进位”,系数 (Ai + Bi) 称作“传递系数”,记作 ti

这样进位信号就可以简写为:

 

以 4 位并行加法器为例,串行进位链的进位表达式就可以写为:

 

如果我们将 C0 的表达式代入 C1,再依次迭代进 C2、C3 ,那么所有进位信号就只依赖于 C-1 了:

 

并行进位链又称先行进位、跳跃进位,理想的并行进位链就是 n 位全加器的 n 个进位全部同时产生,但实际实现会有困难。一般会使用分组的方式来进行实现,小组内的进位同时产生,小组之间则采用串行进位,这种方式可以总结为“组内并行、组间串行”。

例如,对于 16 位的并行全加器,我们可以 4 位分为一组,得到并行进位链如下:

 

当 n 越来越大时,随着分组的增加,组间串行进位的延迟时间也会越来越长。解决策略是可以采用多重分组,让“小组”间的进位也同时产生,而“大组”间采用串行进位。当有两个层级的分组时,称为“双重分组跳跃进位”;与之对应,之前只有一个层级的分组方式,被称为“单重分组跳跃进位”。

5. 算术逻辑单元(ALU)

针对每一种算术运算,都必须有相对应的基本硬件配置,其核心部件就是加法器和寄存器;而当需要完成逻辑运算时,又必须配置相应的逻辑电路。将这些功能结合起来,就构成了“算术逻辑单元”(ALU)。

ALU 是一种能进行多种算术运算和逻辑运算的组合逻辑电路,ALU的核心是“带标志加法器”,基本结构如下所示。

 

其中 A 和 B 是两个 n 位操作数输入端;Cin 是进位输入端;ALUop 是操作控制端,用来控制 ALU 所执行的处理操作。例如,ALU~op~ 选择 Add 运算,ALU 就执行加法运算,输出的结果就是 A 加 B 之和。ALU~op~ 的位数决定了操作的种类。例如,当位数为3时,ALU最多只有8种操作。

F 是结果输出端,此外,还输出相应的标志信息(ZF、OF、SF、CF);在ALU进行加法运算时,可以得到最高位的进位 Cout

2.3.2 定点数的移位运算

移位运算根据操作对象的不同,可以分为算术移位和逻辑移位。算术移位针对的是有符号数,逻辑移位针对的是机器码,可以看作无符号数。

1. 算术移位

算术移位的对象是有符号数,有符号数在计算机中采用补码表示。算术移位的特点是,移位后符号位保持不变;空出的位置根据正负和左右移位的情况,决定补 0 还是 1。

  • 对于正数,由于 [ x ] = [ x ] = 真值,因此移位后的空位均补 0。

  • 对于负数,算术左移时,高位移出,低位补 0;算术右移时,低位移出,高位补 1。

可见,不论是正数还是负数,移位后其符号位均不变。

例如,假设机器字长为 8,[4] = 0000 0100,[-4] = 1111 1100;

  • 将 4 算术左移一位,就得到了 0000 1000 = [8];算术右移一位,就得到了 0000 0010 = [2]

  • 将 -4 算术左移一位,就得到了 1111 1000 = [-8];算术右移一位,就得到了 1111 1110 = [-2]

对于有符号数,左移一位若不产生溢出,相当于乘以2 (与十进制数左移一位相当于乘以10类似); 右移一位,若不考虑因移出而舍去的末位尾数,相当于除以2。

2. 逻辑移位

逻辑移位不考虑符号位。

移位规则:逻辑左移时,高位移出,低位补 0;逻辑右移时,低位移岀,高位补 0 。

2.3.3 定点数的加减运算

加减法运算是计算机中最基本的运算,由于减法可以看成是负值的加法,因此计算机中使用补码表示有符号数之后,可以将减法运算和加法运算合并在一起讨论。

1. 补码的加减运算

补码加减运算的规则简单,易于实现。补码加减运算的公式如下(设机器字长为 n): ​ [A + B] = [A] + [B] (mod 2n) ​ [A - B] = [A] + [-B] (mod 2n)

补码运算的特点如下:

  • 按二进制运算规则运算,逢二进一。

  • 如果做加法,两数的补码直接相加;如果做减法,则将被减数加上减数的机器负数。

  • 符号位与数值位一起参与运算,加、减运算结果的符号位也在运算中直接得出。

  • 最终将运算结果的高位丢弃,保留 n 位,运算结果也是补码。

例如,假设机器字长为 8 (n = 8),那么

[5] = 0000 0101,[4] = 0000 0100;

[-5] = 1111 1011,[-4] = 1111 1100;

[5 + 4] = 0000 0101 + 0000 0100 = 0000 1001 = [9]

[5 - 4] = [5 + (-4)] = 0000 0101 + 1111 1100 = 1 0000 0001 = [1]

[4 - 5] = [4 + (-5)] = 0000 0100 + 1111 1011 = 1111 1111 = [-1]

[-5 - 4] = [-5 + (-4)] = 1111 1011 + 1111 1100 = 1 1111 0111 = [-9]

2. 补码加减运算电路

利用带标志的加法器电路,可实现补码加减运算。

 

当控制端信号 Sub 为 0 时,做加法,Sub 控制多路选择器将 ${B}$ 输入加法器,实现 $A + B = [A]补 + [B]补$。当控制端 Sub 为 1 时,做减法,Sub 控制多路选择器将 $\overline{B}$ 的非输入加法器,并将 Sub 作为低位进位送到加法器,实现 $A + \overline{B} + 1 = [A]补 + [-B]补$。

无符号整数的二进制表示相当于正整数的补码表示,因此,该电路同时也能实现无符号整数的加/减运算。可通过标志信息对运算结果进行不同的解释。

  • 零标志 ZF = 1 表示结果 F 为 0。不管对于无符号整数还是有符号整数运算,ZF 都有意义。

  • 溢出标志 OF = 1 表示有符号数运算时发生溢出。对于无符号数运算,OF 没有意义。

  • 符号标志 SF = 1 表示有符号数运算结果为负;有符号数运算结果为正时 SF = 0。对于无符号数运算,SF 没有意义。

  • 进/借位标志 CF 表示无符号整数运算时的进/借位,判断是否发生溢出。做加法时,CF = 1 表示结果溢出,因此 CF 等于进位输岀 C~out~。做减法时,CF = 1 表示有借位,即不够减,故 CF 等于进位输出 C~out~ 取反。综合可得 CF = Sub ㊉ C~out~。对于有符号数运算,CF 没有意义。

在之前的例子中,如果表示的是无符号数,那么:

[5 + 4] = 0000 0101 + 0000 0100 = [5] + [4] = 0000 1001 = [9]; (加法不溢出)

[5 - 4]补 = 0000 0101 + 1111 1100 = [5] + [252] = 1 0000 0001 = [1]; (加法溢出、减法不溢出)

[4 - 5] = 0000 0100 + 1111 1011 = [4] + [251]= 1111 1111 = [255]; (加法不溢出、减法溢出)

[-5 - 4] = 1111 1011 + 1111 1100 = [251] + [252] = 1 1111 0111 = [247]; (加法溢出)

3. 溢出判别方法

溢出 是指运算结果超出了数的表示范围。通常,大于能表示的最大正数称为正上溢,小于能表示的最小负数称为负上溢。仅当两个符号相同的数相加,或两个符号相异的数相减才可能产生溢出。

在之前的例子中,如果假设机器字长为 4(n = 4),能表示的有符号数范围为 -8 ~ 7,那么就有:

[5] = 0101,[4] = 0100;

[-5] = 1011,[-4] = 1100;

[5 + 4] = 0101 + 0100 = 1001 = [-7]; (正溢出)

[5 - 4] = [5 + (-4)] = 0101 + 1100 = 1 0001 = [1]

[4 - 5] = [4 + (-5)] = 0100 + 1011 = 1111 = [-1]

[-5 - 4] = [-5 + (-4)] = 1011 + 1100 = 1 0111 = [7]; (负溢出)

补码加减运算的溢出判断方法有以下 3 种:

(1)采用一位符号位。

由于减法运算在机器中是用加法器实现的,减法可以看作一个正数和一个负数的加法;因此无论是加法还是减法,只要参加操作的两个数符号相同,结果又与原操作数符号不同,就表示结果溢出。

比如上例中,一正一负相加必然不会溢出;两正数相加得到一个负数(符号位为1),则正溢出;两负数相加得到一个正数,则负溢出。

在实际应用中,为了节省时间,通常可以直接判断符号位产生进位 C~s~ 与最高数位的进位 C~1~ 。如果相同说明没有溢出,否则说明发生溢出。溢出标志 OF = C~s~ ㊉ C~1~。

(2)采用双符号位。

运算结果的两个符号位相同,表示未溢出;运算结果的两个符号位不同,表示溢出,此时最高位就代表真正的符号。也就是说,符号位 S~1~S~2~ = 00 表示结果为正数,无溢出; S~1~S~2~ = 11 表示结果为负数,无溢出。 S~1~S~2~= 01 表示结果正溢出; S~1~S~2~ = 10 表示结果负溢出。溢出标志 OF = S~1~ ㊉ S~2~。

比如上例中,如果采用双符号位,机器字长就应该扩展为 5,那么:

[5] = 00 101,[4] = 00 100;

[-5] = 11 011,[-4] = 11 100;

[5 + 4] = 00 101 + 00 100 = 01 001 = [1]; (正溢出)

[5 - 4] = [5 + (-4)] = 00 101 + 11 100 = 1 00 001 = [1]

[4 - 5] = [4 + (-5)] = 00 100 + 11 011 = 11 111 = [-1]

[-5 - 4] = [-5 + (-4)] = 11 011 + 11 100 = 1 10 111 = [-1]; (负溢出)

2.2.4 定点数的乘法运算

乘除运算的原理难度较大,考查的概率也较低,做基本了解即可。

在计算机中,乘法运算由累加和右移操作实现。根据机器数的不同,可分为原码一位乘法和补码一位乘法。原码一位乘法的规则比补码一位乘法的规则简单。

1. 原码一位乘法

原码乘法运算的符号位与数值位分开计算。

  • 确定乘积的符号位。由两个乘数的符号进行异或运算得到。

  • 计算乘积的数值位。两个乘数的数值部分之积,可看作两个无符号数的乘积。

原码一位乘法的基本思路,就是类似竖式乘法的做法,让被乘数 x 分别乘以乘数 y 的每一位,然后再做叠加。不过竖式乘法需要做连加运算,这在电路实现上会有一些困难;改进的做法是,借鉴进制转换的“重复相乘/除法”,对每一位进行迭代计算。

回忆一下二进制数转换成十进制数的重复相乘/除法:

整数部分从高到低,将每一位乘以基数值、再加上后一位,进行“重复相乘”:

(11011)2 = (((1 × 2 + 1) × 2 + 0 ) × 2 + 1) × 2 + 1 = 27

小数部分从低到高,将每一位除以基数值、再加上前一位,进行“重复相除”:

(0.101)2 = ((1 ÷ 2 + 0) ÷ 2 + 1 ) ÷ 2 + 0 = 0.625

所以,两数相乘时,就可以把乘数 y 用这种方式按每一位拆开,并乘以 x 、再逐位叠加就可以了。由于每次乘以 2 就相当于左移一位、除以 2 就相当于右移一位,因此只需要反复迭代这样的 移位加法 运算就可以很容易地实现乘法了。

以纯小数为例,已知 [ x ]~原~ = x~0~ . x~1~x~2~...x~n~,[ y ]~原~ = y~0~ . y~1~y~2~...y~n~,那么

 

原码一位乘法的运算规则如下:

  • 被乘数和乘数均取绝对值|x| 和 |y|参加运算,看作无符号数,符号位为 x~0~ ㊉ y~0~。

  • 乘数的每一位 y~i~ 乘以被乘数 |x| 得到 |x| · y~i~,将该结果与前面所得的结果相加,作为部分积;初始值为 0。

  • 从乘数的最低位 y~n~ 开始判断:若 y~n~ = 1,则部分积加上被乘数 |x|,然后右移一位;若 y~n~ = 0,则部分积加上 0,然后右移一位。

  • 重复上一步骤,判断 n 次。

由于参与运算的是两个数的绝对值,因此运算过程中的右移操作均为逻辑右移。


例如,当 x = 0.1101 = (0.8125)~10~,y = 0.1011 = (0.6875)~10~ 时,计算 x · y。

 

最终的乘积,高位在“部分积”中,低位在“当前乘数”中,所以得到:

x · y = 0.1101 × 0.1011 = 0.10001111 = (0.55859375)~10~


2. 无符号数乘法运算电路

下面是实现两个 32 位无符号数乘法运算的逻辑结构图。

 

X、Y 均为 32 位寄存器,开始时分别用来存放被乘数 x 、乘数 y ;P 也是 32 位寄存器,用来存放乘积(部分积),初始值置 0。部分积和被乘数做无符号数加法时,可能产生进位,因此还需要一个专门的进位位 C。 C~n~ 为计数器,初始值为 32,此后每进行一轮运算就减 1。

每一轮运算都由寄存器 Y 的最低位 y~n~ 来控制具体逻辑:如果 y~n~ = 1,那么就让 ALU 对乘积寄存器 P 和被乘数寄存器 X 的内容做“无符号加法”运算,运算结果送回寄存器 P,进位存放在 C 中;如果 y~n~ = 0 则不做相加。无论是否做了加法,每一轮运算都会对进位位 C、乘积寄存器 P 和乘数寄存器 Y 实现同步的“逻辑右移”;此时,进位位 C 移入寄存器 P 的最高位,寄存器 Y 的最低位 y~n~ 被移出。每次从寄存器 Y 移出的最低位 y~n~ 都被送到控制逻辑,以决定被乘数是否被加到部分积上。

最终,当原始乘数所有位全部被移出时,寄存器 P 和 Y 就分别存放了乘积的高 32 位和低 32 位。在 CPU 的运算器中,就分别对应了累加器 ACC 和乘商寄存器 MQ。

3. 补码一位乘法

原码一位乘法容易理解,规则也比较简单;缺点是符号位不参与计算,需要单独判断。更重要的是,在计算机中为了方便做加减计算,数据一般是用补码来表示的;这就使得如果我们采用原码乘法,需要计算前先把补码转换成原码,计算结束后再把结果的原码转换为补码,增加了额外的运算。

补码一位乘法 是一种带符号数的乘法,采用相加/相减的校正操作,直接计算补码数据的乘积。

补码乘法是直接对补码进行的。对于纯整数,补码表达为:

 

而类似的,纯小数补码定义为:

 

所以,当取不同的正负符号时,补码表达会有所不同,继而影响到逐位相乘叠加的效果。

已知 [ x ]~补~ = x~0~ . x~1~x~2~...x~n~,[ y ]~补~ = y~0~ . y~1~y~2~...y~n~,那么需要分不同的情况讨论:

① 被乘数 x 和乘数 y 符号均为正,即 x~0~ = y~0~ = 0 时,

[ x ]~补~ = x,[ y ]~补~ = y,所以就有:

 

类似原码一位乘法,利用移位和加法的叠加,就可以计算出补码的乘积;这也就是最终计算结果的补码。

② 被乘数 x 为正,乘数 y 为负,即 x~0~ = 0,y~0~ = 1 时,

[ y ]~补~ = 1. y~1~y~2~...y~n~ = 2 + y,所以:

 

那么两数的乘积就可以写成:

 

这样一个计算结果,它的补码表示为:

 

可以看到,当乘数为负时,可以把乘数补码 [ y ]~补~ 直接去掉符号位,当成一个正数与 [ x ]~补~ 相乘;得到的结果再加上 [ -x ]~补~ 进行校正。所以这种方法也叫做“校正法”。


例如,当 x = 0.1101 = (0.8125)~10~,y = 1.1011 = (-0.3125)~10~ 时,计算 x · y。

我们可以直接计算 0.1101 × 0.1011 = 0.10001111,再加上 [ -x ]~补~ = 1. 0011,得到 :

0.10001111 + 1. 0011 = 1.10111111 = ( -0.25390625 )~10~


③ 被乘数 x 为负,乘数 y 为正,即 x~0~ = 1,y~0~ = 0 时,

我们可以交换被乘数和乘数,直接按情况②来处理;也可以仔细分析,发现乘数 y 为正数,可以写成

[ y ]~补~ = 0. y~1~y~2~...y~n~ 的形式,同样可以借鉴情况②中的分析和原码一位乘的方法。当两数的补码相乘时:

 

观察可以发现,与原码一位乘完全类似,补码相乘也可以将乘数展开,逐位进行相乘、右移和叠加。不过需要注意的是,这时由于被乘数 x 是负数,右移时就需要在左侧高位补 1,也就是做算术右移、而不是逻辑右移。

这样一来,算术右移就实现了对真值 x 的“除以 2”操作,最终叠加之后的结果,就是 x · y 的补码了。

例如,当 x = 1.1 = (-0.5)~10~,y = 0.011 = (0.375)~10~ 时,计算 x · y。

 

最终的乘积,高位在“部分积”中,低位在“当前乘数”中,所以得到:

x · y = 1.1 × 0.011 = 1.1101 = ( - 0.1875 )~10~

④ 被乘数 x 和乘数 y 符号均为负,即 x~0~ = y~0~ = 1 时,

通过情况②和③的分析可以看出,当乘数 y 为正时,可以直接按照原码一位乘的方式进行补码乘法,注意需要进行算术右移;而当乘数 y 为负时,则可以先不考虑 y 的符号位,同样按照原码一位乘进行补码乘法,最后的结果要再加上 [ -x ]~补~ 进行校正。

例如,当 x = 1.1 = (-0.5)~10~,y = 1.011 = (-0.625)~10~ 时,计算 x · y。

我们可以直接计算 1.1 × 0.011 = 1.1101,再加上 [ -x ]~补~ = 0.1,得到 :

1.1101 + 0.1 = 1 0.0101 = ( 0.3125 )~10~

可以看出,如果使用双符号位来表示正负,会更加方便。

Booth算法

以上的 4 种情况需要分别讨论,根据乘数的符号来决定是否需要进行校正,这就导致校正法的逻辑控制电路比较复杂。

如果不考虑操作数的符号,直接用统一的规则来处理所有情况,可以采用 比较法。这种方式是 Booth 夫妇首先提出的,所以又叫 Booth 算法

当被乘数 x 和乘数 y 符号任意时,按照之前讨论的校正法规则,可以写出一个统一的计算公式:

 

容易推出,对于纯小数,在 mod 2 的前提下,[ -x ]~补~ = - [ x ]~补~,所以可以进一步推导得到:

 

令 y~n+1~ = 0,那么就可以得到一个通项系数: d~i~ = y~i+1~ - y~i~ ,上式可以进一步化简为:

 

这样一来,补码乘法的计算方式就跟原码一位乘完全一样了,只是被乘数每次乘的不再是乘数 y 的每一位 y~i~,而是变成了 d~i~ = y~i+1~ - y~i~ 。这样就有 1、-1 和 0 三种情况,每一次计算都由 d~i~ 来决定部分积叠加的是 [ x ]~补~、[ -x ]~补~ 还是 0;然后再做一位算术右移得到新的部分积。最后一步,需要由 d~0~ = y~1~ - y~0~ 决定是否有叠加项,但不再做位移。

Booth 算法的移位规则如下表所示:

 

Booth 算法的具体运算规则如下:

① 符号位参与运算,运算的数均以补码表示。

② 被乘数一般取 双符号位 参与运算,部分积取 双符号位,初值为 0,乘数取单符号位。

③ 乘数末尾增加一个“附加位” y~n+l~,初始值为 0。

④ 根据(y~i~,y~i+1~)的取值来确定操作,如上表所示。

⑤ 移位按补码右移规则(算术右移)进行。

⑥ 按照上述算法进行 n + 1 步操作,但第 n + 1 步不再移位,仅根据 y~0~ (符号位)与 y~1~ (第一位数值位)的比较结果做相应的叠加运算。所以总共需要进行 n + 1 次累加和 n 次右移。


例如,当 x = 1.1101 = (-0.1875)~10~,y = 1.1011 = (-0.3125)~10~ 时,计算 x · y。

首先得到 [x]~补~ = 11.1101,[-x]~补~ = 00.0011。具体计算步骤如下:

 

同样,最终的乘积,高位在“部分积”中,低位在“当前乘数”中,所以得到:

x · y = 1.1101 × 1.1011 = 0.00001111 = (0.05859375)~10~


4. 补码乘法运算电路

对于有符号数的乘法运算,可以采用补码一位乘。与原码一位乘类似,我们可以用下面的逻辑结构图来实现补码一位乘法的运算电路:

 

因为是带符号数运算,所以不需要专门的进位位。由于采用 Booth 算法,还需要一个额外的“附加位” y~n+1~。每轮运算,乘积寄存器 P 和乘数寄存器 Y 实现同步的一位“算术右移”,每次从寄存器 Y 移出的最低位成为新的附加位 y~n+1~,它的前一位则成为 y~n~,它们共同决定叠加项是 [ x ]~补~、[ -x ]~补~ 还是 0。另外由于 ALU 可以对加减法统一操作,所以电路种只需要选择加/减一项 [ x ]~补~ 就可以了。

2.2.5 定点数的除法运算

除法是乘法的逆运算,但不像加减法那样可以直接整合。我们可以先从除法的竖式笔算入手,分析一下除法的具体步骤。


例如,当 x = (-0.1011)~2~ = (-0.6875)~10~,y = (0.1101)~2~ = (0.8125)~10~ 时,计算 x / y。

首先可以看出,商的符号为负,余数的符号为负;其次利用竖式计算绝对值的商:

 

所以可以得到, x / y = - 0.1101(商)... - 0.00000111(余数) = (- 0.8125 ... - 0.02734375 )~10~


如果完全按照竖式除法的规则,需要心算上商,本质就是由当前被除数减去除数乘以当前权值(第 i 轮就乘以 2^-i^),够减就上商 1,不够就上商 0 。得到的余数补 0 后再作为下一轮的被除数进行计算。这个过程中,每轮除数要乘以权值 2^-i^,相当于右移 i 位,得到的余数左侧全部是 0;如果被除数 x、除数 y 和商都是 4 位,就需要 8 位数据来保存余数,这就显得有些麻烦。

更加简单的做法是,每轮相减除数不变,把余数左移;这样效果一样,而电路实现会更加简单。当然,代价就是得到的余数是经过左移之后的;n 轮计算完成之后,需要再右移 n 位,也就是乘以 2^-n^ 才是真正的余数。每轮的相减,也可以转换成负数补码的加法。

所以跟乘法类似,除法运算在计算机中,是转换成逐位的“累加-左移”操作来实现的,可以分为原码除法和补码除法。

1. 原码一位除法

原码一位除法和原码一位乘法一样,特点是符号位单独处理。商符由两个操作数的符号位做“异或”形成,减法操作用补码加法实现。

同样以小数为例,已知 [ x ]~原~ = x~0~ . x~1~x~2~...x~n~,[ y ]~原~ = y~0~ . y~1~y~2~...y~n~,那么

 

其中,0 . x~1~x~2~...x~n~ 就是 x 的绝对值,记作 x^^ ;0 . y~1~y~2~...y~n~ 是 y 的绝对值,记作 y^^ 。商符由两数符号位异或得到,商值由两数绝对值相除得到。

小数定点除法对被除数和除数有一定的约束条件:

0 <| 被除数 |≤| 除数 |

实现除法运算时,被除数应不大于除数,并且应该避免被除数和除数为 0。商的位数一般和操作数位数相同。

原码除法中,每轮计算需要用被除数绝对值减去除数绝对值:x^^ - y^^。计算机中减法可以用负数补码的加法实现,所以最终的操作就是:[x^^]~补~ + [- y^^]~补~ 。如果结果为正,说明够减,上商 1,结果作为余数直接左移,并作为下一轮被除数;如果结果为负,说明不够减,上商 0,这时的结果并不是真实的余数。

根据对余数的处理方式不同,又可以分为 恢复余数法不恢复余数法(加减交替法) 两种。

(1)恢复余数法

恢复余数法的特点是:当余数为负时,需要加上除数的绝对值,将其恢复成原本的余数。

由于每次得到的是商的高位,所以每轮计算可以将余数和商同时左移一位;余数加上 [- y^^]~补~ ,判断正负来决定下一位商是 1 还是 0;如果为负,还需要先加上 [y^^]~补~ 恢复余数,然后再做左移。


例如,当 x = (-0.1011)~2~ = (-0.6875)~10~,y = (-0.1101)~2~ = (-0.8125)~10~ 时,计算 x / y。

首先看出,商的符号为正,余数的符号为负。并且得到:

x^^ = 0.1011,y^^ = 0.1101,[y^^]~补~ = 0.1101,[-y^^]~补~ = 1.0011

具体计算过程如下:

 

所以商值为 x^^ / y^^ = 0.1101;而余数由于经过了 4 次左移,所以最终还应该做 4 次右移才是真正的余数:0.0111 * 2^-4^ = 0.00000111,另外还要注意余数符号为负,所以最终结果为:

x / y = 0.1101(商)... - 0.00000111(余数)


由上例也可以发现,如果最终商保留 4 位,那么我们需要做 5 次上商,第一次上商其实是商的整数位;由于我们要求 |被除数| ≤ |除数|,因此正常情况下第一位商总是 0。所以对于小数除法而言,可以用它来做溢出判断:当该位为 1 时,表示当前除法溢出,不能进行;当该位为 0 时,当前除法合法,可以进行。

(2)不恢复余数法(加减交替法)

在恢复余数法中,每当余数为负时都需要恢复余数,这就增加了运算量,操作也不规则,电路实现会比较复杂。加减交替法就克服了这一缺点。

加减交替法 又称 不恢复余数法,是对恢复余数法的一种改进。

通过分析恢复余数法可以发现,如果把第 i 轮计算的余数记作 R~i~,那么:

  • 如果 R~i~ > 0,就上商 1,接下来需要将余数 R~i~ 左移一位,再减去除数绝对值 y^^,即 2R~i~ - y^^;

  • 如果 R~i~ < 0,就上商 0,接下来先加上 y^^ 恢复余数,再做左移和减法,即 2 (R~i~ + y^^) - y^^ = 2R~i~ + y^^ 。

这样一来,就不需要额外恢复余数了,每轮计算的规则完全统一起来,只是左移之后再加/减 y^*^ 就可以了;所以把这种方法叫做“加减交替法”,或者“不恢复余数法”。


还是上面的例子,当 x = (-0.1011)~2~ ,y = (-0.1101)~2~ 时,计算 x / y。

同样的步骤,首先看出,商的符号为正,余数的符号为负。并且得到:

x^^ = 0.1011,y^^ = 0.1101,[y^^]~补~ = 0.1101,[-y^^]~补~ = 1.0011

具体计算过程如下:

 

所以商值为 x^^ / y^^ = 0.1101;而余数由于经过了 4 次左移,所以最终还应该做 4 次右移才是真正的余数:0.0111 * 2^-4^ = 0.00000111,另外还要注意余数符号为负,所以最终结果为:

x / y = 0.1101(商)... - 0.00000111(余数)


2. 补码一位除法(加减交替法)

与乘法类似,我们同样可以直接使用补码来实现除法操作。补码除法也可以分为恢复余数法和加减交替法,由于加减交替法是改进版本,用得较多,所以我们只讨论加减交替法。

补码一位除法的特点是符号位与数值位一起参加运算,商符自然形成;所有操作数和结果都用补码表示。

二进制除法运算的核心,是“求余数”,本质上就是被除数和除数绝对值的相减;而补码除法是不区分正负统一进行计算的,因此我们需要讨论在不同的正负情况下,如何实现被除数和除数绝对值的相减操作。

 

被除数和除数的绝对值相减,得到了余数,接下来就可以根据是否“够减”(即 |x| ≥ |y|)来上商。

需要注意的是,由于商也需要用补码表示,因此当商为正时,够减时上 1,不够减时上 0;而当商为负时需要取反码,够减时上 0,不够减时上 1。补码是原码“取反加1”得到的,所以负商的最后一位还应该再加1,这比较麻烦;如果对精度没有特殊要求,我们一般可以约定商“末位恒置 1”,这种方法操作简单,最大误差为 2^-n^。

对于新余数 R~i+1~ 的计算,可以借鉴原码除法的加减交替法,根据当前余数 R~i~ 和除数 y 的符号,决定加/减 y 的绝对值,这最终可以用加上 [y]~补~ 或者 [-y]~补~ 来实现。事实上,将上一轮的余数 R~i~ 左移之后就得到了下一轮的被除数,再根据被除数、除数的符号关系就可以得到 R~i+1~ 的表达了。

上面表格中,对商值的判断有点繁琐,可以化简如下:

 

在补码除法中,商符可以在求商的过程中自动生成。

由于要求 |x| ≤ |y|,因此当做第一轮计算时必须”不够减“。如果 x 和 y 同号,那么它们都为正时 R~i~ 应该为负、都为负时 R~i~ 应该为正,即 R~i~ 和 y 必须异号;此时商值为 0,而商符也恰恰要求是正,说明除法运算是合法的。如果 R~i~ 和 y 同号,则说明够减,商值为1,此时发生了溢出。同样,当 x 和 y 异号时,也可以发现商值为 1 时表示”不够减“,是正常情况,这时商符也要求是负的,完全一致。

所以,补码除法中商的符号也可以用来判断溢出。

由于商符自动生成,我们可以把上表进一步化简:

 

可以总结补码一位除法的运算规则如下:

  • 符号位参加运算,除数与被除数均用补码表示,商和余数也用补码表示。

  • 如被除数与除数同号,则被除数减去除数;如被除数与除数异号,则被除数加上除数。

  • 余数与除数同号,商上1,余数左移一位再减去除数;余数与除数异号, 商上 0,余数左移一位再加上除数。

  • 重复执行上一步操作,操作 n 次。

  • 如果对商的精度没有特殊要求,一般采用“末位恒置 1”法。


同样的例子,当 x = (-0.1011)~2~ ,y = (-0.1101)~2~ 时,计算 x / y。

首先得到: [x]~补~ = 1.0101,[y]~补~ = 1.0011,[-y]~补~ = 0.1101

具体计算过程如下:

 

所以商值为

x / y = 0.1101


3. 除法运算电路

n 位定点数的除法运算,可以看作用一个 2n 位的被除数 x,去除以一个 n 位的除数 y。这就需要对被除数进行扩展。下图是一个 32 位除法运算的逻辑结构图。

 

由于被除数 x 是 64 位,所以需要两个 32 位的寄存器 R 和 Q 来存放。初始时,寄存器 R 存放扩展被除数的高位部分,寄存器 Q 存放扩展被除数的低位部分。除数 y 则放在除数寄存器 Y 中。

跟乘法运算电路类似,ALU 也是除法器的核心部件,每轮计算中都要对余数寄存器 R 和除数寄存器 Y 的内容做加/减运算,运算结果送回寄存器 R。开始时,首先取 R 中的数据,也就是 x 的高 32 位,根据符号关系与 y 进行加/减运算,得到的余数写回 R 中;之后每轮计算,寄存器 R 和 Q 实现同步左移,左移时,Q 的最高位移入 R 的最低位,Q 中空出的最低位用来上商。所以 Q 也叫做 余数/商寄存器。每次都会根据余数的符号,交给控制逻辑电路来判断当前商为 1 还是 0,并控制 ALU 的加/减信号。

2.4 浮点数的表示和运算

2.4.1 浮点数的表示

计算机中处理的数,不一定都是纯小数或者纯整数。对于一个同时有整数部分和小数部分的数,我们可以把它拆开,分别用定点整数和定点小数来表示;这样的缺点在于需要手动进行调整组合,往往要编程来调节小数点的位置。而且有些数据的数值范围相差很大,比如太阳的质量是 1.989 × 10^30^ kg,而电子的质量是 9.110 × 10^-31^ kg,如果直接写成 定点整数 + 定点小数 的形式就需要很大的机器字长,造成有效数位的浪费。

更好的方式是,小数点不再固定,使用 ”浮点数“ 来表示。

浮点数 就是小数点的位置可以浮动的数。例如:

365.242 = 3.65242 × 10^2^

= 365242.0 × 10^-3^

= 0.365242 × 10^3^

这其实就是”科学计数法“的思路。通常,浮点数被表示为下面的形式:

$$
N = S \times r^j
$$

上式中,S 称作 尾数(可正可负),j 称作 阶码(可正可负),r 是 基数。在计算机中基数一般取 2,也可以取 4、8 或 16 等。

例如,一个二进制数 N = 11.0101,那么它可以写成各种不同的浮点形式:

N = 11.0101

= 1.10101 × 2^1^

= 0.110101 × 2^10^

= 11010.1 × 2^-11^

= 0.00110101 × 2^100^

...

为了提高数据精度,并且便于比较,在计算机中规定浮点数的尾数用 纯小数形式 表示。所以上面的 0.110101 × 2^10^ 和 0.00110101 × 2^100^ 两种形式都可以在计算机中表示 N。不过很明显,后一种形式对有效数位是一种浪费,所以将尾数最高位为 1 的浮点数称为 规格化数

这样,N 就有了唯一的规格化浮点形式:

N = 0.110101 × 2^10^

浮点数表示为规格化形式,精度是最高的。

1. 浮点数的表示格式

在计算机中,浮点数的格式如下图所示。采用这种数据格式的机器称为 浮点机

 

浮点数由 阶码 j尾数 S 两部分组成。

  • 阶码是纯整数,阶符和阶码值合起来决定了小数点的实际位置;阶码值的位数 m 再结合阶符,可以反映浮点数的表示范围。

  • 尾数是纯小数,数符 S~f~ 代表了浮点数的正负,而尾数值则是有效数位,位数 n 反映了浮点数的精度。

2. 浮点数的表示范围

假设浮点数 N 的阶码 j 数值部分有 m 位,尾数 S 数值部分有 n 位。

阶码是纯整数,尾数是纯小数,它们可以各自选择编码方式。对于非规格化的浮点数,如果阶码和尾数都用原码表达,各自的取值范围如下:

 

那么阶码 j 和尾数 S 组合之后,能表示的浮点数最大范围就是:

 

  • 正数最大为 $ (1-2^{-n})\times2^{2^m-1} $ , 最小为 $ 2^{-n}\times2^{-(2^m-1)} $ ;

  • 负数最大为 $ -2^{-n}\times2^{-(2^m-1)} $ , 最小为 $ -(1-2^{-n})\times2^{2^m-1} $ ;

  • 当 S = 0 ,j = - (2^m^ - 1) 时,可以表示 0 值。

在数轴上表示出来,如下图所示:

 

可以看到,原码是关于原点对称的,所以浮点数的表示范围也是关于原点对称的。

当运算结果大于能表示的最大正数时,称为正上溢;小于最小负数时。称为负上溢:两者统称 上溢。由于尾数的溢出可以通过移位、增加阶码来调整,因此上溢的本质就是 阶码大于最大阶码,这时机器会停止计算,进行中断溢出处理。

当运算结果在 0 至最小正数之间时,称为正下溢;在 0 至最大负数之间时,称为负下溢,统称 下溢。同样道理,下溢的本质是 阶码小于最小阶码,这时溢出的数值绝对值非常小,通常可以将尾数各位直接强置为 0,按 ”机器零“ 来处理,机器可以继续正常运行。

类似地,如果阶码和尾数都用补码表达,各自的取值范围如下:

 

具体应用当中,阶码和尾数到底应该选择哪种编码方式呢?

很明显,采用补码的优势在于方便做加减运算,但是在表示尾数时,数值范围就无法相对原点对称了。而且对于浮点数来说,尾数是不能直接做加减的,需要先把阶数 ”对齐“ ,所以使用补码好处并不明显;反倒是在做乘除运算时,阶码需要直接做加减操作。

所以我们可以发现,阶码可以用补码表示;而尾数用原码和补码表示都可以,用原码会更好一点。

进一步分析会发现,其实只要尾数 S = 0,无论阶码 j 取什么值都应该表示 0 值。不过浮点数的尾数是受精度限制的,只要阶码没有达到最小值,就还可以通过继续减小阶码、让尾数的数值左移,这样就能使得之前丢弃的低位重新成为有效数位。所以只有当阶码已经是最小值时,尾数 S = 0,才能说明当前的数值已经足够小了,可以认为就是 0;这时的尾数本质上是一个 ”接近 0 的很小的数“,那它的正负就还是有意义的,所以原码的 ± 0 也有了独特的含义。

不过这样一来,”机器零“ 的机器码不全为 0,而是 1 0 0 ... 0;这会对实现计算机的 ”判 0“ 电路造成一些麻烦。所以我们可以考虑把阶码做个调整,让它能够用 ”全 0 “ 的机器码来表示真正的 0 值。考虑到阶码范围 ”关于原点对称“ ,我们要表示的阶码真值范围还是 -2^m^ ~ 2^m^ - 1,所以要让全 0 的机器码对应最小的真值 -2^m^,只要整体做一个 2^m^ 的偏移就可以了——这就是 “移码” 的编码方式。

 

 

用移码来表示阶码还有一个好处,就是方便进行阶数的比较和对齐,简称 “对阶”。这在进行浮点数加减运算时非常重要,尾数只有在阶数相同的时候才能做加减。如果用补码表示阶码,那么可以通过阶码相减来确定哪个阶码大;而采用移码则更加简单,直接比较两个阶码的二进制大小关系就可以了,这在电路上更加容易实现。

阶码采用移码、尾数采用原码编码后,各自的取值范围如下:

 

一旦确定了浮点数的位数,那么如何分配阶码和尾数的位数,将直接影响到浮点数的表示范围和精度。整体来说,阶码位数越多,说明浮点数的表示范围越大;而尾数位数越多,说明浮点数的精度越高。

3. 浮点数的规格化

由于规格化数的精度最高,所以当一个非零的浮点数不是规格化数时,应该通过左右移动尾数、并同时修改阶码的方法,将它转换为规格化数。把一个非规格化数转换成规格化数的过程,叫做 规格化

规格化的本质类似于 “科学计数法” 的表达,通过保证尾数最高数位上是一个有效值,尽可能多地保留有效数字的尾数,从而提高精度。

规格化可以分为 “左规” 和 “右规” 两种。以基数 r = 2 为例:

  • 左规:向左规格化。当运算结果尾数的最高数位不是有效位,即出现 0.0...01... 的形式时,需要向左规格化。左规时,尾数左移一位,阶码减 1;

  • 右规:向右规格化。当运算结果尾数的小数点左侧出现有效位,即整数部分不为 0 时,需要向右规格化。右规时,尾数右移一位,阶码加 1;需要右规时,只需进行一次。

当基数不同时,规格化的原则会有相应的改变。比如,当基数 r = 4 时,阶码每次加/减 1,就相当于多乘/除以 4,也就是左/右移 2 位。所以左规就是尾数左移 2 位,阶码减 1;右规是尾数右移 2 位,阶码加 1。尾数的最高 2 位不全为 0 的数,就是规格化数。

用原码表示的尾数 S,经过规格化处理后,绝对值应该满足 2^-1^ ≤ |S| ≤ 1。它的取值范围如下:

 

  • 正数为 0. 1×...× 的形式,最大值为 0. 11...1 = 1 - 2^-n^,最小值为 0. 10...0 = 2^-1^;

  • 负数为 1. 1×...× 的形式,最大值为 1. 10...0 = -2^-1^,最小值为 1. 11...1 = - ( 1 - 2^-n^ )。

这样,浮点数规格化后的取值范围被缩小了。在数轴上表示为:

 

4. IEEE 754 标准浮点数

在现代计算机中,浮点数的格式一般采用 IEEE 制定的国际标准。IEEE 754 标准规定的浮点数形式为:

 

  • S 为数符,直接表示浮点数的正负,它与尾数所表示的有效数位是分开的。

  • 阶码 E 包含了阶符,用移码来表示,不过这里移码的偏移量不是 2 的整次幂,而是要再减去 1。 假设阶码 E 的位数为 m + 1,那么偏移量就是 2^m^ - 1。

  • 尾数 M 是原码表示的纯小数。

浮点数的位数不同,可以表示不同的数值范围和精度。IEEE 标准中常用的浮点数有三种:短浮点数(单精度)、长浮点数(双精度)和临时浮点数(延伸双精度)。

 

IEEE 标准默认的基数为 2。以短浮点数(单精度浮点数)为例,总共 32 位:最高 1 位是数符位;后面的 8 位是阶码域(指数部分),偏置值为 2^8-1^ - 1 = 127,即阶码部分的 8 位无符号数值减去 127,就得到了真正的阶码 E;最后的 23 位是尾数域(小数部分)。

  • IEEE 标准用 规格化数 表示一般的数值,这时尾数 M 的最高有效位一定为 1;于是 IEEE 标准规定这个最高有效位的 1 可以省略,并且将它隐藏放在整数位上,称为 “隐藏位”。这样就可以多出一位有效数位,从而提高了精度。例如,短浮点数的 23 位尾数,可以表示 24 位有效数字;临时浮点数不采用隐藏位的方案。

  • IEEE 标准使用 非规格化数 表示 0 附近的很小的数。这时阶码 E 所有位全部为 0;尾数 M 不为 0,且没有隐藏位,或者说隐藏位为 0。

除此之外,IEEE 标准还规定了几种特殊情况:

  • 当阶码 E 所有位全为 0,并且尾数 M 也为 0 时,表示 0 值(浮点数 0)。根据数符不同可以分别表示 ± 0。

  • 当阶码 E 所有位全为 1,并且尾数 M 为 0 时,表示 无穷大。根据数符不同可以分别表示 ±∞。

  • 当阶码 E 所有位全为 1,并且尾数 M 不为 0 时,表示这不是一个数(NaN)。

以 32 位的单精度浮点数为例,所有的机器码和对应的取值范围如下:

 


例如,对于十进制数 178.125,把它写成 IEEE 标准的短浮点数。

我们需要分整数部分和小数部分,首先转换成二进制数的表示;然后写成类似 “科学计数法” 的二进制浮点数表达。

 

这是一个正数,符号位为 0;然后从二进制浮点表达中得到阶码和尾数。将 8 位二进制阶码加上偏移量 127,尾数隐藏整数位的 1 后补成 23 位,就是最终符合 IEEE 标准的 32 位短浮点数。

 


最后,我们也可以总结一下计算机中数据的定点、浮点表示的区别:

  • 数值的表示范围不同。对于相同的字长,浮点表示法所能表示的数值范围远大于定点表示法。

  • 数值精度不同。对于相同的字长,浮点数虽然扩大了数的表示范围,但精度降低了。

  • 数据的运算不同。浮点数包括阶码和尾数两部分,运算时不仅要做尾数的运算,还要做阶码的运算;而且运算结果要做规格化,所以浮点数运算比定点数运算复杂。

  • 溢出问题。在定点数运算中,当运算结果超出数的表示范围时,发生溢出;在浮点数运算中, 当运算结果超出尾数表示范围时,不一定发生溢出;只有规格化后,阶码超出所能表示的范围时,才发生溢出。

5. C 语言中的浮点数据类型和类型转换

在 C 语言中,用 float 类型来表示 IEEE 标准中的 32 位单精度浮点数,用 double 类型来表示 64 位双精度浮点数。long double 类型对应扩展的双精度浮点数,具体的格式会随编译器和处理器架构的不同而改变。

在进行不同类型数据的混合运算时,遵循的原则是 “类型提升”,即由位数较少的类型(比如 char、short)向位数更多的类型(比如 int、long、double)转换。这时数据范围会扩大、精度也变大,因此不会溢出、也不会损失精度,实现了 “无损转换”。类型转换以 char → int → long → double 和 float → double 最为常见,从前到后范围和精度都从小到大、从低到高,转换过程没有损失。

例如,long 类型与 int 类型一起运算,需先将 int 类型转换为 long 类型,然后再进行运算,结果为 long 类型;如果 float 类型和 double 类型一起运算,需要先将 float 转换为 double 类型再进行运算,结果为 double 类型。所有这些转换都是系统自动进行的,这种转换称为 隐式类型转换。C 语言中的隐式转换有算术转换、赋值转换及输出转换。

在 C 语言程序里,数据类型除隐式转换外,也可以显式地进行转换(强制类型转换)。整型之间、浮点类型之间、整型和浮点型之间都可以进行转换。我们需要注意转换过程中数据的取值范围是否发生了变化(是否溢出)、精度是否发生了缺失。精度的缺失主要针对浮点数而言。

具体来说:

  • 从 int 或者 float 转换为 double 时,因为 double 的表示范围更大、有效位数更多,因此可以无损转换;

  • 从 double 转换为 float 时,因为 float 表示的范围更小,所以可能发生溢出;另外有效数位也减少了,所以可能发生舍入,从而丢失精度;

  • 从 float 或 double 转换为 int 时,因为 int 的表示范围更小,所以可能发生溢出;另外 int 没有小数部分,所以数据会仅保留整数部分而丢失精度;

  • 从 int 转换为 float 时,表示的范围扩大了,不会发生溢出;但 int 有 32 位,而 float 最多只有 24 位有效数位,所以可能发生数据的舍入,从而丢失精度。

2.4.2 浮点数的加/减运算

浮点数运算的特点,是阶码运算和尾数运算分开进行。浮点数加/减运算可以分为 5 步进行:

(1)对阶

对阶的目的是使两个操作数的小数点位置对齐,使两个数的阶码相等。先求阶差,然后以 “小阶向大阶看齐” 的原则,将阶码小的尾数右移一位(基数为2),阶码加1,直到两个数的阶码相等为止。

(2)尾数求和

将对阶后的尾数,按定点数加/减运算规则运算。

(3)规格化

IEEE 754 规格化尾数的形式为 ±1.×...×,所以当计算结果为非规格化数时,需要进行规格化处理。

  • 左规:当结果为 ±0.0...01x...x 时,需进行左规。尾数每左移一位,阶码减 1。可能需要左规多次,直到将第一位 1移到小数点左边。

  • 右规:当结果为 ±1x.x...x 时,出现了尾数的溢出,需进行右规。尾数右移一位,阶码加 1。当尾数右移时,最高位 1 被移到小数点前一位作为隐藏位;当最后一位移出时,要考虑舍入。

左规一次相当于乘以2,右规一次相当于除以2;需要右规时,只需进行一次。

(4)舍入

在对阶和尾数右规时,尾数右移可能会将低位丢失,影响精度,IEEE 754有以下4种舍入方式:

  • 就近舍入:舍入为最近的那个数,类似于 “四舍五入”,一般被叫做 “ 0 舍 1 入” 法;如果被舍入的值恰好是 100...0 形式,选择舍入为最近的偶数;

  • 正向舍入:向 +∞ 方向舍入,即取右边那个数,也叫 “向上舍入”;

  • 负向舍入:向 -∞ 方向舍入,即取左边那个数,也叫 “向下舍入”;

  • 截断:朝 0 方向舍入,即取绝对值较小的那个数。

(5)溢出判断

浮点数的溢出,并不是以尾数溢岀来判断的;尾数溢出可以通过右规操作得到纠正。运算结果是否溢出,主要看结果的指数是否发生了溢出,因此是由阶码来判断的。

  • 若一个正阶码超出了最大允许值(127 或 1023),则发生上溢,产生异常;

  • 若一个负阶码超出了最小允许值(-149 或 -1074),则发生下溢,通常把结果按机器零处理。


例如,两个数 x = 29/32 × 2^7^,y = 5/8 × 2^5^,用浮点加法计算 x + y。假设浮点数的阶码和尾数均用补码表示,且阶码为 5 位(含 2 位阶符),尾数为 7 位(含 2 位数符)。

首先,将浮点数写成下面的规格化二进制形式:

x = 0.11101 × 2^111^,y = 0.101 × 2^101^

具体计算过程如下:

(1)对阶

阶码相减 00, 111 - 00, 101 = 00, 010,说明 x 的阶码比 y 的大 2,需要将 y 的尾数右移两位,阶码加 2:

y = 0.00101 × 2^111^

(2)尾数求和

尾数相加 00.11101 + 00.00101 = 01.00010

(3)规格化

运算结果的尾数出现溢出,需要进行右规:尾数右移一位,阶码加 1:

1.00010 × 2^111^ = 0.100010 × 2^1000^,即计算结果为 01, 000; 00, 10001

(4)舍入

结果的尾数用补码表示为:00 10001,不需要舍入。

(5)溢出判断

结果的阶码用补码表示为:01 000,由于阶符为 01,说明结果溢出。


2.5 数据的存储和排列

2.5.1 数据按“边界对齐”方式存储

可以假设字长为 32 位,可按字节、半字、字寻址。在对准边界的 32 位计算机中,半字地址是 2 的整数 倍,字地址是 4 的整数倍,当所存数据不满足此要求时,可填充一个或多个空白字节。这种存储方式称为 “边界对齐” 。这样无论所存的数据是字节、半字还是字,均可一次访存取出。虽然浪费了一些存储空间,但可提高存取速度。

数据不按边界对齐方式存储时,半字长或字长的数据可能在两个存储字中,此时需要两次访存,并对高低字节的位置进行调整后才能取得所需数据,从而影响系统的效率。

 

 

在 C 语言的 struct 类型中,边界对齐方式存储有两个重要要求:

(1)每个成员按其类型的方式对齐,比如 char 类型的对齐值为 1,short 为 2,int 为 4(单位为字节)。

(2)struct 的长度必须是成员中最大对齐值的整数倍(不够就补空字节),以便在处理 struct 数组时保证每项都满足边界对齐的条件。

例如,下面是两个成员完全一样的结构体:

<span style="background-color:#f8f8f8"><span style="color:#333333">struct A {
	int i;
	char c;
	short s;
}
struct B {
	char c;
	int i;
	short s;
}</span></span>

但两者在内存中占据的空间却不同。这是因为结构体成员是按定义的先后顺序排列的,编译器要使它们在空间上对齐,所以应该有:

每个成员存储的起始地址 % 该成员的长度 = 0

同时,还需要让结构体的长度是最大成员长度的整数倍。

 

按边界对齐方式,相对于不按边界对齐方式是一种空间换时间的思想。

2.5.2 数据的“大端方式”和“小端方式”存储

在存储数据时,通常用 最低有效字节(LSB)最高有效字节(MSB)来分别表示数据的低位和高位。例如,在 32 位机器中定义了一个 int 类型的变量 i,机器数为 18 0B C5 F3H,那么它的 MSB = 18H,LSB = F3H。

现代计算机基本都采用字节编址,也就是每个地址编号对应存放 1 个字节。不同类型的数据占用的字节数不 同,而程序中对每个数据只给定一个地址。变量 i 占据连续的四个字节,它们各有一个内存地址,而变量 i 的地址就是开始的那个字节的地址。假设 i 的地址为 6C 00H,那么 i 具体存放的四个字节的地址就是:6C 00H、6C 01H、6C 02H、6C 03H。而具体每个字节存放什么内容,可以有不同的定义方式。

多字节数据都存放在连续的字节序列中,根据数据中各字节在连续字节序列中的排列顺序不同,分为两种排列方式:大端方式(big endian)和 小端方式(little endian)。

 

  • 大端方式:先存储高位字节,后存储低位字节。高位字节存储在低位地址中,字中的字节顺序和原序列相同。

  • 小端方式:先存储低位字节,后存储高位字节。低位字节存储在低位地址中,字中的字节顺序和原序列相反。

在阅读以小端方式存储的机器代码时,要注意字节是按相反顺序显示的。

2.6 章节练习

一、单项选择题

1. 【2018真题】冯·诺伊曼结构计算机中的数据采用二进制编码表示,其主要原因是( )。

I. 二进制的运算规则简单 II. 制造两个稳态的物理器件较容易

III. 便于用逻辑门电路实现算术运算

A. 仅 I、II B. 仅 I、III C. 仅 II、III D. I、II 和 III

答案: D

  1. 【 2015真题】由 3个“1”和 5个“0”组成的8位二进制补码,能表示的最小整数是 ( )。

A. -126 B. -125 C. -32 D. -3

答案:B

  1. 【2022真题】32 位补码所能表示的整数范围是 ( )。

A. -232~231-1 B. -231~231-1 C. -232~232-1 D. -231~232-1

答案:B

  1. 【2021真题】已知带符号整数用补码表示,变量 x、y、z 的机器数分别为 FFFDH、FFDFH、7FFCH,下列结论中,正确的是 ( )。

A. 若x、y和z为无符号整数,则z<x<y B. 若x、y和z为无符号整数,则x<y<z

C. 若x、y和z为带符号整数,则x<y<z D. 若x、y和z为带符号整数,则y<x<z

答案:D

5. 【2016真题】有如下 C 语言程序段

<span style="background-color:#f8f8f8"><span style="color:#333333">short si = -32767;
unsigned short usi = si;</span></span>

执行上述两条语句后, usi 的值为 ( )。

A. -32767 B. 32767 C. 32768 D. 32769

答案:D

6. 【2019真题】考虑以下 C 语言代码:

<span style="background-color:#f8f8f8"><span style="color:#333333">unsigned short usi = 65535;
short si = usi;</span></span>

执行上述程序段后, si 的值是 ( )。

A.-1 B.-32767 C.-32768 D.-65535

答案:A

7. 【2012真题】假定编译器规定 int 和 short 型长度分别为 32 位和 16 位,执行下列 C 语言语句:

<span style="background-color:#f8f8f8"><span style="color:#333333">unsigned short x=65530;
unsigned int y=x;</span></span>

得到 y 的机器数为 ( )。

A. 0000 7FFAH B. 0000 FFFAH C. FFFF 7FFAH D. FFFF FFFAH

答案:B

  1. 【2009真题】一个 C 语言程序在一台 32 位机器上运行。程序中定义了三个变量 x、 y 和 z,其中 x 和 z 为 int 型, y 为 short 型。当 x=127, y=-9 时,执行赋值语句 z=x+y 后, x、 y 和 z 的值分别是 ( )。

A. x=0000007FH, y=FFF9H, z=00000076H B. x=0000007FH, y=FFF9H, z=FFFF0076H ​ C. x=0000007FH, y=FFF7H, z=FFFF0076H D. x=0000007FH, y=FFF7H, z=00000076H

答案:D

9. 【2018真题】整数x的机器数为1101 1000,分别对x进行逻辑右移1位和算术右移1位操作,得到的机器数各 是 ( )。

A.1110 1100、1110 1100 B. 0110 1100、1110 1100

C.1110 1100、0110 1100 D. 0110 1100、 01101100

答案:B

  1. 【2013真题】某字长为 8 位的计算机中,已知整型变量 x、y的机器数分别为$[x]补=1 {\quad} 1110100,[y]补=1 {\quad}0110000$。若整型变量 $z=2*x+y/2$,则 z 的机器数为 ( )。

A. 1 1000000 B. 0 0100100 C. 1 0101010 D. 溢出

答案:A

  1. 【2018真题】假定带符号整数采用补码表示,若 int 型变量 x 和 y 的机器数分别是 FFFF FFDFH 和 0000 0041H,则 x、y 的值以及 x-y 的机器数分别是 ( )。

A.x=-65,y=41,x-y 的机器数溢出 B.x=-33,y=65,x-y 的机器数为FFFF FF9DH

C.x=-33,y=65,x-y 的机器数为FFFF FF9EH D. x = -65,y = 41,x-y 的机器数为FFFF FF96H

答案:C

  1. 【2016真题】某计算机字长为 32 位,按字节编址,采用小端(Little Endian)方式存放数据。假定有一个 double 型变量,其机器数表示为 1122 3344 5566 7788H,存放在 0000 8040H 开始的连续存储单元中,则存储单元 0000 8046H 中存放的是 ( )。

A. 22H B. 33H C. 66H D. 77H

答案:A

  1. 【2018真题】某32位计算机按字节编址,采用小端(Little Endian)方式。若语句 “inti=0;” 对应指令的机器代码为 “C7 45 FC 00 00 00 00”,则语句 “int i = -64;” 对应指令的机器代码是 ( )。

A.C7 45 FC C0 FF FF FF B.C7 45 FC 0C FF FF FF C.C7 45 FC FF FF FF C0 D.C7 45 FC FF FF FF 0C

答案:A

  1. 【2012真题】某计算机存储器按字节编址,采用小端方式存放数据。假定编译器规定 int 型和 short 型长度分别为 32 位和 16 位,并且数据按边界对齐存储。某 C 语言程序段如下:

<span style="background-color:#f8f8f8"><span style="color:#333333">struct{
	int a;
	char b;
	short c;
} record;
record.a=273;</span></span>

若 record 变量的首地址为 0xC008,则地址 0xC008 中内容及 record.c 的地址分别为 ( )。

A. 0x00、 0xC00D B. 0x00、 0xC00E C. 0x11、 0xC00D D. 0x11、 0xC00E

答案:D

  1. 【2020真题】在按字节编址,采用小端方式的 32 位计算机中,按边界对齐方式为以下 C语言结构型变量a分配存储空间。

<span style="background-color:#f8f8f8"><span style="color:#333333">Struct record{
	short   x1;
	int   x2;
} a;</span></span>

若a的首地址为 2020 FE00H,a的成员变量x2的机器数为1234 0000H,则其中34H所在存储单元的地址是 ( )。

A. 2020 FE03H B. 2020 FE04H C.2020 FE05H D. 2020 FE06H

答案:D

  1. 【2012真题】 float 类型(即 IEEE754 单精度浮点数格式)能表示的最大正整数是 ( )。

A. 2126-2103 B. 2127-2104 C. 2127-2103 D. 2128-2104

答案:D

  1. 【2013真题】某数采用 IEEE 754 单精度浮点数格式表示为C640 0000H,则该数的值是 ( )。

A. $-1.5×2^{13}$ B. $-1.5 × 2^{12}$ C. $-0.5×2^{13}$ D. $-0.5×2^{12}$

答案:A

  1. 【2014真题】float 型数据常用 IEEE 754 单精度浮点格式表示。 假设两个 float 型变量 x 和 y 分别存放在32位寄存器 f1 和 f中,若(f1)=CC90 0000H, (f2)= B0C0 0000H, 则 x 和 y 之间的关系为 ( )。

A. x<y且符号相同 B. x<y且符号不同 C. x>y且符号相同 D. x>y且符号不同

答案:A

  1. 【2015真题】下列有关浮点数加减运算的叙述中,正确的是 ( )。 Ⅰ .对阶操作不会引起阶码上溢或下溢 Ⅱ .右规和尾数舍入都可能引起阶码上溢 Ⅲ.左规时可能引起阶码下溢 Ⅳ.尾数溢出时结果不一定溢出

A.仅Ⅱ 、 Ⅲ B. 仅Ⅰ 、 Ⅱ 、 Ⅳ C.仅Ⅰ 、 Ⅲ、 Ⅳ D. Ⅰ 、 Ⅱ 、 Ⅲ、 Ⅳ

答案: D

  1. 【2018真题】IEEE 754 单精度浮点格式表示的数中,最小的规格化正数是 ( )。

A.1.0x2-126 B. 1.0x2-127 C.1.0x2-128 D.1.0x2-149

答案:A

  1. 【2020真题】已知带符号整数用补码表示,float 型数据用 IEEE 754 标准表示,假定变量 x 的类型只 可能是 int 或 float,当 x 的机器数为 C800 0000H时,x 的值可能是 ( )。

A.-7x227 B.-216 C. 217 D. 25x227

答案:A

  1. 【2021真题】下列数值中,不能用 IEEE 754 浮点格式精确表示的是 ( )。

A.1.2 B.1.25 C.2.0 D.2.5

答案:A

  1. 【2022真题】-0.4375 的 IEEE 754 单精度浮点数表示为( )。

A. BEE0 0000H B. BF60 0000H C. BF70 0000H D. C0E0 0000H

答案:A

二、综合应用题
  1. 【2020真题】有实现 x*y 的两个 C 语言函数如下:

<span style="background-color:#f8f8f8"><span style="color:#333333">unsigned umul (unsigned x, unsigned y){return x*y; }
int imul (int x,int y) {return x* y;}</span></span>

假定某计算机 M 中 ALU 只能进行加运算和逻辑运算。请回答下列问题。

(1)若 M 的指令系统中没有乘法指令,但有加法、减法和位移等指令,则在 M 上也能实现上述两个函数中的乘法运算,为什么?

(2)若 M 的指令系统中有乘法指令,则基于 ALU、位移器、寄存器以及相应控制逻辑实现乘法指令时,控制逻辑的作用是什么?

(3)针对以下三种情况:a)没有乘法指令;b)有使用ALU和位移器实现的乘法指令;c)有使用阵列乘法器实现的乘法指令,函数 umul() 在哪种情况下执行时间最长?哪种情况下执行的时间最短?说明理由。

(4)n 位整数乘法指令可保存 2n 位乘积,当仅取低 n 位作为乘积时,其结果可能会发生溢出。当 n=32、x=231-1、y=2 时,带符号整数乘法指令和无符号整数乘法指令得到的 x*y 的 2n 位乘积分别是什么(用十六进制表示)?此时函数 umul() 和 imul() 的返回结果是否溢出?对于无符号整数乘法运算,当仅取乘积的低 n 位作为乘法结果时,如何用 2n 位乘积进行溢出判断?

答案:

(1)编译器可以将乘法运算转换为一个循环代码段,在循环代码段中通过比较、加法、移位等指令实现乘法运算。

(2)控制逻辑的作用为: 控制循环次数,控制加法和移位操作。

(3)a)最长, c)最短。

对于 a), 需要用循环代码段(软件)实现乘法操作,因而需反复执行很多条指令, 而每条指令都需要取指令、译码、取数、执行并保存结果,所以执行时间很长; 对于 b)和 c), 都只要用一条乘法指令实现乘法操作,不过, b)中的乘法指令需要多个时钟周期才能完成,而 c)中的乘法指令可以在一个时钟周期内完成, 所以 c)执行时间最短。

(4)当 n=32、 x=231-1、 y=2 时,带符号整数和无符号整数乘法指令得到的 64 位乘积都为 0000 0000 FFFF FFFEH。

函数 imul 的结果溢出,而函数 umul 结果不溢出。对于无符号整数乘法,若乘积高 n 位全为 0, 则不溢出,否则溢出。

  1. 【2017真题】 已知

计算 f(n) 的 C 语言函数 f1 如下:

<span style="background-color:#f8f8f8"><span style="color:#333333">int f1(unsigned n){
	int sum=1, power=1;
	for(unsigned i=0;i<=n-1;i++){
		power *= 2;
		sum += power;
	}
	return sum;
}</span></span>

将 f1 中的 int 都改为 float, 可得到计算 f(n) 的另一个函数 f2。假设 unsigned 和 int 型数据都占 32 位, float 采用 IEEE 754 单精度标准。请回答下列问题。

(1)当 n=0 时, f1 会出现死循环,为什么?若将 f1 中的变量 i 和 n 都定义为 int 型,则 f1 是否还会出现死循环?为什么?

(2)f1(23) 和 f2(23) 的返回值是否相等?机器数各是什么(用十六进制表示)?

(3)f1(24)和 f2(24)的返回值分别为 33 554 431 和 33 554 432.0, 为什么不相等?

(4)f(31)=2^32^-1, 而 f1(31) 的返回值却为 -1,为什么?若使 f1(n) 的返回值与 f(n) 相等,则最大的 n 是多少?

(5)f2(127) 的机器数为 7F80 0000H, 对应的值是什么?若使 f2(n) 的结果不溢出,则最大的 n 是多少?若使 f2(n) 的结果精确(无舍入),则最大的 n 是多少?

答案:

(1)由于 i 和 n 是 unsigned 型,故“i<=n-1”是无符号数比较; n=0 时, n-1 的机器数为全1,值是 232-1,为 unsigned 型可表示的最大数,条件“i<=n-1”永真,因此出现死循环。

若 i 和 n 改为 int 类型,则不会出现死循环。因为“i<=n-1”是带符号整数比较, n=0 时, n-1 的值是-1,当 i=0 时条件“i<=n-1”不成立,此时退出 for 循环。

(2)f1(23)与 f2(23)的返回值相等。

f(23) = 223+1-1 = 224-1,它的二进制形式是 24 个1。 int 占 32 位,没有溢出。 float 有 1 个符号位, 8 个指数(阶码)位, 23 个底数(尾数)位, 23 个底数位可以表示 24 位的底数。所以两者返回值相等。

f1(23)的机器数是 00FF FFFFH;f2(23)的机器数是 4B7F FFFFH。显而易见前者是 24 个 1,即 0000 0000 1111 1111 1111 1111 1111 1111(2),后者符号位是 0,指数位为 23+127(10) = 1001 0110(2),底数位是 111 1111 1111 1111 1111 1111(2)

(3)当 n=24 时, f(24) = 1 1111 1111 1111 1111 1111 1111 B,而 float 型数只有 24 位有效位,舍入后数值增大,所以 f2(24) 比 f1(24) 大 1。

(4)f(31) 已超出了 int 型数据的表示范围,用 f1(31) 实现时得到的机器数为 32 个 1,作为 int 型数解释时其值为-1,所以 f1(31) 的返回值为-1。

因为 int 型最大可表示数是 0 后面加 31 个 1,故使 f1(n)的返回值与 f(n)相等的最大 n 值是 30。

(5) f2 返回值为 float 型,7F80 0000H 中数符为0,阶码全为1,尾数为0。IEEE 754 标准用“阶码全 1、尾数全 0”表示无穷大。所以机器数 7F80 0000H 对应的值是+∞。

当 n=126 时, f(126) = 2127-1 = 1.1…1×2126,对应阶码为 127+126=253,尾数部分舍入后阶码加 1,最终阶码为 254,是 IEEE 754 单精度格式表示的最大阶码。故使 f2 结果不溢出的最大 n 值为 126。

当 n=23 时, f(23) 为 24 位 1, float 型数有 24 位有效位,所以不需舍入,结果精确。所以使 f2 获得精确结果的最大 n 值为 23。

第三章 存储器

3.1 本章大纲要求与核心考点

3.1.1 大纲内容

(一)存储器的分类

(二)层次化存储器的基本结构

(三)半导体随机存储器

  1. SRAM 存储器

  2. DRAM 存储器

  3. Flash 存储器

(四)主存储器

  1. DRAM 芯片和内存条

  2. 多模块存储器

  3. 主存和CPU之间的连接

(五)外部存储器

  1. 磁盘存储器

  2. 固态硬盘(SSD)

(六)高速缓冲存储器(Cache)

  1. Cache 的基本工作原理

  2. Cache 和主存之间的映射方式

  3. Cache 中主存块的替换算法

  4. Cache 写策略

(七)虚拟存储器

  1. 虚拟存储器的基本概念

  2. 页式虚拟存储器

    基本原理,页表,地址转换,TLB(快表)

  3. 段式虚拟存储器

  4. 段页式虚拟存储器

3.1.2 核心考点

本章内容是考研考察的一个重点和难点,往往会有综合应用题出现。

需要重点掌握的内容包括:

  • 半导体存储芯片的特性、工作原理、扩展技术及与 CPU 的连接,多模块存储器的原理。

  • 磁盘存储器的原理、特点、性能指标,RAID 的原理,固态硬盘的特点和原理。

  • 程序访问的局部性原理,Cache 的工作原理及性能计算,Cache 和主存的三种映射方式的原理、 特点、地址结构、访存过程,Cache 替换算法(常考 LRU) , Cache 写策略,Cache 块中的标记项。

  • 虚拟存储器的基本原理,页表机制(二级页表结合操作系统考查),快表的原理,具有快表和 Cache 的多级页式存储系统的工作原理(综合性较强),段式和段页式虚拟存储器的基本原理。

3.1.3 真题分布
考点考查次数
单项选择题综合应用题
高速缓冲存储器(Cache)129
虚拟存储器59
半导体存储器61
主存的扩展及与 CPU 的连接61
磁盘存储器40
低位交叉存储器21


3.2 存储器概述

3.2.1 存储器的分类

存储器是计算机系统中的记忆设备,用来存放程序和数据。存储器的种类繁多,从不同的角度对存储器可作不同的分类。

 

1. 按存储介质分类

存储介质是指能寄存“0”、“1”两种代码并能区别两种状态的物质或元器件。存储介质主要有半导体器件、磁性材料和光盘等。

  • 半导体存储器

存储元件由半导体器件组成的存储器称为半导体存储器。现代半导体存储器都用超大规模集成电路工艺制成芯片,其优点是体积小、功耗低、存取时间短。

半导体存储器又可按其材料的不同, 分为双极型(TTL)半导体存储器和 MOS 半导体存储器两种。前者具有高速的特点;后者具有高集成度的特点,并且制造简单,成本低廉,功耗小,所以 MOS 半导体存储器被广泛用。

  • 磁性材料存储器

磁性材料存储器主要依靠磁性材料作为记录的介质,是不易失的永久记忆存储器。又可以分为磁表面存储器和磁芯存储器。

磁表面存储器是在金属或塑料基体的表面上涂一层磁性材料作为记录介质,工作时磁层随载磁体高速运转,用磁头在磁层上进行读/写操作,故称为磁表面存储器。按载磁体形状的不同,可分为磁盘、磁带和磁鼓。

磁芯是由硬磁材料做成的环状元件,在磁芯中穿有驱动线(通电流)和读出线,这样便可进行读/写操作,这种存储器称为磁芯存储器。磁芯属磁性材料,故它也。不过,磁芯存储器的体积过大、工艺复杂、功耗太大,目前几乎已不被采用。

  • 光盘存储器

光盘存储器是应用激光在记录介质(磁光材料)上进行读/写的存储器,具有非易失性的特点。光盘具有记录密度高、耐用性好、可靠性高和可互换性强等特点。

2. 按存取方式分类

按存取方式可把存储器分为随机存储器、只读存储器、顺序存取存储器和直接存取存储器。

  • 随机存储器(Random Access Memory, RAM

RAM是一种可读/写存储器, 其特点是存储器的任何一个存储单元的内容都可以随机存取,而且存取时间与存储单元的物理位置无关。计算机系统中的主存都采用这种随机存储器。

由于存储信息原理的不同, RAM 又分为静态 RAM(以触发器原理寄存信息)和动态 RAM(以电容充放电原理寄存信息)。

  • 只读存储器(Read Only Memory, ROM

只读存储器是能对其存储的内容读出,而不能对其重新写人的存储器。这种存储器一旦存入了原始信息后,在程序执行过程中,只能将内部信息读出,而不能随意重新写人新的信息去改变原始信息。

所以 ROM 通常用来存放固定不变的程序、常数和汉字字库,甚至用于操作系统的固化。它与随机存储器可共同作为主存的一部分,统一构成主存的地址域。

  • 串行访问存储器

如果对存储单元进行读/写操作时,需按其物理位置的先后顺序寻找地址,则这种存储器称为串行访问存储器,也称为顺序存取存储器。显然这种存储器由于信息所在位置不同,使得读/写时间均不相同,比如磁带存储器就是一种顺序存取存储器。

  • 直接存取存储器

还有一种属于部分串行访问的存储器,比如磁盘。在对磁盘读/写时,首先直接指出该存储器中的某个小区域(磁道),然后再顺序寻访,直至找到位置。故其前段是直接访问,后段是串行访问,称为直接存取存储器。

3. 按在计算机中的作用分类

按在计算机系统中的作用不同,存储器可以分为主存储器、辅助存储器、缓冲存储器。

  • 主存储器(简称主存):用来存放程序和数据,可以和CPU直接交换信息。

  • 辅助存储器(简称辅存):主存储器的后援存储器, 用来存放当前暂时不用的程序和数据, 它不能与CPU直接交换信息。两者相比,主存速度快、容量小、每位价格高;辅存速度慢、容量大、每位价格低。

  • 缓冲存储器(简称缓存Cache) 用在两个速度不同的部件之中。

 

3.2.2 存储器的性能指标

存储器有 3 个主要性能指标:速度、容量和每位价格(简称位价)。

1.存储速度

(1)存取时间

要想衡量存储速度,最直观的指标就是完成一次存储器读/写操作所需要的时间,这叫做 存取时间,又称为 访问时间(Memory Access Time)。

存取时间又分为读出时间和写入时间。读出时间是从存储器接受到有效地址开始,到产生有效输出所需的全部时间;写入时间是从存储器接受到有效地址开始,到数据写入被选中存储单元为止的全部时间。

(2)存储器周期

存储器周期 (Memory Cycle Time)指连续进行两次独立的存储器操作(读或者写)需要的最小时间间隔,也叫 存取周期。需要注意的是,存储器周期并不等同于完成一次读写操作的时间,而是要更大;因为存储器经过一次读写操作后,并不能立即进行下一次读写,中间还需要一段时间来恢复内部状态。所以,

存储器周期 = 存取时间 + 恢复时间

(3)存储器带宽

一般来说,存储器周期越短,存储器的速度就越快;这前提是存储器的每次读写操作处理的数据位数相同。通常情况下,存储器每次读写的位数是跟存储字长相关的,字长越长,一个存取周期处理的数据就越多。

所以可以用 数据传输率 来表示存储速度,而 存储器带宽 就是衡量数据传输率重要指标。存储器带宽指单位时间内存储器存取的数据量。单位为位/秒(b/s),或者字节/秒(B/s)、字/秒。

存储器带宽 = 数据宽度 / 存储周期

例如,存储器周期为 500 ns,每个存取周期可以访问 16 位,那么带宽就是:

16 bit ÷ 500 ns = 32 Mb/s

2. 存储容量

存储容量指存储器能存放的数据总量,一般用二进制代码的总位数(bit)来表示。

存储容量 = 存储字数 × 存储字长

存储字数代表了存储器地址空间的大小,由地址线的位数决定。容量一般也可以用字节总数(Byte)来表示,也就是:

存储容量(字节数) = 存储字数 × 存储字长 / 8

例如,某机器存储字长为 8 位,地址线有 28 位,那么它的主存最大存储容量为:

2^28^ × 8 / 8 = 2^28^ B = 256 MB

3. 位价

每位价格也就是存储器的单位成本。

位价 = 总成本 / 总容量

一般来说,速度越高,位价就越高;容量越大,位价就越低;而且容量越大,速度也会越低。

3.2.3 层次化存储器的基本结构

最理想的存储器应该同时满足大容量、高速度、低位价,可惜这是很难达到的。

 

上面列出了不同层级的存储器。由上至下, 位价越来越低,速度越来越慢,容量越来越大,CPU 访问的频度也越来越少。

  • 寄存器通常都制作在 CPU 芯片内。寄存器中的数直接在 CPU 内部参与运算, CPU 内可以有十几个、几十个寄存器,它们的速度最快,位价最高,容量最小。

  • 主存用来存放将要参与运行的程序和数据,它与 CPU 速度差距较大。

  • 为了使主存和 CPU 之间速度更好地匹配, 需要在主存与 CPU 之间插入一种比主存速度更快、容量更小的高速缓冲存储器 Cache,其位价要高于主存。

以上三类存储器都是由速度不同、位价不等的半导体存储材料制成的,它们都设在主机内。现代计算机将 Cache 也制作在 CPU 内。

磁盘、磁带属于辅存,其容量比主存大得多,大都用来存放暂时未用到的程序和数据文件。CPU不能直接访问辅存, 辅存只能与主存交换信息, 因此辅存的速度可以比主存慢得多。

存储系统的层次结构主要体现在缓存-主存和主存-辅存这两个存储层次上。显然, CPU 和缓存、主存都能直接交换信息; 缓存能直接和 CPU、主存交换信息; 而主存可以和 CPU、缓存、辅存交换信息。

 

  • 缓存-主存层次

这一层次主要解决 CPU 和主存速度不匹配的问题。由于缓存的速度比主存的速度高, 只要将CPU近期要用的信息调人缓存, CPU 便可以直接从缓存中获取信息, 从而提高访存速度。但由于缓存的容量小,因此需不断地将主存的内容调入缓存,使缓存中原来的信息被替换掉。主存和缓存之间的数据调动是由硬件自动完成的,对程序员是透明的。

  • 主存-辅存层次

这一层次主要解决存储系统的容量问题。辅存的速度比主存的速度低,而且不能和 CPU 直接交换信息, 但它的容量比主存大得多, 可以存放大量暂时未用到的信息。当CPU需要用到这些信息时, 再将辅存的内容调人主存, 供CPU直接访问。主存和辅存之间的数据调动是由硬件和操作系统共同完成的。

在主存-辅存这一层次的不断发展中,逐渐形成了虚拟存储系统。主存和辅存共同构成了虚拟存储器,二者在硬件和系统软件的共同管理下工作。

从CPU角度来看, 缓存-主存这一层次的速度接近于缓存, 高于主存; 其容量和位价却接近于主存。主存-辅存这一层次,从整体分析,其速度接近于主存,容量接近于辅存,平均位价也接近于低速、廉价的辅存位价。这就解决了速度、容量、成本这三者的矛盾。现代的计算机系统几乎都具有这两个存储层次,构成了缓存、主存、辅存三级存储系统。

3.3 半导体存储器

半导体存储器分为 随机存取存储器(RAM)只读存储器(ROM)

RAM 是一种可读/写存储器,其特点是存储器的任何一个存储单元的内容都可以随机存取,而且存取时间与存储单元的物理位置无关。计算机系统中的主存都采用这种随机存储器。

RAM 按照存储信息的原理不同,又可以分为静态随机存取存储器(SRAM)和动态随机存取存储器(DRAM),主存储器主要由 DRAM 实现,靠近处理器的那一层缓存 (Cache)则由 SRAM 实现,它们都是易失性存储器。ROM 是非易失性存储器。

   

采用超大规模集成电路制造工艺,可以将半导体存储器集成在一个芯片上,一个芯片内主要包括了具有记忆功能的存储矩阵、译码驱动电路和读/写电路等。

 

主存中各个存储单元的空间位置,是由一个地址号来表示的;通过地址总线可以给定一个存储单元的地址号,从而根据地址读出或者写入一个存储字。

  • 译码驱动:将地址总线送来的地址信号翻译成对应存储单元的选择信号,该信号在读/写电路的配合下完成对被选中单元的读/写操作。

  • 读/写电路:包括读出放大器和写入电路,用来完成读/写操作。

存储芯片通过地址总线、数据总线和控制总线与外部连接。地址线和数据共同反映了芯片的存储容量。比如,10 根地址线,4 根数据线,表示芯片的存储容量为:2^10^ × 4 = 4 Kb。

  • 地址线是单向输入的,其位数与芯片存储容量有关。

  • 数据线是双向输入的,其位数与芯片每次可读出或写入的数据位数有关,从而也影响到存储容量。

  • 控制线包括了读/写控制线和片选线。读/写控制线决定芯片进行的具体操作,片选线用来选择芯片。不同的存储芯片的控制线可能是不同的,有的芯片读/写控制线是两根(引脚名称一般是 $\overline{OE}$ 和 $\overline{WE}$ ),有的芯片则读/写共用一根(引脚 $\overline{WE}$);片选线一般是一根(引脚 $\overline{CE}$ 或 $\overline{CS}$),也有可能是两根。

半导体存储芯片的译码驱动,主要有两种方式:线选法重合法

  • 线选法:是用一根字选择线(字线),直接选中一个存储单元的各位。这种方式结构比较简单,不过只适合用于容量不大的存储芯片。

  • 重合法:用两个方向的地址,共同决定选中存储矩阵中的一个存储单元。相比 “一维” 的线选法,重合法就升级到了 “二维”,可以用更少的选择线实现对所有存储单元的选择。

 

上面是一个采用线选法译码驱动的存储芯片结构示意图。这个芯片有 8 位地址线和 8 位数据线,所以有 2^8^ = 256 个存储字,需要 256 根字线来实现选中每个存储字。

如果采用重合法,则可以使用 X、Y 两个方向的地址译码器分别对 4 位地址进行译码,只需要两个方向各 16 根选择线,就可以直接选中 16 × 16 存储矩阵中的每一位。

 

当然,在这个示例中,如果用重合法实现 256 个字节(256 × 8)的存储器,需要使用 8 片上面结构的芯片,这样一来总的选择线并没有更少。不过如果考虑更大容量的存储器,比如地址线有 32 位、数据线仍为 8 位时,则线选法需要 2^32^ 根字线;而重合法同样是需要 8 片芯片,每片芯片只需 2^16^ × 2 = 2^17^ 根选择线就可以实现。

3.3.1 SRAM

通常把存放一个二进制位的物理器件称为存储元,它是存储器最基本的构件。地址码相同的多个存储元构成一个存储单元。存储单元的集合构成存储体。

静态 RAM(Static RAM,SRAM)的存储元是用双稳态触发器(六晶体管MOS)来记忆信息的,因此信息被读出后,它仍保持其原状态而不需要刷新;这种读特性被称为“非破坏性读出”。

 

上图中,T~1~ ~ T~4~ 构成了 MOS 管双稳态触发器基本电路;而 T~5~、T~6~ 受行地址选择信号控制,T~7~、T~8~ 受列地址选择信号控制,它们就像开关,将位线 A’ 、A 与数据线连接起来。由 T~1~ ~ T~6~ 这 6 个 MOS 管构成了静态 RAM 的基本单元电路,T~7~、T~8~ 则不包含在内,它们是芯片内同一列的各个基本单元电路所共有的。

下面是 Intel 2114 RAM 芯片的存储矩阵结构示意图。2114 芯片有 10 根地址线,其中 6 根行地址线、4 根列地址线,存储矩阵由 64 × 64 个基本单元电路组成,总容量为 1K × 4 位。

 

SRAM 使用触发器工作原理存储信息,因此在读出信息后,它仍会保持原来的状态,不需要刷新。不过如果电源掉电,存储的信息就会丢失,所以它属于易失性半导体存储器。

SRAM 的存取速度快,但集成度低,功耗较大,价格昂贵,一般用于 Cache。

3.3.2 DRAM

动态 RAM(Dynamic RAM,DRAM)是利用存储元电路中栅极电容上的电荷来存储信息的。若电容上存有足够多的电荷表示存 “1”,电容上无电荷则表示存 “0”。

常见的动态 RAM 基本单元电路有三管式和单管式两种。单管式只需要一个 MOS 管和一个电容,因此可以极大地提高集成度。

 

  • 当读取数据时,字线上的高电平使 MOS 管 T 导通,如果电容 C~s~ 有电荷就会在数据线上产生电流,可以看作 “1”;反之如果没有电荷,数据线上就没有电流,看作 “0”。进行读操作之后,电容上的电荷就释放掉了,所以必须进行 “再生” 处理;这种读取方式为 破坏性读出

  • 对于写入操作,同样是字线高电平令 T 导通,如果数据线上为高电平则对电容充电,存入 “1”;如果为低电平则电容放电,存 “0”。

可以看到,DRAM 的基本存储元可以只使用一个晶体管, 所以它比 SRAM 的密度要高很多。为了进一步提高集成度,DRAM 采用 地址复用技术,地址信号分行、列两次传送,这样地址线是原来的一半,地址引脚数也可以减少一半,就能够进一步减小芯片的体积。

下面是 Intel 4116 RAM 芯片的整体结构和存储矩阵示意图。4116 芯片的存储矩阵为 128 × 128,共有 16 K 个单管 MOS 基本单元电路,容量为 16K × 1 位。本来芯片应该有 14 根地址线,不过为了减少芯片封装的引脚数,地址线只有 7 根。这就需要将完整的地址信息分成行地址、列地址两部分(各自 7 位),分两次传送。

 

 

相对 SRAM 来说,DRAM 具有容易集成、价位低、容量大和功耗低等优点,但 DRAM 的存取速度比 SRAM慢,一般用于大容量的主存系统。

DRAM 的刷新

由于电容上的电荷一般只能维持1 ~2ms,因此即使电源不掉电,信息也会自动消失。为此,必须在 2ms 内对所有存储单元恢复一次原状态,这个过程称为 再生 或者 刷新

刷新的过程,实质上是先将原存信息读出,再由刷新放大器形成原信息并重新写入的再生过程。由于存储单元是被随机访问的,有些存储单元可能一直不会被访问,因此其存储的原信息将会慢慢消失。因此,必须进行定时刷新。一般要求在一定的时间内,对动态 RAM 的全部基本单元必须作一次刷新,这个时间称为 刷新周期,也叫 再生周期,一般取 2ms。

通常有三种刷新方式:集中刷新、分散刷新和异步刷新。

(1)集中刷新

在规定的一个刷新周期内,对全部存储单元集中一段时间进行逐行刷新;刷新时必须停止读/写操作。

例如,我们有一个芯片的存储矩阵为 128 × 128,它的存取周期为 0.5 μs,刷新周期为 2 ms(4000 个存取周期),那么对它的 128 行存储单元进行集中刷新需要:

0.5 μs × 128 = 64 μs

那剩余的 1936 μs(3872个存取周期)就可以用来读/写或者维持信息。由于在这 64 μs 内无法进行读/写操作,所以这段刷新时间被称为 “死时间”,也叫访存 “死区”。死时间占据存取周期的比例 64 μs / 2 ms × 100% = 3.2%,称为死时间率。

 

(2)分散刷新

对每行存储单元的刷新,分散到每个存取周期内完成。这样,每个存储周期 t~C~ 就分成了两段:前半段 t~M~ 用来读/写或者维持信息,后半段 t~R~ 用来刷新。所以:

t~C~ = t~M~ + t~R~

同样以 128 × 128 存储矩阵的芯片为例,读/写周期 t~M~ = t~R~ = 0.5 μs,那么存取周期 t~C~ = 1 μs。逐行进行刷新,每隔 128 μs 就可以将存储芯片全部刷新一遍。

 

这样的好处是不存在停止读/写操作的死时间,而且刷新间隔比要求的刷新周期 2ms 短得多;缺点在于存取周期 t~C~ 变长了,使得整个系统速度变慢。

(3)异步刷新

异步刷新是前两种方式的结合,它既可以缩短 “死时间”,又能充分利用最大的刷新间隔 2ms。

还是之前的例子,对于 128 × 128 存储矩阵的芯片,存取周期 t~C~ = 0.5 μs,可以让它把对 128 行的刷新平均分配到 2ms 的刷新周期内。也就是说,每隔 2ms ÷ 128 ≈ 15.6 μs 刷新一行,每次刷新的时间还是一个存取周期 t~R~ = 0.5 μs。

 

这样一来,2ms 内用于刷新的时间仍然是 128 t~R~ = 64 μs,而由于分散到了整个刷新周期内,每次刷新一行只停了一个存取周期;所以对于每行来说,刷新的间隔还是 2ms,而 “死时间” 缩短为 0.5 μs。

如果将 DRAM 的刷新安排在 CPU 对指令的译码阶段,由于这个阶段 CPU 不会访问存储器,所以这样就完全避免了 “死时间” 的问题,从根本上提高了机器效率。

DRAM 和 SRAM 的比较

目前,随着 DRAM 的容量不断扩大,速度不断提高,它的应用要比 SRAM 更加广泛。DRAM 主要用在计算机的主存中,而 SRAM 通常用于容量不大的高速缓存(Cache)中。

两者的特点可以比较如下:

 

3.3.3 ROM

ROM (Read Only Memory)最原始的定义是 “只读存储器”,一旦写入原始信息后就不能更改。所以ROM 通常用来存放固定不变的程序、常数和汉字字库,甚至用于操作系统的固化。它与随机存储器可共同作为主存的一部分,统一构成主存的地址域。

不过随着用户的需要和技术的发展,又出现了更多类型的 ROM,让用户拥有了修改数据的能力。

根据制造工艺不同,ROM 可分为固定掩模型 ROM(MROM)、一次可改写 ROM (PROM)、紫外线擦除电可编程 ROM(EPROM)、电擦除电可编程 ROM (EEPROM)、快擦写(Flash)存储器。

早期只读存储器的存储内容根据用户要求,厂家采用掩模工艺,把原始信息记录在芯片中,一旦制成后无法更改, 称为掩模型只读存储器(Masked ROM, MROM)。随着半导体技术的发展和用户需求的变化, 只读存储器先后派生出可编程只读存储器(Programmable ROM, PROM)、可擦除可编程只读存储器(Erasable Programmable ROM, EPROM)以及用电可擦除可编程只读存储器(Electrically Erasable Programmable ROM, EEPROM)。到 20 世纪 80 年代,又出现了闪速存储器 (Flash Memory), 它具有 EEPROM 的特点, 而速度比 EEPROM 快得多。

Flash

闪速存储器(闪存,Flash),又称快擦型存储器,是在 EEPROM 的工艺基础上发展而来的,性价比更好、可靠性更高。其主要特点有:

  • 价格便宜、集成度高;

  • 属非易失性存储器,适合长期保存信息;

  • 能快速擦写(电可擦除),写入前必须先擦除,因此写比读要慢。

由于 Flash 的擦除、重写时间已经非常短,比一般的 EEPROM 要快得多,所以 Flash 已经具备了 RAM 的功能,可以与 CPU 直接相连。电脑的 BIOS 程序由于包含了开机后的自检程序和自举装载程序,一般都会固化到主板上的一个 ROM 芯片中;如今的电脑通常就会用 Flash 芯片来存放 BIOS 程序。

Flash 可以至少擦写 10000 次以上,而且是非易失性存储器,所以在需要周期性修改存储信息、并长期保存的场合,它是一个非常理想的存储器;比如工控系统、单片机中作为数据采集和存储器件,用于制作 U 盘和移动硬盘等。

目前随着闪存技术的发展,容量越来越大、价格越来越低,让大容量 Flash 取代磁盘成为了可能。用闪存技术做成 固态硬盘(SSD),可以代替传统的磁盘,速度更快,功耗更低,体积更小。如今很多笔记本电脑中都使用了 SSD,使得计算机平均无故障时间大大延长。

SRAMDRAMROM 这 3 种存储器的特点可以总结如下。

 

3.4 主存储器

3.4.1 主存储器的基本组成

主存储器简称主存或内存,是计算机中存储程序和数据的重要部件。主存内包含了存储体、各种逻辑部件以及控制电路等。

主存是通过按地址访问的方式,对存储体内的存储单元进行读写操作的。因此主存首先需要从 MAR 中获取地址,由译码器进行地址译码、再经过驱动电路,进而通过选择线选中所需访问的单元。读出时,需要经过读出放大器才能将被选中存储单元的内容送到 MDR;写入时,MDR 中的数据也需要经过写入电路才能真正存入被选中的单元。所以主存实际结构的基本组成如下:

 

译码器、驱动器和读写电路都集成在 DRAM 存储芯片中,而 MAR 和 MDR 则集成在 CPU 芯片内。存储芯片可以通过总线与 CPU 相连。

 

当要从主存中读某个数据字时,首先由 CPU 将字的地址送到 MAR,通过地址总线送至主存,然后发出读命令;主存的译码器将地址总线送来的地址译码,导通对应存储单元的选择线,收到读信号后,便将该单元的内容送到数据总线上,进而交给 MDR。

如果要向主存写入一个数据字,仍然需要 CPU 先把地址送到 MAR,并把要写的数据送到 MDR,然后发出写命令;主存译码器依然从地址总线读取地址进行译码,接到写命令后,就把数据线上的信息写入对应的存储单元。

译码器(复习)

译码器是一种具有 “翻译” 功能的组合逻辑电路器件,可以将以二进制码表示的输入状态,转换成对应的特定输出信号。“译码” 就是 “编码” 的逆过程。

对于 n 位信号输入,译码器对应有 2^n^ 个输出。译码器可以用逻辑门电路很容易实现。

 

存储器中的地址译码器,就是以 n 位地址线作为输入,以 2^n^ 根选择线作为输出的译码器。当输入一个地址信号时,地址可以看作一个二进制数,它对应的十进制数就是选择线的序号。

主存中地址的分配

主存中各存储单元的空间位置,都是由存储单元的地址号表示的;地址总线的作用就是给出要访问的存储单元的地址。每次访问存储单元,可以读出或者写入一个存储字。

存储字长必须是字节(8位)的整数倍,不同机器的存储字长不同。计算机一般既可以按字来寻址,也可以按字节寻址。例如,一台机器的存储字长为 32 位,并且可以按字节寻址,那么它的每个存储字都包含了 4 个具有独立地址的字节,地址的分配方式如下:

 

字地址是用该字高位字节的地址来表示,所以字地址总是 4 的整数倍,即二进制末两位总是 0。这样,对于同一个字内的字节,可以用地址末两位来进行区分,高位则是完全相同的。

如果这台机器的地址线为 24 位,那么按字节寻址的范围是 2^24^ = 16M,按字寻址的范围为 16M / 4 = 4 M。

3.4.2 主存和 CPU 之间的连接

现代计算机的主存都由半导体集成电路构成,一般使用 DRAM 芯片。单个芯片的容量不可能很大,通过存储器芯片扩展技术,将多个芯片集成在一个 内存条上,然后由多个内存条及主板上的 ROM 芯片组成计算机所需的主存空间,再通过总线与 CPU 相连。

 

早期的内存就只是一块芯片,直接焊接在主板上;这样很难拆卸、更换,无法轻易扩容升级。内存条 的出现使得内存的更换和升级更加方便。内存条的主体是一块印制电路板(PCB),上面一般焊有多个内存芯片。通过印制板边缘的一排金色引脚(“金手指”)可以很容易地插入主板上的内存插槽,与 CPU 进行连接和数据交换。

1. 主存容量扩展

单片存储芯片的容量有限,很难满足我们实际应用的需要,所以主存一般不会直接使用单个芯片实现,而是需要将多个存储芯片连在一起扩展成更大的存储器。这称为 存储容量的扩展,主要的方法有 位扩展字扩展

(1)位扩展

位扩展是指对字长进行扩展,也就是增加存储字长。这种情况下,系统地址线位数等于芯片地址线位数,而系统数据线位数多于芯片数据线位数。

位扩展的连接方式:各芯片的地址线、片选线和读写控制线与系统总线相应 并联;各芯片的 数据线单独引出,分别连接系统数据线。各芯片同时工作。

以之前介绍过的 SRAM 芯片 2114 为例,它的存储容量为 1K × 4,那么用 2 片 2114 采用位扩展的方式可以组成 1K × 8 的存储器。如下所示:

 

(2)字扩展

字扩展是指对存储字的数量进行扩展,而存储字的位数满足系统要求。这种情况下,系统数据线位数等于芯片数据线位数,系统地址线位数多于芯片地址线位数。

字扩展的连接方式:各芯片的地址线与系统地址线的 低位对应相连,芯片的数据线和读写控制线与系统总线相应 并联;由系统地址线的 高位译码 得到各芯片的片选信号。各芯片分时工作,同一时间只能有一个芯片被选中。

例如,用 2 片容量为 1K × 4 的 2114 芯片,采用字扩展的方式可以组成 2K × 4 的存储器。如下所示:

 

扩展之后的地址线为 11 位,共有 2^11^ = 2 K 个地址。其中:

  • 第一片 2114 的地址范围为 000 0000 0000 ~ 011 1111 1111;

  • 第二片 2114 的地址范围为 100 0000 0000 ~ 111 1111 1111。

(3)字和位同时扩展

字和位同时扩展是前两种扩展的组合,这种方式既增加存储字的数量,又增加存储字长。

字和位同时扩展的连接方式:将进行位扩展的芯片作为一组,各组的连接方式与位扩展相同;由系统地址线高位译码产生若干个片选信号,分别接到各组芯片的片选信号。

例如,用 8 片容量为 1K × 4 的 2114 芯片,字和位同时扩展之后可以组成 4K × 8 的存储器。如下所示:

 

扩展之后的地址线为 12 位,共有 2^12^ = 4 K 个地址。其中:

  • 第一、二片 2114 通过位扩展构成第一组,地址范围为 0000 0000 0000 ~ 0011 1111 1111;

  • 第三、四片 2114 通过位扩展构成第二组,地址范围为 0100 0000 0000 ~ 0111 1111 1111;

  • 第五、六片 2114 通过位扩展构成第三组,地址范围为 1000 0000 0000 ~ 1011 1111 1111;

  • 第七、八片 2114 通过位扩展构成第四组,地址范围为 1100 0000 0000 ~ 1111 1111 1111。

2. 主存与CPU的连接

(1)合理选择存储芯片。通常选用 ROM 存放系统程序,选用 RAM 组成用户区。

(2)地址线的连接。CPU 地址线的低位与存储芯片的地址线相连,以选择芯片中的某一单元(字选);CPU 地址线的高位在扩充存储芯片时用,以选择存储芯片(片选)。

(3)数据线的连接。比较CPU的数据线数与存储芯片的数据位数。如果相等可以直接相连;如果不等,必须对存储芯片进行扩位,使其数据位数与 CPU 的数据线数量相等。

(4)读/写命令线的连接。CPU 的读/写命令线一般可以直接与存储芯片的读/写控制端相连。

(5)片选线的连接。片选信号一般由系统地址线高位译码,它是主存与 CPU 连接的关键。

3.4.3 多模块存储器

随着计算机技术的发展,处理的信息量越来越多,对存储器的速度和容量要求也越来越高;而且随着 CPU 性能的不断提升、I/O 设备数量不断增加,导致主存的存取速度已经成为了整个计算机系统的性能瓶颈。这就要求我们必须提高主存的访存速度。

基本的想法是寻找更加高速的元器件和存储芯片,或者采用层级结构、加入高速缓存;除此之外,调整主存的结构也可以提高访问速度。这就是所谓的 多模块存储器

1. 单体多字存储器

在主存中,程序和数据是连续存放的,所以 CPU 访存取出的信息也是连续的。如果将存储器的存储单元进行扩展,让它能够存储更多的字,那么就可以在一个存取周期内,从同一地址取出更多的指令。将多条指令逐条送至 CPU 执行,由于 CPU 的速度远高于主存,这样就相当于增大了主存的带宽,提高了速度。

这种方式是对单独的存储器进行了扩展,类似于位扩展的思路,不过是将一个地址对应的数据扩展到了多个存储字。所以这种结构的存储器称为 单体多字存储器

例如,对于一个单体四字存储器,可以在一个存取周期取出四个字的信息。假设指令字长就是一个存储字,那么原先一个存取周期拿到一条指令,现在就可以拿到 4 条;逐条传给 CPU 进行处理,就相当于每隔 1/4 周期,主存就向 CPU 传送了一条指令,带宽变成了 4 倍。

 

  • 结构特点:存储器中只有一个存储体,每个存储单元存储 m 个字,总线宽度也为 m 个字。

  • 访问方式:一次并行读出 m 个字,地址必须顺序排列并处于同一存储单元。

  • 优点:宽度为单体单字存储器的近 m 倍(访问的内容在同一行时)。

  • 缺点:如果出现访问冲突(需要的内容不在同一行)或遇到转移指令,效率会显著降低。

2. 多体并行系统

另一种思路是采用多模块组成存储器,各个模块可以并行读写,这就是多体并行系统。每个模块有相同的容量和存取速度,各模块都有自己独立的地址寄存器(MAR)、数据寄存器(MDR)、地址译码、驱动电路和读/写电路,它们能并行工作,也能交叉工作。

所谓的 “并行工作”,就是 CPU 可以同时访问 N 个模块,同时启动,同时读出;当然,由于总线是公共的,同时读出的 N 个字需要在总线上分时传送。

根据对这 N 个模块中存储单元的不同编址方式,多体并行系统又可以分为 多体高位交叉存储器多体低位交叉存储器

(1)多体高位交叉存储器

多体高位交叉存储器中,各模块采用 高位交叉方式编址

高位交叉方式编址时,地址分为两部分,高位地址表示体号,低位地址为体内地址。这种编址方式下,一个模块(也就是 “体”)内的地址是连续的,程序存储时会按照体内地址的顺序存放,也就是先存一个模块,存满之后再存下一个;所以这种方式也叫 “顺序存储”。

 

只要调动合理,使不同的请求源同时去访问不同的模块,就可以实现并行工作。比如,CPU 在访问一个模块的同时,外部设备可以以直接存储器访问(DMA)的方式访问另一个模块,这样两个体就是并行工作的。

(2)多体低位交叉存储器

多体低位交叉存储器中,各模块采用 低位交叉方式编址

低位交叉编址是指用主存地址的低位来指明存储器模块,高位指明模块内的字地址。这种编址方式下,连续的地址分布在相邻的模块中,同一模块内的地址是不连续的,因此也叫做 “交叉存储”。有 M 个模块的低位交叉编址,又叫 模 M 编址

 

上面是一个模 4 交叉编址的存储器,存储体模块个数为 4,所以第一个模块中所有存储单元的地址号,对 4 取模都为 0;同样道理,第二、三、四个模块的地址号,对 4 取模结果分别为 1、2、3。具体的编址地址号如下所示:

 

程序按照地址连续存放在相邻模块中,采用低位交叉编址后,可以在不改变每个模块存取周期的前提下,采用 流水线 方式并行存取,提高存储器的带宽。

在一个存取周期 T 内,m 个模块按一定的顺序分时启动;如果分时启动的时间间隔为 t = T/m,那么在一个存取周期内,CPU 交叉访问各个模块,从而使各模块的读/写操作交错重叠进行,最终向 CPU 可以传送 m 个字。这样,存储器的带宽提升为 m 倍。

由于各个模块传送取出的字共享总线,因此假设总线传输周期为 τ,当 t 小于等于 τ 时,就可以获得最大的存储器带宽。所以:

t = T/m ≤ τ

所以,对于一个存取周期为 T、总线传输周期为 τ 的机器,设计多体低位交叉存储器时应该有 m ≥ T / τ。一般取最小值即可,在采用流水线方式时应该满足

T = m τ

对于四体低位交叉编址存储器 T = 4 τ,按流水线方式工作时不同模块访问字的时间顺序如下:

 

可以看出,对于流水线工作的低位交叉存储器,连续读取 n 个字所需的时间为:

t~1~ = T + ( n - 1 ) τ

而如果是高位交叉存储器,对应的时间为:

t~2~ = n T

对于上面的四字低位交叉存储器,τ = T / 4,所以 t~1~ = ( n + 3 ) T / 4,明显要低于 t~2~;当 n 非常大时,t~1~ 趋近于 T / 4,即速度提升了 4 倍。

当然,由于多模块存储器需要同时管理多个存储模块,还要同时处理来自不同部件的访问请求,因此必须增加一个存储器控制部件(简称存控)来处理这些事情。存控可以合理安排各部件请求访问的顺序,并有控制主存读/写操作的功能。

因为多模块有额外的开销,所以尽管 m 体低位交叉存储器理论上速度可以达到原先的 m 倍,在实际运行中会有很大差距;而且随着模块数 m 的增大,速度的提升也会越来越不明显。因此实际应用中,最多的情况就是采用 2个模块的二体低位交叉存储器,这就是所谓的 “双通道内存” 技术。

此外,通过在 DRAM 中引入一个锁存器,可以将 CPU 给出的地址和控制信号锁存,然后在指定的时钟周期后再响应。这样一来,CPU 和主存的数据交互就可以 同步 于系统时钟信号,这种存储器就叫做 同步 DRAM(Synchronous DRAM,SDRAM)。CPU 在发出访问请求后不需要等待,可以在存储器的读取周期内去完成其它操作;等到读取周期(可能需要若干个时钟周期)结束后,CPU 就可以获得从存储器读出的数据了。

SDRAM 还支持猝发访问模式,CPU 只要发出一个地址就可以访问连续的一段数据块。SDRAM 芯片内也可以包含多个存储体,构成多体并行系统,提高访问速度。新一代的 SDRAM 可以每周期两次向 CPU 传输数据,因此称为 双倍数据速率 SDRAM(Double Data Rate SDRAM,DDR-SDRAM)。

3.5 外部存储器

外部存储器是主存的后援设备,也叫做辅助存储器,简称 外存辅存,与主存一起构成了存储器系统的主存-辅存层次。与主存相比,外存容量大、速度慢、价格低,可以脱机保存信息,属于 非易失性存储器

用于计算机系统的外存主要有磁盘、磁带、光盘;磁盘和磁带都属于 磁表面存储器。而目前广泛应用的 固态硬盘(SSD)主体由闪存芯片构成,属于半导体存储器。

3.5.1 磁盘存储器

磁盘是应用最为广泛的外存设备。磁盘根据结构和盘片材质的不同,可以分为 硬磁盘软磁盘,如今随着存储技术的发展,软磁盘存储器已渐渐不再使用,而硬磁盘存储器依然在外存中占据着重要的比例。

磁盘存储器具有外存设备普遍的优缺点:

  • 优点:存储容量大,位价低;记录介质可重复使用;记录信息可长期保存而不丢失, 甚至可脱机存档;非破坏性读出,读出时不需要再生。

  • 缺点:存取速度慢,机械结构复杂。

1. 磁表面存储器和磁记录原理

磁表面存储器在不同形状(盘状、带状)的载体上涂有磁性材料层,这磁层就是记录信息的存储介质。存储器工作时,依靠载体的机械运动,由磁头在磁层上进行读/写操作;信息就记录在磁层上,这些信息的轨迹叫做 磁道。磁盘的磁道是一个个同心圆,磁带的磁道则是一条条直线。

 

  • 磁记录原理:磁表面存储器在磁头和磁性记录介质做相对运动时,通过电磁转换完成读/写操作。

 

 

  • 磁记录方式:又称为编码方法,就是按某种规律把一连串的二进制信息转换成磁表面相应的磁化状态。通常采用调频制(FM)和改进型调频制(MFM)的记录方式。

2. 硬磁盘的分类和基本结构

(1)硬磁盘存储器的类型

硬磁盘中的存储载体是盘片,它是由硬质铝合金材料制成的,其表面涂有一层硬磁特性材料,可以被磁化从而完成信息的存储。通过磁头和盘片的相对运动,就可以实现信息的读取和写入。

 

  • 根据能否更换盘片,硬磁盘可以分为 可换盘磁盘固定盘磁盘

可换盘磁盘的盘片可以脱机保存,所以更换的时候可以只换单片,方便维护和扩容;固定盘磁盘的盘片则不能从驱动器中取下,更换的时候需要整体更换,可靠性更高。

  • 按照磁头的工作方式,硬磁盘可以分为 固定磁头磁盘移动磁头磁盘

 

  • 固定磁头的磁盘存储器,磁头位置是固定不动的,磁盘上的每一个磁道都对应着一个磁头,盘片也不可以更换;这样省去了磁头在盘片上移动寻找磁道的时间,存取速度更快。

  • 移动磁头的磁盘存储器,存取数据时磁头需要在盘面上做径向运动;这类存储器可以只有一个盘片,也可以有多个盘片。多个盘片会装在一个同心主轴上,每个记录面各有一个磁头。所有这些磁头连成一体,固定在支架上移动;任何时刻所有磁头和主轴的距离都相等,它们位于和圆心相等距离的一组磁道上,这组磁道称为一个 柱面

目前,移动磁头的多盘片磁盘存储器应用最广泛,典型代表是 温切斯特磁盘。温切斯特磁盘简称温盘,是一种可移动磁头、固定盘片的磁盘存储器。它采用密封组合的方式,将磁头、盘片、驱动部件以及读/写电路等做成一个不可拆卸的整体,称作 头盘组合体。所以它的特点是可靠性强,防尘性能好,对环境要求不高。

(2)硬磁盘存储器的组成

硬磁盘存储器由磁盘驱动器、磁盘控制器和盘片组成。

 

  • 磁盘驱动器

磁盘驱动器是主机之外的一个独立装置,又称作 磁盘机。驱动器主要包括主轴、定位驱动和数据控制 3 个部分。

 

主轴受传动机构的控制,可以使磁盘高速旋转;磁头分装在读/写臂上,连接到一个小车,在音圈电机的控制下平行移动进行寻道;定位驱动系统是一个带有速度、位置反馈的闭环自动控制系统,根据磁头的即时位置和速度计算出接下来运动的方向和速度;数据控制部分主要对数据转换和读/写操作进行控制。首先接收选头选址信号,然后根据磁记录方式将数据脉冲和线圈的驱动电流进行转换。

  • 磁盘控制器

磁盘控制器是磁盘存储器和主机的接口,通常就是一块电路板,插在主机总线插槽中。它的作用是接收由主机发来的命令,将其转换成磁盘驱动器的控制命令,实现主机和驱动器之间的数据格式转换和数据传送,并且控制驱动器的读/写操作。一个磁盘控制器可以控制多台驱动器。

将磁盘控制器的功能全部内置在磁盘设备中,主机和设备之间就可以采用标准的通用接口了。最初这种接口就称为 IDE(Integrated Drive Electronics)接口,同时期还有更高性能的 SCSI(Small Computer System Interface)接口;之后又发展出了采用串行传输技术的接口,这就是 SATA(Serial Advanced Technology Attachment)和 SAS(Serial Attached SCSI)。目前我们的个人电脑中,大多都是采用 SATA 接口的硬盘。

  • 盘片

盘片是磁盘中存储信息的载体,由驱动器控制它的转动并读/写数据;有时也会直接把盘片当作驱动器的一部分。目前硬盘的盘片正朝着小体积大容量的方向发展,记录密度越来越高。

3. 磁盘的工作原理

(1)磁盘存储区域

一块磁盘划分为若干个记录面,每个记录面划分为若干条 磁道,而每条磁道又划分为若干个 扇区,扇区(也称块、扇段)是磁盘读写的最小单位,即磁盘按块存取。一个具有多盘片的磁盘组,可将其 n 个面上所有同一半径的磁道看成一个圆柱面,称为 柱面;在移动磁头的组合盘中,多个磁头一次定位的磁道集合就是一个柱面。

  

  • 磁头数:表示磁盘总共有几个磁头,一般来说一个记录面对应一个磁头,所以等于记录面数。

  • 柱面数:表示磁盘中柱面的个数,等于每个记录面上的磁道数。

  • 扇区数:表示每条磁道上有几个扇区。

(2)磁盘地址

一个磁盘存储器可以有多台驱动器,不同的驱动器可以用一个编号(驱动器号,或者台号)来区分。当驱动器号确定后,磁盘进行寻址定位时,首先需要整体移动磁头找到对应柱面(磁道)、再选定磁头,最后转动盘片找到扇区。所以寻址所需要的磁盘地址,一般由 驱动器号柱面(磁道)号盘面号扇区号 组成。

磁盘的地址格式如下所示:

例如,系统中有 4 个驱动器,每个驱动器带一个磁盘组,其中有 11 个盘片(最外层上下侧为保护面),每个盘面有 203 个磁道、划分为 16 个扇区。则可以算出,驱动器号需要 2 位;柱面号需要 8 位( 2^7^ < 203 < 2^8^ );而 11 个盘片有 20 个盘面,所以盘面号需要 5 位;扇区号需要 4 位。最终每个磁盘地址要 19 位二进制代码。

(3)磁盘的工作过程

磁盘的主要操作是寻址、读盘、写盘。磁盘属于机械式部件,其读/写操作是串行的,不可能在同一 时刻既读又写,也不可能在同一时刻读两组数据或写两组数据。

4. 磁盘的性能指标

(1)记录密度

记录密度通常是指单位长度内所存储的二进制信息量。磁盘存储器用 道密度位密度面密度 来表示。

  • 道密度:沿磁盘半径方向单位长度上的磁道数;单位 tpi(Track Per Inch,道每英寸)或 tpm(道每毫米)。为避免电磁干扰,磁道之间会保持一定的距离,称为 道距;道密度就是 D~t~ 就是 道距 P 的倒数:

$$
D_t = \frac{1}{P}
$$

  • 位密度:单位长度的磁道上能记录的二进制位数;单位 bpi(Bits Per Inch,位每英寸)。磁盘中每个磁道上记录的信息量是相同的,可以记为每道总位数 f~t~ ;由于各个磁道周长不同,因此位密度也不同。一般所说的磁盘位密度,指的就是最内圈上的位密度(最大位密度)。如果最内圈同心圆的直径为 d~min~,那么位密度 D~b~ 为:

$$
D_b = \frac{f_t}{{\pi}d_{min}}
$$

  • 面密度:位密度和道密度的乘积。

(2)存储容量

存储容量指磁盘能存储的二进制信息的总数量,一般以位或者字节为单位。磁盘存储容量 C 可以计算为:

$$
C = n \times k \times s
$$

其中 n 为 盘面数,k 为每个盘面的磁道数,s 为每条磁道上记录的二进制代码数。

磁盘有非格式化容量和格式化容量两个指标。非格式化容量是指磁表面可利用的磁化单元总数,可以由道密度和位密度计算得到;格式化容量是指按某种特定的记录格式所能存储信息的总量,即用户可以使用的容量,一般是非格式化容量的 60% ~ 70%。

(3)平均寻址时间

磁盘的存取方式是直接存取,它的寻址时间分为两个部分:磁头寻找目标磁道的时间 t~s~;和找到磁道后,磁头等待要读写的磁道区段(扇区)旋转到磁头下方的时间 t~w~ 。由于寻找相邻磁道和不相邻磁道的时间不同,磁头等待不同扇区的时间也不同,所以应该取平均值,称为 平均寻址时间;它是 平均寻道时间 t~sa~ 和 平均等待时间 t~wa~ 之和。

$$
T_a = t_{sa} + t_{wa} = \frac{t_{smax} + t_{smin}}{2} + \frac{t_{wmax} + t_{wmin}}{2}
$$

平均寻址时间再加上数据传输时间,就是磁盘的 平均访问时间

(4)数据传输率

数据传输率是指单位时间内,磁盘向主机传送数据的位数或字节数。数据传输率 D~r~ 与记录位密度 D~b~ 和磁道运动速度 V 有关;

$$
D_r = D_b \times V
$$

对于磁盘来说,“磁道运动速度” 一般用磁盘的转速 r (单位 转/s)表示,那么

$$
D_r = D_b \times (r \times \pi d_{min}) = r \times f_t
$$

(5)误码率

误码率是衡量磁盘出错概率的参数,等于从磁盘读出信息时,出错信息位数和读出信息总位数之比。为了减少出错率,磁盘一般采用循环冗余校验(CRC)码来发现和纠正错误。

5. 冗余磁盘阵列 RAID

冗余磁盘阵列(Redundant Array of Independent Disks,RAID)是将多个独立的物理磁盘组成一个磁盘阵列,引入并行处理技术,让数据在多个物理盘上分割交叉存储、并行访问。

根据不同的目的,可以采用不同的 RAID 方案;在 RAID1 ~ RAID5 的几种方案中,无论何时有磁盘损坏,都可以随时拔出受损的磁盘再插入好的磁盘,而数据不会损坏。RAID 的分级如下所示:

  • RAID0:无冗余和无校验的磁盘阵列。

  • RAID1:镜像磁盘阵列,无校验。

  • RAID2:采用纠错的海明码的磁盘阵列。

  • RAID3:位交叉奇偶校验的磁盘阵列。

  • RAID4:块交叉奇偶校验的磁盘阵列。

  • RAID5:无独立校验的奇偶校验磁盘阵列。

其中,RAID0 把连续多个数据块交替地存放在不同物理磁盘的扇区中,几个磁盘交叉并行读写,不仅扩大了存储容量,而且提高了磁盘数据存取速度,但 RAID0 没有容错能力。

RAID1 是为了提高可靠性,使两个磁盘同时进行读写,互为备份,如果一个磁盘出现故障,可从另 一磁盘中读出数据。两个磁盘当一个磁盘使用,意味着容量减少一半。

总之,RAID通过同时使用多个磁盘,提高了传输率;通过在多个磁盘上并行存取来大幅提高吞吐量;通过镜像功能,提高了安全性、可靠性;通过数据校验,提供容错能力。

3.5.2 固态硬盘(SSD)

固态硬盘(Solid State Disk,SSD)是基于闪存(Flash)技术的半导体存储器,它与 U 盘并没有本质差别。SSD 由闪存芯片和闪存翻译层组成,闪存芯片代替了传统磁盘中的磁盘驱动器,闪存翻译层则将来自 CPU 的读写请求翻译成对芯片的读写控制信号,相当于磁盘中的磁盘控制器。

 

固态硬盘有很多优点。它由半导体存储器构成,没有机械部件,所以随机访问速度比磁盘快很多,也没有任何机械噪声和震动。另外,SSD 还具有能耗低、抗震性好、安全性高等优点。

当然,固态硬盘也有缺点。它最大的问题是依然基于 EEPROM 的擦除原理,随机写入比较慢。

固态硬盘的数据都存放在闪存芯片中。一个闪存芯片内包含了多个 “块”,每个块又由若干 “页” 组成。数据以页为单位进行读写,但是需要以块为单位进行擦除;所以只有一页所属的块整个被擦除之后,才能重新写这一页。一旦一个块被擦除了,块中的每一页都可以再写一次。一般某个块进行了数千次重复写之后,就会损坏。

 

因此随机写很慢,有两个原因:首先,擦除块本身就比较慢;其次,如果试图写的页所在块已经有数据了,那么这个块中其它所有有数据的页都必须被复制到一个新块(擦除过的块),然后才能进行写操作。

因此,闪存的擦写寿命是有限的,读/写数据通常会集中在 SSD 的一部分闪存,这部分闪存就会损坏得特别快;在磨损不均衡的情况下,数个闪存块的损坏,会导致整个 SSD 损坏。为弥补 SSD 的寿命缺陷,引入了 磨损均衡技术,SSD 磨损均衡技术大致分为两种:

  • 动态磨损均衡:写入数据时,自动选择较新的闪存块。

  • 静态磨损均衡:监测并自动进行数据分配,让旧的闪存块承担无须写数据的储存任务,同时让较新的闪存块空出来;平常的读/写操作都在较新的闪存块中进行,这样就使各闪存块的损耗更为均衡。

有了磨损均衡技术,SSD 的寿命就比较可观了。例如,对于一个 256 GB 的 SSD,闪存的擦写寿命是 500 次的话,那么就需要写入125 TB 数据才可能损坏;而目前的 Flash 芯片已经做到至少可以擦写上万次了。

3.6 高速缓冲存储器(Cache)

为了解决 CPU 和主存之间速度不匹配的问题,计算机系统中引入了高速缓存(Cache)的概念。基本想法就是使用速度更快但容量更小、价格更高的 SRAM 制作一个缓冲存储器,用来存放经常用到的信息;这样一来,CPU 就可以直接与 Cache 交换数据,而不用访问主存了。

这种方案之所以有效,是因为通过对大量典型程序分析发现,在一定时间内,CPU 要从主存取指令或者数据,只会访问主存局部的地址区域。这是由于指令和数据在内存中都是连续存放的,而且有些指令和数据会被多次调用(比如常用函数、循环代码段、数组和一些常数);也就是说,指令和数据在主存中地址分布不是随机的,而是相对的簇聚。这使得 CPU 执行程序时,访存具有相对的局部性;这称为程序访问的 局部性原理

  • 时间局部性:如果一个数据现在被访问了,那么以后很有可能也会被访问

  • 空间局部性:如果一个数据现在被访问了,那么它周围的数据在以后可能也会被访问

局部性原理是 Cache 高效工作的理论基础。

3.6.1 Cache 的基本工作原理

为了便于 Cache 与主存交换信息,Cache 和主存都被划分为相等的块。Cache 块又称 Cache 行,每块由若干字节组成,块的长度称为块长。由于 Cache 的容量远小于主存的容量,所以 Cache 中的块数要远少于主存中的块数,Cache 中仅保存主存中最活跃的若干块的副本。

1. Cache 工作原理

假设主存按字节编址,地址用 n 位二进制码表示,那么主存容量为 2^n^ B;块的大小为 16 个字节,那么主存中块的个数为:2^n^ / 16 = 2^n-4^。那么如果对每个块也做一个编号,其实就对应着地址中的前 n - 4 位。

 

这样,主存地址就分成了两部分:高 n - 4 位表示主存中的 “块地址”,低 4 位表示 “块内地址”,块内地址其实就是具体存储字在块内的 “偏移量”。类似,Cache 中地址也可以分成这样的两部分,由于 Cache 中块长与主存一致,所以低 4 位同样是块内地址;剩余的高位则为 Cache 的块号。Cache 的块号位数小于 n - 4。

 

所以,可按照某种策略预测 CPU 在 未来一段时间内要访存的数据,将其装入 Cache。当 CPU 要读取主存中的某个字时,分为两种情况:

  • Cache 命中:需要的字已经在缓存中,就将其地址转换为缓存地址,直接访问 Cache,与主存无关;

  • Cache 未命中:需要的字不在缓存中,仍需访问主存,并将该字所在的块一次性地从主存调入 Cache。

如果某个主存块已经调入 Cache,就称该主存块和 Cache 中的缓存块建立了对应关系。由于 Cache 容量有限,当 Cache 已满时,就需要根据某种替换算法,让需要调入 Cache 的块替换之前某个缓存块的内容。所以,一个缓存块不可能永远只对应一个主存块;需要给每个缓存块设置一个标记,写入当前对应的主存块号,表示它当前存放了哪个主存块。

CPU 与 Cache 之间的数据交换,通常是以字为单位;而 Cache 与主存之间的数据交换则以块为单位。

2. 命中率

Cache 的效率,通常用 命中率 来衡量。命中率是指 CPU 要访问的信息已经在 Cache 中的比率。Cache 的容量和块长都是影响命中率的重要因素。

假设一个程序执行期间,访问 Cache 的总命中次数为 N~c~,访问主存的总次数为 N~m~,那么命中率为:

$$
h = \frac{N_c}{N_c + N_m}
$$

设 t~c~ 为命中时的 Cache 访问时间,t~m~ 为未命中时的主存访问时间,那么 Cache - 主存系统的平均访问时间 t~a~ 为:

$$
t_a = ht_c + (1-h)t_m
$$

由于 t~c~ 远小于 t~m~,因此平均访问时间 t~a~ 越接近 t~c~ 就说明 Cache 效率越高。用 e 表示访问效率,则有:

$$
e = \frac {t_c}{t_a} \times 100\% = \frac{t_c}{ht_c + (1-h)t_m} \times 100\%
$$

命中率 h 越接近 1,访问效率就高。一般来说,Cache 容量越大,命中率就越高;而块长与命中率的关系较为复杂,它取决于程序的局部特性,一般取每块 4 ~ 8 个可编址单位(字或字节)效果较好。

3. Cache 的基本结构

Cache 主要由 Cache 存储体、主存 - Cache 地址映射变换机构、Cache 替换机构几大模块组成。

 

(1)Cache 存储体

Cache 存储体以块为单位与主存交换信息,Cache 访存的优先级最高。

(2)主存 - Cache 地址映射变换机构

地址映射变换机构会将 CPU 送来的主存地址转换为 Cache 地址。由于主存和 Cache 块长相同,所以块内地址是不变的,地址变换主要就是主存的块号(高位地址)到 Cache 块号之间的转换。这涉及到一个函数的映射关系,被称为 地址映射

(3)Cache 替换机构

地址转化之后,如果 Cache 命中,CPU 就直接访问 Cache 存储体;如果不命中,CPU 需要访问主存将需要的字取出,并把它所在的主存块调入 Cache。如果 Cache 已满,无法将主存块直接调入 Cache,就需要 Cache 内的替换机构执行替换策略。

所谓替换策略,就是按一定的替换算法,确定从 Cache 中移出哪个块返回主存,并把新的主存块调入 Cache 进行替换。

在执行写操作时,还需要考虑如何使 Cache 如何与主存的内容保持一致。这就需要用某种 Cache 写策略。

4. Cache 的改进

Cache 的改进,主要就是由一个缓存改为使用多个缓存。主要有两个方向:增加 Cache 级数;将统一的 Cache 变为分立的 Cache。

(1)两级缓存

最初在 CPU 和主存之间只设一个缓存,称为 单一缓存。随着集成电路密度的提高,这个缓存就直接与 CPU 集成在了一个芯片中,所以又称为 片内缓存(片载缓存)

由于片内缓存容量无法做到很大,所以可以考虑在片内缓存和主存之间再加一级缓存,称为 片外缓存,也由 SRAM 组成。这种由片外缓存和片内缓存构成的 Cache 系统被称为 “两级缓存”,片内缓存作为第一级(L1 Cache),片外缓存作为第二级(L2 Cache)。

(2)分立缓存

指令和数据都存放在同一缓存内的 Cache,称为 统一缓存;而 分立缓存 则将指令和数据分别存放在两个缓存中,一个叫指令 Cache,另一个叫数据 Cache。这两种缓存的选择主要考虑两个因素:

  • 主存结构。如果计算机主存中指令、数据是统一存储的,则相应的 Cache 采用统一缓存;如果主存指令、数据分开存储,则相应的 Cache 采用分立缓存。

  • 机器对指令执行的控制方式。如果采用了超前控制或者流水线控制方式,一般都采用分立缓存。所谓超前控制,是指在当前指令执行尚未结束时就提前把下一条准备执行的指令取出;而所谓流水线控制,就是多条指令同时分阶段执行。

3.6.2 Cache 和主存之间的映射方式

Cache 块中的信息是主存中某个块的副本,地址映射是指把主存地址空间映射到 Cache 地址空间,这相当于定义了一个函数:

Cache 地址 = f ( 主存地址 )

当然,由于 Cache 和主存块长一样,而块内地址只是字在当前块内的 “偏移量”,所以映射转换之后块内地址是不变的。我们需要的其实只是 Cache 块号和主存块号之间的函数关系:

Cache 块号 = f ( 主存块号 )

Cache 块远少于主存块,所以 Cache 块不可能永远对应唯一的主存块,需要在 Cache 中为每一个块加一个 标记,指明它是主存中哪一块的副本。这个标记的内容,应该能够唯一确定对应主存块的编号。另外,为了说明 Cache 行中的信息是否有效,每个 Cache 行还需要有一个 有效位,该位为 1 时,表示 Cache 中该映射的主存块数据有效;为 0 则无效。

地址映射的方法有以下 3 种。

1. 直接映射

直接映射 的思路非常简单,就是 “挨个对应”,主存中的每一块只能装入 Cache 中的唯一位置。由于 Cache 容量很小,当主存中的块已经 “遍历” 完所有 Cache 地址后,下一个主存块的对应位置就又成了 Cache 中的第一行(第一个块)。

很明显,这跟 “顺序存储” 的思路是一样的,用主存块号对 Cache 的总行数取模,就可以得到对应 Cache 的行号了:

Cache行号 = 主存块号 mod Cache总行数

例如,假设主存地址为 32 位,按字节编址,主存块大小为 64 B,所以主存块共有 2^32^ / 64 = 2^26^ 个;如果 Cache 只有 4 行(4 个块),那么采用直接映射方式的对应关系如下:

 

更加一般化,假设 Cache 共有 2^c^ 行,主存有 2^m^ 个块,那么 Cache 行号有 c 位,主存块号有 m 位。在直接映射方式中,主存块号为 0、2^c^、2^c+1^... 的块,都映射到 Cache 的第 0 行;而主存中块号为 1、2^c^ + 1、2^c+1^ + 1... 的块,映射到 Cache 的第 1 行;以此类推。

这样一来,主存块号的低 c 位就对应了 Cache 中的行号;当一个块存放在 Cache 中,只需要高 m - c 位就可以指明它对应的主存中的块号。给每个 Cache 行设置一个 t = m - c 位的标记,那么当主存某块调入 Cache 后,就将其块号的高 t 位设置在对应 Cache 行的标记中。

所以直接映射方式下,主存地址结构为:

 

访存过程:

① 根据访存地址中间的 c 位,找到对应的 Cache 行。

② 将该 Cache 行中的标记和主存地址的高 t 位标记进行比较。

③ 若相等且有效位为1,则 Cache 命中,此时根据主存地址中低位的块内地址,在对应的 Cache 行中存取信息;若不相等或有效位为 0,则 Cache 未命中,此时 CPU 从主存中读出该地址所在的一块信息,并送至对应的 Cache 行中,将有效位置 1,并置标记为地址中的高 t 位。

 

直接映射实现简单,但不够灵活,即使 Cache 的其他许多地址空着也不能占用,这使得直接映射的块冲突概率高,空间利用率低。

2. 全相联映射

直接映射的问题在于,我们找到的是从主存块到缓存行的一种 “多对一” 的关系,每一个主存块只能对应唯一的缓存行,从而导致冲突概率高。如果让一个主存块,可以映射到多个缓存块上,变成 “多对多” 的关系,明显就可以减少冲突了。

最简单的情况,就是不加任何条件限制,让主存的每一个块都可以映射到 Cache 的任意位置;简单来说就是 “有空就填”,放在哪里都可以。这就是 全相联映射 方式。

 

由于没有任何规律,所以当一个块存放在 Cache 中,无法根据 Cache 行号推出它对应主存块的任何信息;因此必须在每行的标记中明确指出该行取自主存的哪一块,这样标记就需要完整的 m 位主存块号。CPU 访存时,需要与所有 Cache 行的标记进行比较。

全相联映射方式下,主存的地址结构为:

 

全相联映射方式的优点是灵活,Cache块的冲突概率低,空间利用率高,命中率也高;缺点是标记的速度较慢,实现成本较高,通常需采用昂贵的按内容寻址的相联存储器进行地址映射。

3. 组相联映射

把直接映射和全相联映射两种方式结合起来,就是 组相联映射 方式。

组相联的思路是将 Cache 分成 Q 个大小相等的组,每个主存块可装入对应组的任意一行;它所在的组则按顺序依次排列得到。也就是 组间采用直接映射、而 组内采用全相联映射 的方式。当 Q=1 时,变为全相联映射;当 Q = Cache 行数时变为直接映射。

假设每组有 R 个 Cache 行,则称之为 R 路组相联;例如每组有 2 个 Cache 行时称为 2 路组相联。

类似的例子,假设主存地址为 32 位,按字节编址,主存块大小为 64 B,所以主存块共有 2^32^ / 64 = 2^26^ 个;如果 Cache 有 8 行(8 个块),采用 2 路组相联映射方式,那么共有 Q = 8 / 2 = 4 组。对应关系如下:

 

可以看出,现在的 “组号” 就相当于直接映射方式下的行号,可以由主存块号对组数 Q 取模得到:

Cache组号 = 主存块号 mod Cache组数

更加一般化,假设 Cache 共有 2^c^ 行,分为 Q = 2^q^ 组,主存有 2^m^ 个块;那么 Cache 行号有 c 位,其中高 q 位是组号,主存块号有 m 位。这时每组中的 Cache 行数为 R = 2^c^ / Q = 2^c-q^ ,行号的低 c - q 位就代表了 Cache 行在组内的序号。

在 R 路组相联映射方式中,主存块号为 0、2^q^、2^q+1^... 的块,都映射到 Cache 的第 0 组,可以选择组内 2^c-q^ 行的任一行;而主存中块号为 1、2^q^ + 1、2^q+1^ + 1... 的块,映射到 Cache 的第 1 组,同样可以任选组内的 Cache 行;以此类推。

这样一来,主存块号的低 q 位就对应了 Cache 中的组号;当一个块存放在 Cache 中,只需要高 m - q 位就可以指明它对应的主存中的块号。给每个 Cache 行设置一个 t = m - q 位的标记,那么当主存某块调入 Cache 后,就将其块号的高 t 位设置在对应 Cache 行的标记中。

所以组相联映射方式下,主存地址结构为:

 

访存过程:

① 先根据访存地址中间的 Cache 组号,找到对应的 Cache 组。

② 然后将该组中每个 Cache 行的标记与主存地址的高位标记进行比较。

③ 若有一个相等且有效位为1,则 Cache 命中,此时根据主存地址中的块内地址,在对应 Cache 行中存取信息;若都不相等,或虽相等但有效位为 0,则 Cache 未命中,此时 CPU 从主存中读出该地址所在的一块信息,并送至对应 Cache 组的任意一个空闲行,将有效位置 1,并设置标记。

组相联映射方式下,路数 R 越大,即每组 Cache 行的数量越多,发生块冲突的概率越低,但比较电路也越复杂。

可以将以上 3 中映射方式对比如下:

 

3.6.3 Cache 中主存块的替换算法

如果有新的主存块需要调入 Cache,而可用空间又已经占满,这时就需要替换掉某个旧块,这就产生了替换策略(替换算法)的问题。当采用直接映射时,替换的位置是固定的,无须考虑替换算法;而在采用全相联映射或组相联映射时,就需要使用替换算法来确定到底置换哪个 Cache 行。

常用的替换算法有 随机(RAND)算法先进先出(FIFO)算法最近最少使用(LRU)算法最不经常使用(LFU)算法。其中最常考查的是 LRU 算法。

  • 随机算法:随机地确定替换的 Cache 块。实现简单,但未依据局部性原理,命中率较低。

  • 先进先出算法(Fisrt In First Out,FIFO):选择最早调入的行进行替换。实现简单,但也未依据局部性原理。

  • 最近最少使用算法(Least Recently Used,LRU):依据局部性原理,选择近期最久未访问过的 Cache 行作为被替换的行。LRU 算法为每个 Cache 行设置一个计数器,用来记录每个块的使用情况,并根据计数值选择淘汰某个块。

  • 最不经常使用算法(Least Frequently Used,LFU):将一段时间内访问次数最少的 Cache 行换出。与 LRU 类似,也设置一个计数器,Cache 行建立后从 0 开始计数,每访问一次计数器加 1,需要替换时将计数值最小的行换出。

例如,假设一台机器 Cache 有 8 个行,初始值为空,采用 4 路组相联映射方式和 LRU 替换策略,当顺序访问主存块号为 0,4,8,3,0,6,12,0,4,8 时,缓存的命中和替换情况如下:

 

LRU 算法中利用计数器来表示 Cache 行未被访问的时间。整体原则是:当 Cache 行有新的主存块调入时,计数器开始计数,初始值为 0,此后每遇到一次对 Cache(或 Cache 组)的访问就加 1;如果一次访问 Cache 命中了这一行,就将计数器清 0;每次有 Cache 行计数器清 0,其它行的计数器依然要加 1,不过只需要计数值比当前行更小的那些继续加 1 就可以了。

需要替换时,直接选择计数值最大的行,调入新的块并将计数器置 0。这是由于不同的 Cache 行不会同时开始计数,且每次都同步加 1,所以所有 Cache 行的计数值都不会相同,每次发生替换时必然能够找到一个最大值;而一旦有计数器清 0,比它计数值更大的那些也是都不加 1,依然保持着原有的大小顺序。

这样一来,如果当前 Cache 共有 2^c^ 行,分为 Q = 2^q^ 组,每组行数为 R = 2^c^ / Q = 2^c-q^ ,那么计数器的值就不会超过 R;只要用 c - q 位就可以表示计数器了,这被叫做 LRU 位。因此,计数值的位数与 Cache 组的大小有关。当为 2 路时有 1 位 LRU 位,4 路时有 2 位 LRU 位。LRU 位会同标记、有效位一同作为 Cache 的一部分。

3.6.4 Cache 写策略

因为 Cache 中的内容是主存块内容的副本,当对 Cache 中的内容进行更新时,就需选用写操作策略使 Cache内容和主存内容保持一致。此时分两种情况:

(1)Cache 写命中(要修改的单元在 Cache 中)

这种情况有两种处理方法:

  • 写直达法

也叫全写法、写穿透法。将数据同时写入 Cache 和主存。这种方法实现简单,一致性好。缺点是降低了速度,时间开销为访存时间。为了减少写入主存的开销,可以在 Cache 和主存之间加一个写缓冲。

  • 写回法

也叫回写法、写返回法。数据只写入 Cache,而不立即写入主存,只有当此块被换出时才写回主存。这种方法效率很高,但一致性较差。在每个 Cache 行中设置一个修改位(脏位),若修改位为 1(“脏”),则说明对应 Cache 行中的块被修改过,替换时须写回主存;若修改位为 0(“净”),则替换时无须写回主存。

(2) Cache 写未命中(要修改的单元不在 Cache中 )

这种情况也有两种处理方法:

  • 写分配法

把数据写入主存,同时将该块调入Cache。这种方法依据了空间局部性原理。

  • 非写分配法

只把数据写入主存,不进行调块。

非写分配法通常与全写法合用,写分配法通常与回写法合用。

这样,还是之前的机器,采用组相联映射的 Cache 共有 2^c^ 行,分为 Q = 2^q^ 组,主存有 2^m^ 个块;那么 Cache 行号有 c 位,其中高 q 位是组号,主存块号有 m 位。这时每组中的 Cache 行数为 R = 2^c^ / Q = 2^c-q^ ,即采用 R 路组相联映射,假如还采用了 LRU 替换策略和回写法,那 Cache 行应该包含以下部分:

 

现代计算机通常设立多级 Cache,一般两级 Cache 按离 CPU 的远近分别命名为 L1 Cache、L2 Cache,离 CPU 越近则速度越快、容量越小。指令 Cache 与数据 Cache 分离一般在 L1 级,LI Cache 对 L2 Cache 使用全写法,L2 Cache 对主存使用回写法。由于L2 Cache的存在,避免了因频繁写而造成写缓冲溢出的情况。

3.7 虚拟存储器

早期的计算机,CPU 是直接操作主存的,也就是运行程序时,直接给出要访问的实际主存地址。这种方式简单直接,但是会有一些问题:

  • 不同的程序之间需要共享内存,它们的内存地址空间很难隔离,从而导致程序运行的稳定性和安全性降低;

  • 主存容量有限,如果同时执行的程序太多、使用内存太大容易超出容量限制而崩溃。

 

为了解决这些问题,在主存-辅存这一层次的不断发展中,逐渐形成了虚拟存储系统。

主存和辅存共同构成了虚拟存储器,二者在硬件和系统软件的共同管理下工作。对于应用程序员而言,虚拟存储器是透明的。虚拟存储器具有主存的速度和辅存的容量。

3.7.1 虚拟存储器的基本概念

虚拟存储器将主存和辅存的地址空间统一编址,形成一个庞大的地址空间,在这个空间内,用户可以自由编程,而不必在乎实际的主存容量和程序在主存的实际存放位置。用户编程允许涉及的地址称为 虚地址逻辑地址,虚地址对应的存储空间称为虚拟空间。实际的主存地址称为 实地址物理地址,实地址对应的是主存地址空间。虚地址比实地址要大很多。

使用虚拟存储器之后,程序中看到的地址都是逻辑地址。在访存时,逻辑地址首先会被转换成物理地址,然后再访问实际物理内存。

 

这样一来,每一个程序都有独立的虚拟地址空间,不同进程的虚拟地址空间互相不干扰,提高了安全性。在每个进程看来,就像它自己独享了整个内存。当物理内存不够时,可以将一部分不常使用的内存块换出(Swap-out)到磁盘中,下次使用时再换入到内存中(Swap-in),这样程序就可以使用超过实际物理内存大小的地址空间了。

 

CPU 使用逻辑地址时,先判断这个逻辑地址对应的内容是否已装入主存。若已在主存中,则通过地址变换,CPU 可直接访问主存指示的实际单元;若不在主存中,则把包含这个字的一页或一段调入主存后再由 CPU 访问。若主存已满,则采用 替换算法 置换主存中的页。

虚拟存储器采用了和 Cache 类似的技术,将辅存中经常被访问的数据副本存放到主存中。但缺页 (或段)而访问辅存的代价很大,因此虚存机制采用 全相联映射,每个页可以存放到主存区域的任意一个空闲页位置。此外,当进行写操作时,不能每次写操作都同时写回磁盘,因而采用 回写法

3.7.2 页式虚拟存储器

页式虚拟存储器 以页为基本单位。虚拟空间与主存空间都被划分成同样大小的页,主存的页称为 实页页框,虚存的页称为 虚页。这样,一个逻辑地址可以分为两段:虚页号页内地址

 

虚页和实页之间采用全相联映射,所以从主存中依次查找要访问的虚页号比较困难。所以我们专门引入一个数据结构,用来保存虚页号和实页号的映射关系,这就是 页表。页表可以实现从逻辑地址到物理地址的转换。

1. 页表

页表是一张存放在主存中的虚页号和实页号的对照表,它记录程序的虚页调入主存时被安排在主存中的位置。每个程序都有自己的页表,页表一般长久地保存在内存中。

页表中的每一项,都包含以下几部分:

  • 有效位:也称 装入位,用来表示对应页面是否在主存,若为 1,则表示该虚页已从外存调入主存,此时页表项存放该页的物理页号;若为 0,则表示页面没有调入主存,此时页表项可以存放该页的磁盘地址。

  • 脏位:也称 修改位,用来表示页面是否被修改过,虚拟存储机制中采用回写策略,利用脏位可判断替换时是否需要写回磁盘。

  • 引用位:也称 使用位,用来配合替换策略进行设置,例如是否使用先进先出(FIFO)或近期最少使用(LRU)策略等。

 

CPU 执行指令时,需要先将逻辑地址转换为主存物理地址。每个进程都有一个 页表基址寄存器,存放该进程的页表首地址,然后根据逻辑地址高位部分的虚页号找到对应的页表项。若装入位为 1,则取出物理页号,和逻辑地址低位部分的页内地址拼接,形成物理地址;若装入位为 0,则说明缺页,需要操作系统进行 缺页处理。缺页时会由 CPU 的内存管理单元(MMU)发出中断,操作系统需要将相应的页从磁盘取回调入主存,并将物理页的地址填入页表中。

页式虚拟存储器的优点是:页的长度固定,页表简单,调入方便。缺点是:最后一页的零头无法利用而造成浪费,并且页不是逻辑上独立的实体,所以处理、保护和共享都不及段式虚拟存储器方便。

2. 快表(TLB)

有了虚拟存储器之后,CPU 在寻址时所生成的都是虚拟地址。于是 CPU 在取指或者执行访存指令的时候,都需要进行地址翻译,而每次地址翻译都要访问主存中的页表,会产生严重的开销。

依据程序执行的局部性原理,当 CPU 在一段时间内总是经常访问某些页时,若把这些页对应的页表项存放在 Cache 中,就可以不访问主存直接进行地址翻译了;这样明显能提高效率。

在 CPU 芯片中,加入一个专门存放最常访问的页表项的 Cache,就叫做 转址旁路缓存(Translation Lookaside Buffer,TLB),一般简称为 “快表”。TLB 实质上就是 “页表的 Cache”,其中存储了当前最可能被访问到的页表项,其内容是部分页表项的一个副本;所以 TLB 又被称为 页表缓存

 

相应地,把放在主存中的页表称为 慢表(Page)。 在地址转换时,先查找快表,若命中,则无须再访问主存中的页表(慢表)。

TLB 通常采用 全相联映射。每个 TLB 项由页表表项内容加上一个 TLB 标记字段以及有效位等标志位组成,TLB 标记用来表示该表项取自页表中哪个虚页号对应的页表项,其内容就是该页表项对应的虚页号。

 

3. 具有 TLB 和 Cache 的多级存储系统

TLB 和 Cache 都属于缓存,不过它们的用途不同:

  • TLB 用来保存最近经常访问的页表项,是对 地址映射 的缓存。

  • Cache 用来保存最近经常访问的主存块,是对 数据内容 的缓存。

所以对于一个有虚拟存储器的计算机系统,可以先通过 TLB 对逻辑地址的翻译进行加速,快速得到一个物理地址;然后再通过 Cache 的地址转换判断是否 Cache 命中,从而对数据的访问进行加速。

这样就将 Cache 和 TLB 结合起来,构成了多级存储系统。下面就是一个具有 2 路组相联映射 Cache 和 TLB 的多级存储系统;CPU 给出的是一个 32 位的逻辑地址,TLB 采用全相联映射,每一项都有一个比较器。

 

  • 查找时将虚页号与每个 TLB 标记同时进行比较,若有某一项相等且对应有效位为 1,则 TLB 命中,此时可直接通过TLB进行地址转换;若未命中,则 TLB 缺失,需要访问主存去査页表。

  • 图中所示是 两级页表方式,虚页号被分成 页目录索引页表索引 两部分,由这两部分得到对应的页表项,从而进行地址转换,并将相应表项调入TLB。若 TLB 已满,则还需要采用替换策略。

  • 完成由逻辑地址到物理地址的转换后,Cache 机构根据映射方式将物理地址划分成多个字段,然后根据映射规则找到对应的 Cache 行或组,将对应 Cache 行中的标记与物理地址中的高位部分进行比较,若相等且对应有效位为1,则 Cache 命中,此时根据块内地址取岀对应的字送 CPU。

查找时,快表和慢表也可以同步进行。若快表中有此虚页号,则能很快地找到对应的实页号,并使慢表的查找作废,从而就能做到虽采用虚拟存储器,但访问主存速度几乎没有下降。

在一个具有 Cache 和 TLB 的虚拟存储系统中,CPU —次访存操作可能涉及对 TLB、页表(Page)、Cache、主存和磁盘的访问。CPU 在访存过程中存在 3 种缺失情况:

① TLB 缺失:要访问页面的页表项不在 TLB 中;

② Page 缺失:要访问的页面不在主存中。

③ Cache 缺失:要访问的主存块不在 Cache 中;

需要注意,如果 TLB 命中,那么 Page 一定命中;如果 Page 缺失,那么 Cache 一定缺失。所以有如下一些组合情况:

 

  • 第 1 种情况下,无须访问主存,地址转换和访问数据都可以通过高速缓存完成;

  • 第 2 种和第 3 种情况都 需要访问一次主存,第 2 种是访问主存取数据,第 3 种是访问页表转换物理地址;

  • 第 4 种情况需要访问两次主存,访问页表转换物理地址一次、访存取数据一次;

  • 第 5 种情况就是 “缺页异常”,需要访问磁盘,并且至少访问两次主存。

Cache 缺失处理由硬件完成;缺页处理由软件完成,操作系统通过 “缺页异常处理程序” 实现;而 TLB 缺失既可以用硬件也可以用软件来处理。

3.7.3 段式虚拟存储器

在段式虚拟存储器中,将虚拟空间用 “” 进行分割;而段是按程序的逻辑结构划分的,各段的长度因程序而异。虚地址分为两部分:段号段内地址。虚地址到实地址之间的变换是由 段表 来实现的。段表的每行记录与某个段对应的段号、 装入位和段长等信息。由于段的长度可变,所以段表中要给出各段的起始地址与段的长度。

 

CPU 用逻辑地址访存时,先根据段号与段表基地址拼接成对应的段表项,再根据该段表项的装入位判断该段是否已调入主存(装入位为 “1”,表示该段已调入主存)。当已调入主存时,从段表读岀该段在主存的起始地址,与段内地址相加,得到对应的主存物理地址。

段式虚拟存储器的优点是,段的分界与程序的逻辑分界相对应,这使得程序易于编译、修改和保护,也便于多道程序共享;缺点是因为段长度可变,分配空间不便,容易留下碎片,造成浪费。

3.7.4 段页式虚拟存储器

把程序按逻辑块分段,段内再分页,主存空间也划分为大小相等的页,程序对主存的调入调出仍以 为基本单位,这样的虚拟存储器称为 段页式虚拟存储器。在段页式虚拟存储器中,每个程序对应一个 段表,每段对应一个 页表,段的长度必须是页长的整数倍,段的起点必须是某一页的起点。

 

虚地址分为 段号段内页号页内地址 3 部分。CPU 根据虚地址访存时,首先根据段号得到段表地址,然后从段表中取出该段的页表起始地址,与虚地址段内页号拼接,得到页表地址;最后从页表中取出实页号,与页内地址拼接成主存实地址。

段页式虚拟存储器的优点是,兼具页式和段式虚拟存储器的优点,可以按段实现共享和保护;缺点是在地址变换过程中需要两次查表,系统开销较大。

3.7.5 虚拟存储器与 Cache 的比较

相同点:

  • 目标都是为了提高系统性能,两者都有容量、速度、价格的梯度。

  • 都把数据划分为信息块,作为基本的传送单位,虚拟存储器系统的信息块更大。

  • 都有地址的映射算法、替换算法、更新策略等问题。

  • 依据局部性原理,应用“快速缓存”思想,将活跃的数据放在相对高速的部件中。

不同点:

  • Cache主要是为了提高系统速度,而虚拟存储器是为了解决主存容量不足的问题。

  • Cache由硬件实现,对所有程序员透明;虚拟存储器由操作系统和硬件共同实现,对应用程序员透明。

  • 在不命中时对性能的影响不同。因为 CPU 的速度约为 Cache 的 10 倍,而主存的速度为硬盘的 100 倍以上,因此虚拟存储器系统在不命中时对系统性能的影响更大。

  • CPU 与 Cache 和主存有直接通路,而辅存与 CPU 没有直接通路。在 Cache 不命中时,CPU 能和主存直接通信;而虚拟存储器系统在不命中时,须先将数据从硬盘调入主存,CPU 才能访问。

3.8 章节练习

一、单项选择题
  1. 【2010真题】下列有关 RAM 和 ROM 的叙述中,正确的是 ( )。

Ⅰ . RAM 是易失性存储器, ROM 是非易失性存储器 ​ Ⅱ . RAM 和 ROM 都采用随机存取方式进行信息访问 ​ Ⅲ. RAM 和 ROM 都可用作 Cache ​ Ⅳ. RAM 和 ROM 都需要进行刷新

A.仅Ⅰ 和Ⅱ B.仅Ⅱ 和Ⅲ C.仅Ⅰ 、 Ⅱ 和Ⅳ D.仅Ⅱ 、 Ⅲ和Ⅳ

答案:A

  1. 【2011真题】下列各类存储器中,不采用随机存取方式的是 ( )。

A. EPROM B. CDROM C. DRAM D. SRAM

答案:B

  1. 【2015真题】下列存储器中,在工作期间需要周期性刷新的是 ( )。

A. SRAM B. SDRAM C. ROM D. FLASH

答案:B

  1. 【2012真题】下列关于闪存(Flash Memory)的叙述中,错误的是 ( )。

A. 信息可读可写,并且读、写速度一样快

B. 存储元由 MOS 管组成,是一种半导体存储器

C. 掉电后信息不丢失,是一种非易失性存储器

D. 采用随机访问方式,可替代计算机外部存储器

答案:A

  1. 【2011真题】某计算机存储器按字节编址,主存地址空间大小为 64MB,现用 4MB×8 位的 RAM 芯片组成 32MB 的主存储器,则存储器地址寄存器 MAR 的位数至少是 ( )。

A. 22 位 B. 23 位 C. 25 位 D. 26 位

答案:D

要点:MAR 的位数跟主存地址空间有关,与主存实际容量无关。

  1. 【2010真题】假定用若干个 2K×4 位的芯片组成一个 8K×8 位的存储器,则地址 0B1FH 所在芯片 的最小地址是 ( )。

A. 0000H B. 0600H C. 0700H D. 0800H

答案:D

  1. 【2014真题】某容量为 256MB 的存储器由若干 4Mx8 位的 DRAM 芯片构成, 该 DRAM 芯片的地址引脚和数据引脚总数是 ( ) 。

A. 19 B. 22 C. 30 D. 36

答案:A

要点:DRAM 采用地址复用技术,地址线为正常的一半。

  1. 【2016真题】某存储器容量为 64KB,按字节编址,地址 4000H~5FFFH 为 ROM 区,其余为 RAM 区。若采用 8K× 4 位的 SRAM 芯片进行设计,则需要该芯片的数量是 ( )。

A. 7 B. 8 C. 14 D. 16

答案:C

  1. 【2018真题】假定 DRAM 芯片中存储阵列的行数为 r、列数为 c,对于一个2Kx1 位的 DRAM 芯片,为保证其地址引脚数最少,并尽量减少刷新开销,则 r、c的取值分别是 ( )。

A. 2048、1 B. 64、32 C. 32、64 D. 1、2048

答案:C

要点:DRAM 芯片采用地址复用技术,按行刷新。

  1. 【2017真题】某计算机主存按字节编址,由 4 个 64M × 8 位的 DRAM 芯片采用交叉编址方式构成,并与 宽度为 32 位的存储器总线相连,主存每次最多读写 32 位数据。若 double 型变量 x 的主存地址为 804 001AH,则读取 x 需要的存储周期数是 ( )。

A. 1 B. 2 C. 3 D. 4

答案:C

要点:多体低位交叉存储器可增大带宽,每个存储周期对所有芯片各读取一次;double 型变量占 8 个字节。

  1. 【2021真题】某计算机的存储器总线中有 24 位地址线和 32 位数据线,按字编址,字长为 32 位。若 00 0000H~3F FFFFH 为 RAM 区,则需要 512K x 8 位的 RAM 芯片数为 ( )。

A.8 B.16 C.32 D.64

答案:C

  1. 【2022真题】某内存条包含 8 个 8192 x 8192 x 8 位的 DRAM 芯片,按字节编址,支持突发 (burst) 传送方式,对应存储器总线宽度为 64 位,每个 DRAM 芯片内有一个行缓冲区 (row buffer)。 下列关于该内存条的叙述中,不正确的是 ( )。

A. 内存条的容量为 512 M B. 采用多模块交叉编址方式

C. 芯片的地址引脚为 26 位 D. 芯片内行缓冲有 8192 x 8 位

答案:C

要点:DRAM 芯片采用地址复用技术,地址引脚为正常的一半;行缓冲区的大小就是一行的大小。

  1. 【2013真题】下列选项中,用于提高 RAID 可靠性的措施有 ( )。

I.磁盘镜像 II. 条带化 III.奇偶校验 IV.增加 Cache机制

A.仅I、II B.仅I、III C.仅I、III和IV D.仅II、III和IV

答案:B

要点:RAID 通过条带化来实现并行读写;通过磁盘镜像和校验增加可靠性。

  1. 【2013真题】某磁盘的转速为 10000 转/分,平均寻道时间是 6ms,磁盘传输速率是 20MB/s,磁盘控 制器延迟为 0.2ms,读取一个 4KB 的扇区所需的平均时间约为 ( )。

A.9ms B.9.4 ms C.12 ms D. 12.4 ms

答案:B

要点:平均访问时间 = 平均寻道时间 + 平均等待时间 + 数据传输时间 + 控制器延迟 。

  1. 【2015真题】若磁盘转速为 7200 转/分,平均寻道时间为 8 ms,每个磁道包含 1000 个扇区,则访问一个扇区的平均存取时间大约是 ( )。

A. 8.1 ms B. 12.2 ms C. 16.3 ms D. 20.5 ms

答案: B

要点:平均访问时间 = 平均寻道时间 + 平均等待时间 + 数据传输时间 。

  1. 【2019真题】下列关于磁盘存储器的叙述中,错误的是 ( )。

A.磁盘的格式化容量比非格式化容量小 B.扇区中包含数据、地址和校验等信息

C.磁盘存储器的最小读写单位为一字节 D.磁盘存储器由磁盘控制器、磁盘驱动器和盘片组成

答案:C

要点:磁盘最小读写单位为一个扇区。

  1. 【2014真题】采用指令 Cache 与数据 Cache 分离的主要目的是 ( ) 。

A. 降低 Cache 的缺失损失 B. 提高 Cache 的命中率

C. 降低 CPU 平均访存时间 D. 减少指令流水线资源冲突

答案:D

  1. 【2017真题】某C语言程序段如下:

<span style="background-color:#f8f8f8"><span style="color:#333333">for(i=0; i<=9; i++)
{ 
    temp=1;
    for(j=0; j<=i; j++)temp * =a[j];
    sum + =temp;
}</span></span>

下列关于数组 a 的访问局部性的描述中,正确的是 ( )。

A.时间局部性和空间局部性皆有

B.无时间局部性,有空间局部性

C.有时间局部性,无空间局部性

D.时间局部性和空间局部性皆无

答案:A

  1. 【2015真题】假定主存地址为 32 位,按字节编址,主存和 Cache 之间采用直接映射方式,主存块大小为 4 个字,每字 32 位,采用回写(Write Back)方式,则能存放 4K 字数据的 Cache 的总容量的位数至少是 ( )。

A. 146K B. 147K C. 148K D. 158K

答案:C

要点:直接映射方式下,主存块号位数 m = 标记位数 t + Cache 行号位数 c ;

回写策略下,每个 Cache 行需要另加 1 位修改位(脏位);

Cache 行总位数 = 1位有效位 + 1位修改位 +(LRU位)+ 标记 + 数据。

  1. 【2022真题】若计算机主存地址为 32 位,按字节编址,某 Cache 的数据区容量为 32KB, 主存块大小为 64B, 采用 8 路组相联映射方式,该 Cache 中比较器的个数和位数分别为 ( )。

A. 8, 20 B. 8, 23 C. 64, 20 D. 64, 23

答案:A

要点:Cache 中比较器的个数就是组相联的路数 R,比较器的位数就是标记 t 的位数。

组相联映射方式下,主存块号位数 m = 标记位数 t + Cache 组号位数 q

  1. 【2016真题】有如下 C 语言程序段:

<span style="background-color:#f8f8f8"><span style="color:#333333">for(k=0; k<1000; k++)
    a[k] = a[k]+32;</span></span>

若数组 a 及变量 k 均为 int 型, int 型数据占 4B,数据 Cache 采用直接映射方式,数据区大小为 1KB、块大小为 16B,该程序段执行前 Cache 为空,则该程序段执行过程中访问数组 a 的 Cache 缺失率约为 ( )。

A. 1.25% B. 2.5% C. 12.5% D. 25%

答案:C

要点:循环内语句需要对 a[k] 访问两次,第一次未命中,并将其所在块调入主存;第二次命中;在该块中的后面三个元素的 6 次访问也都命中。

  1. 【2010真题】下列命中组合情况中,一次访存过程中不可能发生的是 ( )。

A. TLB 未命中, Cache 未命中, Page 未命中

B. TLB 未命中, Cache 命中, Page 命中

C. TLB 命中, Cache 未命中, Page 命中

D. TLB 命中, Cache 命中, Page 未命中

答案:D

要点:TLB 命中,Page 必命中;Page 缺失,Cache 必缺失。

  1. 【2019真题】下列关于缺页处理的叙述中,错误的是 ( )。 A.缺页是在地址转换时 CPU 检测到的一种异常 B.缺页处理由操作系统提供的缺页处理程序来完成 C.缺页处理程序根据页故障地址从外存读入所缺失的页 D.缺页处理完成后回到发生缺页的指令的下一条指令执行

答案:D

要点:缺页处理完成后回到发生缺页的指令继续执行。

  1. 【2020真题】下列关于 TLB 和 Cache 的叙述中,错误的是 ( )。

A.命中率都与程序局部性有关 B.缺失后都需要去访问主存

C.缺失处理都可以由硬件实现 D.都由 DRAM 存储器组成

答案:D

  1. 【2015真题】假定编译器将赋值语句 “ x=x+3; ” 转换为指令 “add xaddr, 3”,其中, xaddr 是 x 对应的存储单元地址。若执行该指令的计算机采用页式虚拟存储管理方式,并配有相应的 TLB,且 Cache 使用直写(Write Through)方式,则完成该指令功能需要访问主存的次数至少是 ( )。

A. 0 B. 1 C. 2 D. 3

答案:B

要点:直写方式下,每次写入都必须将数据同时写入 Cache 和主存。

  1. 【2013真题】某计算机主存地址空间大小为256 MB,按字节编址。虚拟地址空间大小为 4GB,采用页式存储管理,页面大小为 4KB,TLB (快表)采用全相联映射,有 4 个页表项,内容如下表所示

 

则对虚拟地址 03FF F180H 进行虚实地址变换的结果是 ( )。

A.015 3180H B.003 5180H C.TLB缺失 D.缺页

答案:A

要点:虚页号的位数,可以由虚页的个数推出;TLB 中保存的标记就是虚页号。

  1. 【2022真题】某计算机主存地址为 24 位,采用分页虚拟存储管理方式,虚拟地址空间大小为 4GB, 页大小为4KB, 按字节编址。 某进程的页表部分内容如下表所示。

 

当 CPU 访问虚拟地址 0008 2840H 时,虚-实地址转换的结果是 ( )。

A. 得到主存地址 02 4840H B. 得到主存地址 18 0840H

C. 得到主存地址 01 8840H D. 检测到缺页异常

答案:C

二、综合应用题
  1. 【2016真题】某计算机采用页式虚拟存储管理方式,按字节编址,虚拟地址为 32 位,物理地址为 24 位,页大小为 8KB;TLB 采用全相联映射; Cache 数据区大小为 64KB,按 2 路组相联方式组织,主存块大小为 64B。存储访问过程的示意图如下。

 

请回答下列问题。

(1)图中字段 A~G 的位数各是多少? TLB 标记字段 B 中存放的是什么信息?

(2)将块号为 4099 的主存块装入到 Cache 中时,所映射的 Cache 组号是多少?对应的 H 字段内容是什么?

(3) Cache 缺失处理的时间开销大还是缺页处理的时间开销大?为什么?

答案:

(1)页大小为 8KB,页内偏移地址为 13 位,故 A=B=32-13=19; D=13; C=24-13=11;

主存块大小为 64B,故 G=6。

2 路组相联,每组数据区容量有 64 B×2=128B,共有 64KB/128B=512 组,故 F=9;

E = 24-G-F = 24-6-9 = 9。

因而 A=19, B=19, C=11, D=13, E=9, F=9, G=6。

TLB 中标记字段 B 的内容是虚页号,表示该 TLB 项对应哪个虚页的页表项。

(2)块号 4099=00 0001 0000 0000 0011B,因此,所映射的 Cache 组号为 0 0000 0011B=3,对应的 H 字段内容为 0 0000 1000B。

(3) Cache 缺失带来的开销小,而处理缺页的开销大。 因为缺页处理需要访问磁盘,而 Cache 缺失只要访问主存。

  1. 【2018真题】某计算机采用页式虚拟存储管理方式,按字节编址。CPU 进行存储访问的过程如图所示。

 

根据上图回答下列问题。

(1) 主存物理地址占多少位?

(2) TLB 采用什么映射方式?TLB 用 SRAM 还是 DRAM 实现?

(3) Cache 采用什么映射方式?若 Cache 采用 LRU 替换算法和回写(Write Back)策略,则 Cache 每行中除数据(Data)、Tag 和有效位外,还应有哪些附加位?Cache 总容量是多少?Cache 中有效位的作用是什么?

(4) 若 CPU 给出的虚拟地址为 0008 C040H,则对应的物理地址是多少?是否在 Cache 中命中?说明理由,若 CPU 给出的虚拟地址为 0007 C260H,则该地址所在主存块映射到的 Cache 组号是多少?

答案:

(1)物理地址由实页号和页内地址拼接,因此其位数为 16+12 = 28;或直接可得 20+3+5 = 28。

(2) TLB 采用全相联映射,可以把页表内容调入任一块空 TLB 项中, TLB 中每项都有一个比较器,没有映射规则,只要空闲就行。 TLB 采用静态存储器 SRAM,读写速度快,但成本高,多用于容量较小的高速缓冲存器。

(3)图中可以看到, Cache 中每组有两行,故采用 2 路组相联映射方式。

因为是 2 路组相联并采用 LRU 替换算法,所以每行(或每组)需要 1 位 LRU 位;因为采用回写策略,所以每行有 1 位修改位(脏位),根据脏位判断数据是否被更新,如果脏位为 1 则需要写回内存。

28 位物理地址中 Tag 字段占 20 位,组索引字段占 3 位,块内偏移地址占 5 位,故 Cache 共有 23 = 8组,每组 2 行,每行有 25 = 32B;故 Cache 总容量为 8×2×(20+1+1+1+32×8) = 4464 位 = 558 字节。

Cache 中有效位用来指出所在 Cache 行中的信息是否有效。

(4)虚拟地址分为两部分:虚页号、页内地址;物理地址分为两部分:实页号、页内地址。

利用虚拟地址的虚页号部分去查找 TLB 表(缺失时从页表调入),将实页号取出后和虚拟地址的页内地址拼接,就形成了物理地址。

虚页号 008CH 恰好在 TLB 表中对应实页号 0040H(有效位为 1,说明存在),虚拟地址的后 3 位为页内地址 040H,则对应的物理地址是 0040040H。物理地址为 0040040H,其中高 20 位 00400H 为标志字段,低 5 位 00000B 为块内偏移量,中间 3 位 010B 为组号 2,因此将 00400H 与 Cache 中的第 2 组两行中的标志字段同时比较,可以看出,虽然有一个 Cache 行中的标志字段与 00400H 相等,但对应的有效位为 0,而另一 Cache 行的标志字段与 00400H 不相等,故访问 Cache 不命中。

因为物理地址的低 12 位与虚拟地址低 12 位相同,即为 0010 0110 0000B。根据物理地址的结构,物理地址的后八位 01100000B 的前三位 011B 是组号,因此该地址所在的主存映射到 Cache 组号为 3。

  1. 【2020真题】假定主存地址为 32 位,按字节编址,指令 Cache 和数据 Cache 与主存之间均采用 8 路组相联映射方式,直写(WriteThrough)写策略和 LRU 替换算法,主存块大小为 64B,数据区容量各为 32KB。开始时 Cache 均为空。请回答下列问题。 (1) Cache 每一行中标记(Tag)、LRU 位各占几位?是否有修改位? (2) 有如下 C 语言程序段:

<span style="background-color:#f8f8f8"><span style="color:#333333">for (k=0; k<1024 ;k++)
    s[k]=2*s[k];</span></span>

若数组 s 及其变量 k 均为 int 型,int 型数据占 4B,变量 k 分配在寄存器中,数组 s 在主存中的起始地址为0080 00C0H,则该程序段执行过程中,访问数组 s 的数据 Cache 缺失次数为多少?

(3) 若 CPU 最先开始的访问操作是选取主存单元 0001 0003H 中的指令,简要说明从 Cache 中访问该指令的过程,包括 Cache 缺失处理过程。

答案:

(1)主存块大小为 64B=26 字节,故主存地址低 6 位为块内地址, Cache 组数为 32KB/(64B×8) = 64=26, 故主存地址中间 6 位为 Cache 组号, 主存地址中高 32-6-6=20 位为标记;

采用 8 路组相联映射, 故每行中 LRU 位占 3 位;

采用直写方式,故没有修改位。

(2)因为数组s的起始地址最后 6 位全为 0, 故 s 位于一个主存块开始处,共占 1024×4B/64B=64 个主存块;

执行程序段过程中,每个主存块中的 64B/4B=16 个数组元素依次读、写 1 次, 因而对于每个主存块,总是第一次访问缺失,以后每次命中。

综上, 数组 s 的数据 Cache 访问缺失次数为 64 次。 (3) 0001 0003H = 0000 0000 0000 0001 0000 000000 000011B, 根据主存地址划分可知,组索引为 0, 故该地址所在主存块被映射到指令 Cache 第 0 组;

因为 Cache 初始为空,所有 Cache 行的有效位均为 0, 所以 Cache 访问缺失。此时,将该主存块取出后存入指令 Cache 第 0 组的任意一行,并将主存地址高 20 位(00010H)填人该行标记字段,设置有效位,修改 LRU 位, 最后根据块内地址 000011B 从该行中取出相应内容。

  1. 【2021真题】假设计算机 M 的主存地址为 24 位,按字节编址;采用分页存储管理方式,虚拟地址为 30 位,页大小为 4 KB;TLB 采用 2 路组相联方式和 LRU 替换策略,共 8 组。请回答下列问题。

(1) 虚拟地址中哪几位表示虚页号?哪几位表示页内地址?

(2) 已知访问 TLB 时虚页号高位部分用作 TLB 标记,低位部分用作 TLB 组号,M 的虚拟地址中哪几位是 TLB 标记?哪几位是 TLB 组号?

(3) 假设 TLB 初始时为空,访问的虚页号依次为 10、12、16、7、26、4、12 和 20,在此过程中,哪一个虚页号对应的 TLB 表项被替换?说明理由。

(4) 若将 M 中的虚拟地址位数增加到 32 位,则 TLB 表项的位数增加几位?

答案:

注意:对于本题的 TLB,需要采用处理 Cache 的方式求解。

(1)按字节编址, 页面大小为 4 KB=212B,所以页内地址为 12 位。 虚拟地址中高 30-12=18 位 表示虚页号, 虚拟地址中低 12 位表示页内地址。

(2)TLB 采用 2 路组相联方式,共 8=23 组,用 3 位来 标记组号。 虚拟地址(或虚页号)中高 18-3=15 位为 TLB 标记, 虚拟地址中随后 3 位(或虚页号中低 3 位)为 TLB 组号。

(3)虚页号 4 对应的 TLB 表项被替换。 因为虚页号与 TLB 组号的映射关系为

TLB 组号=虚页号 mod TLB 组数=虚页号 mod 8,

因此,虚页号 10,12,16, 7,26,4,12,20 映射到的 TLB 组号依次为 2,4,0,7,2,4,4,4。

TLB 采用 2 路组相联方式, 从上述映射到的TLB 组号序列可以看出,只有映射到 4 号组的虚页号数量大于 2, 相应虚页号依次是 12,4,12 和 20。根据 LRU 替换策略, 当访问第 20 页时, 虚页号 4 对应的TLB 表项被替换出来。

(4)虚拟地址位数增加到 32 位时, 虚页号增加了 32-30=2 位, 使得每个TLB 表项中的标记字段增加 2 位, 因此,每个TLB 表项的位数增加 2 位。

第四章 指令系统

4.1 本章大纲要求与核心考点

4.1.1 大纲内容

(一)指令系统的基本概念

(二)指令格式

(三)寻址方式

(四)数据的对齐和大/小端存放方式

(五)CISC 和 RISC 的基本概念

(六)高级语言程序与机器级代码之间的对应

  1. 编译器、汇编器和链接器的基本概念

  2. 选择结构语句的机器级表示

  3. 循环结构语句的机器级表示

  4. 过程(函数)调用对应的机器级表示

4.1.2 核心考点

本章内容是也是考研考察的一个重点,一般会与第二章、第三章和第五章进行结合,往往以综合应用题的形式出现。而常见的寻址方式很容易以单项选择题的形式进行考察。

需要重点掌握的内容包括:

  • 指令的格式及相关概念,定长与扩展操作码格式。

  • 常见的寻址方式、特点及有效地址的计算。

  • 常用的汇编指令,过程调用、选择语句和循环语句的机器级表示,标志位及其使用。

  • CISC 和 RISC 的基本概念,CISC 和 RISC 的比较。

4.1.3 真题分布
考点考查次数
单项选择题综合应用题
常见寻址方式95
指令格式46
程序的机器级代码表示02
CISC 和 RISC11

4.2 指令和指令格式

计算机是通过连续执行一条条机器语言语句,而实现自动工作的。习惯上就把每一条机器语言的语句称为 指令,而把全部机器指令的集合称为机器的 指令系统

  • 指令(机器指令)是指计算机执行某种操作的机器语言命令。

  • 一台计算机的所有指令的集合构成该计算机的 指令系统,也称 指令集

指令系统是计算机的主要属性,位于硬件和软件的交界面上。

4.2.1 指令的基本格式

指令由 操作码地址码(操作数地址) 两部分组成。

 

  • 操作码:指出指令执行什么操作和具有何种功能。例如,指出是算术加运算,还是减运算;是程序转移,还是返回操作。

  • 地址码:指岀被操作的信息(指令或数据)的地址,包括参加运算的一个或多个操作数的地址、运算结果的保存地址、程序的转移地址、被调用的子程序的入口地址等。

4.2.2 指令字长

指令字长 是指一条指令中所包含的二进制代码的位数,它取决于操作码的长度、操作数地址的长度和操作数的个数。不同机器的指令字长是不同的,指令字长通常取 8 的整数倍。

按照字长是否可变,又可以将指令系统分为 定长指令字结构变长指令字结构

1. 定长指令字结构

在一个指令系统中,若所有指令的长度都是相等的,称为 定长指令字结构(定字长指令)

早期的计算机都是定长指令字结构,而且指令字长、机器字长、存储字长全部相等;这样每次访问某个存储单元,就可以取出一个完整的指令或者数据。

 

定字长指令的执行速度快,控制简单。精简指令系统计算机(Reduced Instruction Set Compter,RISC)采用定字长指令。

2. 变长指令字结构

随着计算机的发展,存储容量不断增大,要求处理的数据类型也越来越多,指令字长发生了很大变化。一台计算机的指令系统可以采用不同长度的指令,比如单字长指令、多字长指令。

若指令系统中各种指令的长度随指令功能而异,就称为 变长指令字结构(变字长指令)。由于主存是按字节编址的,所以指令字长多为字节的整数倍。

 

控制变长指令的电路会比较复杂,而且多字长指令需要多次访问主存才能取出一条完整指令,导致了 CPU 速度降低。

原则上讲,短指令比长指令好,因为短指令能节省存储空间,提高取指令的速度,但也有很大的局限性。长指令占用更多的存储空间,取指令的时间也会更长,但其能扩大寻址范围或可带多个操作数。 如果长、短指令在同一机器中混合使用,就会给指令系统带来很大的灵活性。为了提高指令运行速度、节省存储空间,一般会尽可能地把常用的指令设计成单字长或者短字长的格式。

复杂指令系统计算机 (Complex Instruction Set Compter,CISC)采用变字长指令。

4.2.3 地址码

地址码 用来指出指令操作涉及到的数据或指令具体保存的位置,可以包括:

  • 源操作数的地址(一个或两个)

  • 操作结果的地址

  • 下一条指令的地址

这里的 “地址” 一般指主存地址,也可以是寄存器的地址,甚至可以是 I/O 设备的地址。

我们以主存地址为例,分析一下地址码的分配。根据地址码字段的数量不同,可以把指令再做分类:

1. 四地址指令

地址码字段最多的指令,可以包含所有的信息,共有四个地址字段:

 

其中 OP 是操作码;地址码 A~1~ 为第一操作数地址,A~2~ 为第二操作数地址,A~3~ 为结果地址,A~4~ 为下一条指令的地址。

这一指令完成的操作可以写作:

$$
(A_1)OP(A_2)→A_3
$$

对 A~1~ 和 A~2~ 中存放的数据执行 OP 操作,得到的结果填入 A~3~ ,然后再跳转到 A~4~ 位置执行下一条指令。后续指令地址可以任意填写。

 

假设采用定字长指令结构,指令字长为 32 位,操作码 OP 固定为 8 位。这样,每个地址码就应该占据 6 位,那么能够寻址的地址范围为 2^6^ = 64。

这里如果都是主存地址,那么完成这样的一条四地址指令,需要访问主存 4 次(取指令 → 取操作数 A~1~ → 取操作数 A~2~ → 结果写入 A~3~)。

2. 三地址指令

程序中大多数指令都是顺序执行的,而程序计数器 PC 存放了当前要执行指令的地址,每次执行完会自动计算下一条指令的地址(“加 1”);所以一般并不需要在指令中直接给出下一条指令的地址,A~4~ 可以省去,这样就得到了三地址指令。

 

跟四地址指令一样,它也可以完成操作:

$$
(A_1)OP(A_2)→A_3
$$

后续的指令地址隐含在 PC 中。如果指令字长仍为 32 位、操作码为 8 位,那么每个地址码也可以占据 8 位,能够寻址的地址范围为 2^8^ = 256。当然,完成这样一条指令同样需要 4 次访存。

3. 二地址指令

如果将操作的结果直接保存在某个操作数地址对应的存储单元,就又可以在指令中节省一个地址 A~3~,这样就得到了二地址指令。

 

它表示执行的操作为:

$$
(A_1)OP(A_2)→A_1
$$

这里 A~1~ 既代表第一个源操作数的地址,也代表本次运算结果的存放地址。这种情况下,完成这一条指令同样需要 4 次访存(取指令 → 取操作数 A~1~ → 取操作数 A~2~ → 结果写入 A~1~)。

由于访存开销比较大,计算机运行过程中,可以将中间计算结果暂存在 CPU 的寄存器(如 ACC)中,这样就节省了最后写入主存的过程。这时的二地址指令表示执行的操作为:

$$
(A_1)OP(A_2)→ACC
$$

这种情况下,完成指令只需要 3 次访存。如果指令字长仍为 32 位、操作码为 8 位,那么每个地址码可以占据 12 位,能够寻址的地址范围为 2^12^ = 4K。

4. 一地址指令

自然可以想到,如果把二地址指令的两种情况结合起来,把某个操作数放在寄存器中,同时计算结果也放在寄存器中,那么就又可以节省一个地址了;这就是一地址指令。

 

它表示执行的操作为:

$$
(ACC)OP(A_1)→ACC
$$

这里第一个操作数存放在 ACC 中,第二个操作数地址为 A~1~,运算结果仍存放在 ACC 中。这样,完成指令只需要 2 次访存(取指令 → 取操作数 A~1~)。

当然,上面所讨论的都是需要两个操作数的情况;一些特殊的操作,可能只需要一个操作数,比如按位取反、自增自减操作。这时表示执行的操作为:

$$
OP(A_1)→A_1
$$

因为得到的结果还要再写回到对应地址中,所以需要 3 次访存(取指令 → 取操作数 A~1~ → 结果写入 A~1~)。如果指令字长仍为 32 位、操作码为 8 位,那么地址码可以占据 24 位,能够寻址的地址范围为 2^24^ = 16M。

5. 零地址指令

在指令系统中,还有一种指令是没有地址码的,这就是零地址指令。零地址指令中只有操作码也可以分两种情况:

  • 没有操作数的指令,比如空操作(NOP)、停机(HLT);

  • 有一个隐含操作数的指令,比如子程序返回(RET)、中断返回(IRET),这类操作利用了栈数据结构,操作数的地址就隐含在堆栈指针(SP)中。

4.2.4 操作码

操作码的位数就代表了机器的操作种类,也就是机器指令集中的指令条数。操作码的长度可以是固定的,也可以是变化的。根据操作码长度是否可变,可以分为 定长操作码变长操作码 两种指令格式。

1. 定长操作码指令格式

定长操作码指令,是在指令字的最高位部分分配固定的若干位(定长)表示操作码。一般 n 位操作码字段的指令系统最大能够表示 2^n^ 条指令。

 

这种指令格式便于计算机硬件设计,指令译码和识别时间短,广泛应用于字长较长的、大中型计算机和超级小型计算机以及 RISC(Reduced Instruction Set Compter,精简指令集计算机)中。当计算机字长为 32 位或更长时,这是常规做法。

2. 扩展操作码指令格式

可变长度操作码指令,是指全部指令的操作码字段的位数不固定,且分散地放在指令字的不同位置。显然,这将增加指令译码和分析的难度,使控制器的设计复杂化。

常见的可变长度操作码是 扩展操作码。在指令字长一定的条件下,操作码的长度随地址数的减少而增加,不同地址数的指令可以具有不同长度的操作码。

在设计扩展操作码指令时,需要注意两点:

  • 不允许短操作码是长操作码的前缀;

  • 各指令的操作码一定不能重复。

下面就是一种扩展操作码的示例。指令字长为 16 位,前 4 位为基本操作码字段 OP,另有 3 个 4 位的地址字段分别为 A~1~、A~2~、A~3~。

 

4 位基本操作码若全部用于三地址指令,则有 16 条。如果考虑到需要扩展到较少地址的指令,则将三地址指令减为 15 条,1111 留作扩展操作码使用;二地址指令为 15 条,1111 1111留作扩展操作码使用;一地址指令为 15 条,1111 1111 1111 留作扩展操作码使用;零地址指令为 16 条。

除了这种安排以外,还有其他多种扩展方法,如形成 15 条三地址指令、12 条二地址指令、63 条一地址指令和 16 条零地址指令,共 106 条指令;或者 12 条三地址指令、61 条二地址地址、47 条一地址指令和 16 条零地址指令。

 

在通常情况下,对使用频率较高的指令分配较短的操作码,对使用频率较低的指令分配较长的操作码,从而尽可能减少指令译码和分析的时间。

4.2.5 指令的操作数类型和操作类型
1. 操作数类型

计算机中常见的操作数类型有数字、地址、字符、逻辑数据等。

  • 数字:计算机中常见的数字有定点数、浮点数和十进制数,它们分别用不同格式的二进制编码来表达。

  • 地址:本质上也是一种数据,很多时候需要对操作数地址进行计算,可以认为是一个无符号整数。

  • 字符:文本或者字符串也是一种常见的数据形式。计算机不能直接存储和传送数据,需要按一定规则对字符进行编码;广泛使用的是 ASCII 编码。

  • 逻辑数据:除算术运算外,计算机还经常需要进行逻辑运算,此时二进制码中的 0 和 1 就应该被看作逻辑上的 “假” 和 “真”,参与逻辑与或非运算。这样的数据就是 “逻辑数据”。

2. 指令的操作类型

设计指令系统时必须考虑应提供哪些操作类型,指令操作类型按功能可分为以下几种。

(1)数据传送

数据传送指令通常有寄存器之间的数据传送(MOV)、从内存单元读取数据到 CPU 寄存器 (LOAD)、从CPU 寄存器写数据到内存单元(STORE)等。

(2)算术和逻辑运算

这类指令主要有加(ADD)、减(SUB)、比较(CMP)、乘(MUL)、除(DIV)、加1 (INC)、减1 (DEC)、与(AND)、或(OR)、取反(NOT)、异或(XOR)等。

(3)移位操作

移位操作指令主要有算术移位指令、逻辑移位指令、循环移位指令等。移位操作经常被用来替代简单的乘/除法运算。

(4)转移操作

转移操作指令主要有无条件转移(JMP)指令、条件转移(BRANCH)指令、调用(CALL)指令、 返回(RET)指令、陷阱(TRAP)指令等。

① 无条件转移指令

在任何情况下都执行转移操作,可以直接把程序转移到下一条要执行指令的地址。

例如:“ JMP X ”,就是无条件跳转到地址 X 去执行下一条指令。

② 条件转移指令

条件转移指令仅在满足特定条件时才执行转移操作,转移条件一般是一个或几个标志位的值。这些标志位是某些操作的结果,也叫做 “条件码”。例如:

  • 零标志位(ZF),当结果为 0 时,ZF = 1;

  • 负标志位(SF),结果为负时,SF = 1;

  • 溢出标志位(OF),结果溢出时,OF = 1;

  • 进位标志位(CF),最高位有进位时,CF = 1;

  • 奇偶标志位(PF),结果为偶数时,PF = 1

这样,

“ JZ X ”,就表示判断结果是否为 0:如果为 0 就跳转到 X,如果不为 0 则继续顺序执行;

“ JO Y ”,就表示判断结果是否溢出:如果溢出就跳转到 Y,如果没有溢出则继续顺序执行;

“ JC Z ”,就表示判断结果最高位是否有进位:如果有进位就跳转到 Z,如果没有则继续顺序执行。

③ 调用和返回指令

调用指令可以实现从一个程序到另一个程序的转移操作。

程序中,有些特定的程序段会被反复调用。为了避免重复编写,可以将这些程序段设定为 子程序;需要调用时,只需要执行子程序调用指令就可以了。还有一些子程序是系统提供的,用户也可以直接调用。

调用指令(CALL)一般与 返回指令(RETURN)配合使用。CALL 用来从当前程序位置转移至子程序的入口,RETURN 则用于子程序执行完后重新返回到原程序的调用点。

 

调用指令和转移指令的区别:执行调用指令时必须保存下一条指令的地址(返回地址),当子程序执行结束时,根据返回地址返回到主程序继续执行;而转移指令则不返回执行。

④ 陷阱(Trap)指令

陷阱其实是一种意外事故引发的中断。比如,电压不稳定、I/O 设备发生故障、用户使用未定义的指令、除数为 0 等等各种意外事件,都会导致计算机不能继续正常工作。此时计算机就发出陷阱信号,暂定当前程序的执行,转入故障处理程序进行相应的处理。

计算机的陷阱指令一般不提供给用户使用,而是作为隐指令,在出现故障时由 CPU 自动产生并执行。

(5)输入输出操作

对于 I/O 单独编址的计算机,通常设有 I/O 指令。这类指令用于 CPU 与外部设备交换数据或传送控制命令及状态信息。

(6)其它操作

计算机中还有一些通用的控制操作,比如等待(WAIT)、停机(HLT)、空操作(NOP)、开关中断、置条件码等,都有相应的指令来完成。

4.3 寻址方式

寻址方式是寻找指令或操作数有效地址的方式,也就是指确定本条指令的数据地址,以及下一条将要执行的指令地址的方法。

寻址方式分为指令寻址和数据寻址两大类。

4.3.1 指令寻址

指令寻址是指寻找下一条将要执行的指令地址。指令寻址方式有两种:一种是顺序寻址方式,另一种是跳跃寻址方式。

  • 顺序寻址:可通过 程序计数器(PC)加1,自动形成下一条指令的地址。

  • 跳跃寻址:通过 转移类指令 实现。跳跃寻址是指下一条指令的地址码不是由 PC 给出的,而是本条指令给出的。

 

注意:这里所说的 “ PC 加1 ” 并不是 PC 一定会加 “1” 这个数值,而是要加当前指令占据的地址长度,从而总能得到下一条指令的地址;例如,如果机器按字节编址,当前指令字长是 4 个字节,那么执行这条指令后 PC = PC + 4。

4.3.2 数据寻址

数据寻址是指确定本条指令中所有操作数的地址,即寻找指令要操作的数据的地址。

指令中的地址字段并不一定代表操作数的真实地址,称为形式地址(A)。结合形式地址和寻址方式,可计算出操作数在存储器中的真实存储地址,这一地址称为有效地址(EA)。

由于地址字段表达的含义不同,可以有多种不同的寻址方式:比如 直接寻址间接寻址寄存器寻址 等。此外,如果通过将某个寄存器内容与一个形式地址相加而生成有效地址,这种方式称为 偏移寻址。偏移寻址又包括基址寻址变址寻址相对寻址

数据寻址方式的种类较多,通常在指令中设一个字段,用来指明属于哪种寻址方式。由此可得指令的格式如下:

 

 

操作码的位数决定了指令的条数,寻址特征和形式地址共同决定了可寻址的范围。

  • 若为立即寻址,则形式地址的位数决定了数的范围。

  • 若为直接寻址,则形式地址的位数决定了可寻址的范围。

  • 若为寄存器寻址,则形式地址的位数决定了通用寄存器的最大数量。

  • 若为寄存器间接寻址,则寄存器字长决定了可寻址的范围。

下面是常见数据寻址方式的详细介绍。

1. 立即寻址

指令的地址字段指出的不是操作数的地址,而是操作数本身,称为 立即寻址,又称为 立即数寻址。 数据采用补码形式存放。

 

上面图中 # 表示立即寻址特征,A 就是操作数本身。

  • 优点:指令在执行阶段不访问主存,指令执行速度快。

  • 缺点:A的位数限制了立即数的范围,只适合操作数较小的情况。

2. 直接寻址

指令格式的地址字段中直接指出操作数在内存中的地址,就称为 直接寻址。即

$$
EA = A
$$

 

  • 优点:指令简单,不需要专门计算操作数的地址,指令在执行阶段仅访问一次主存。

  • 缺点:A 的位数决定了操作数的寻址范围,操作数的地址不易修改。

3. 间接寻址

间接寻址 是相对于直接寻址而言的,是指指令的地址字段给出的形式地址不是操作数的真正地址,而是操作数地址所在的存储单元地址;也就是 “地址的地址”,即

$$
EA = (A)
$$

这里用(A)来表示地址为 A 的存储单元所存放的数据。

 

间接寻址也可以分为多次进行,简称 多次间址。对于两次间接寻址,地址 A 存储的内容 A~1~ 还不是有效地址 EA,地址 A~1~ 对应的存储单元所存储的内容才是 EA。这时可以用存储字的首位来标记间接寻址是否结束:存储字首位为 “1” 时,说明还需要继续寻址;为 “0” 时,寻址结束,当前存储字存放的就是 EA。

  • 优点:可扩大寻址范围(有效地址 EA 的位数大于形式地址 A 的位数);便于编制程序。

  • 缺点:指令在执行阶段要多次访存(一次间址需两次访存,多次间址需多次访存)。

4. 隐含寻址

隐含寻址 是指指令字中不直接给出操作数的地址,而是隐含在某个寄存器中(通过操作码表示)。比如,一地址指令中,对于加法操作就可以只给出一个操作数的地址,而把另一个操作数放在 ACC 中;这时 ACC 就是另一个操作数的地址。

 

因为隐含寻址可以省去指令字中的一个地址,所以这种方式可以缩短指令字长,在计算机的指令集中被广泛使用。

5. 寄存器寻址

寄存器寻址 的指令在执行时所需的操作数来自寄存器,运算结果也写回寄存器;地址码字段直接指出了寄存器的编号,即

$$
EA = R_i
$$

这样,指令执行期间不需要访问主存,减少了执行时间;而且计算机中寄存器数量是有限的,所以地址字段只需要用很少的二进制位指明寄存器编号即可,节省了存储空间。因此寄存器寻址在计算机被广泛应用。

 

  • 优点:指令在执行阶段不访问主存,只访问寄存器,指令字短且执行速度快。

  • 缺点:寄存器的价格昂贵,且数量有限。

6. 寄存器间接寻址

如果寄存器中不是直接给出操作数,而是操作数的内存地址,那么就称为 寄存器间接寻址。即

$$
EA = (R_i)
$$

这是寄存器寻址和间接寻址的结合。跟寄存器寻址相比,指令的执行阶段还需要访问主存;跟间接寻址相比,则可以少一次对主存的访问。

 

  • 优点:获得操作数地址的速度较快;寄存器编号较短,可有效减少操作数字段的位数。

  • 缺点:寄存器数量有限;指令的执行阶段需要访问主存(因为操作数在主存中)。

7. 基址寻址

基址寻址 是一种偏移寻址的方式,需要设有基址寄存器 BR ;将基址寄存器 BR 的内容(基地址)加上指令中的形式地址,就可以形成操作数的有效地址。即

$$
EA = (BR) + A
$$

其中基址寄存器既可采用专用寄存器,也可采用通用寄存器。采用专用基址寄存器 BR ,使用时不用明确指出,只需要指令中的寻址特征反映出是基址寻址即可,这是 隐式 基址寄存器;对应地,如果采用通用寄存器,则需要用户明确指出用哪个寄存器作为基址寄存器,这是 显式 基址寄存器。

 

  • 优点:可扩大寻址范围;有利于多道程序设计和浮动程序编制。

  • 缺点:偏移量(形式地址 A)的位数较短。

8. 变址寻址

变址寻址 和基址寻址极为相似,需要设有变址寄存器 IX;将变址寄存器 IX 的内容加上指令中的形式地址,就可以形成操作数的有效地址。即

$$
EA = (IX) + A
$$

只要变址寄存器的位数足够,也可以扩大操作数的寻址范围。变址寄存器同样可以采用隐式和显式两种方式。

 

不过从本质上讲,变址寻址和基址寻址还是有较大区别的。

  • 基址寄存器是面向 操作系统 的,主要用于为程序或数据分配存储空间,其内容由操作系统或管理程序确定,在程序的执行过程中其值不可变,而指令字中的 A 是可变的;

  • 变址寄存器的内容是由 用户设定 的,在程序执行过程中其值可变,而指令字中的 A 是不可变的。变址寻址主要用于处理数组问题。

例如,某个数组 a 内有 N 个数据元素,在主存中存放的首地址为 D。那么如果要对数组所有元素求和,可以通过直接寻址的方式依次叠加得到结果,写成汇编语言程序如下:

 

随着 N 的增大,程序的指令条数会越来越多,占据的存储空间也越来越大。

而如果使用变址寻址,只需要将数组元素的索引下标放入变址寄存器中,每次改变变址寄存器的内容(加 1),就能用同样的指令 “ ADD X, D ” 处理所有所有数的相加了。

 

利用一个条件转移指令(BNE),当变址寄存器 X 中内容还没有增大到 N 时,就跳转回 M 处再次执行相同的指令;当增大到 N 时就结束,得到最后结果。这样,不论 N 取多大,这段程序都只需用 7 条指令,所占存储单元大大减少。这其实就是 “循环” 的实现思路。

  • 优点:可扩大寻址范围;在循环体中将 A 设为数组初始地址,可实现数组功能;适合编制循环程序。

9. 相对寻址

相对寻址 也是偏移寻址的一种,它所基于的是 程序计数器 PC 。将 PC 的内容加上指令中的形式地址,就形成操作数的有效地址,即

$$
EA = (PC) + A
$$

相对寻址通常用于转移类指令,转移后的的目标地址与当前指令有一段距离,称为 相对位移量;这里就是形式地址 A ,所以 A 也称为 位移量。A 的位数决定了操作数的寻址范围,可正可负,用补码表示。

 

相对寻址的最大特点是转移的目标地址不固定,可以随 PC 的值变化;这样,无论程序加载到主存的哪段区域,都以正确运行,对于编写浮动程序非常有利。例如,之前计算数据和的程序,跳转的目标地址固定为 M;如果程序的首地址发生了改变,M 也会变化。可以将条件转移指令改为相对寻址:

 

这样,无论程序浮动到哪一地址空间,都可以正常运行了。另外,相对寻址也可以和间接寻址结合使用。

  • 优点:便于程序浮动,广泛应用于转移指令。

10. 堆栈寻址

堆栈寻址 要求计算机中设有堆栈。堆栈既可以用寄存器组来实现,称为 硬堆栈;也可以利用主存的一部分空间作为堆栈,称为 软堆栈

以软堆栈为例,可以用一个 堆栈指针 SP (Stack Pointer)指出栈顶地址,也可以用 CPU 中的寄存器作为 SP。根据栈数据结构的特点,操作数只能在栈顶指针指向的存储单元里存取。

 

 

可以看出,堆栈寻址也是一种 隐含寻址,操作数的地址被隐含在了 SP 中。而从本质上看,把 SP 看作存放有效地址的寄存器,堆栈寻址就是一种 寄存器间接寻址

堆栈有 进栈(PUSH)出栈(POP)两种操作:

  • 进栈(PUSH A):(SP)- 1 → SP;(ACC)→ M [ (SP) ]

  • 出栈(POP A): (M [ (SP) ])→ ACC;(SP)+ 1 → SP

上面讨论的,都是主存按字编址的情况。如果主存按字节编址,则每次进出栈时 SP 的变化 Δ 会受到存储字长的影响;如果存储字长为 2 字节(16 位)则 Δ = 2,为 4 字节则 Δ = 4。


下表列出了所有寻址方式、有效地址及访存次数的简单总结(不含取本条指令的访存)。

寻址方式有效地址 EA访存次数
立即寻址不需要0
直接寻址EA = A1
间接寻址EA = (A)2(一次间址)
隐含寻址隐含在寄存器中0
寄存器寻址EA = Ri0
寄存器间接寻址EA = (Ri)1
基址寻址EA = (BR) + A1
变址寻址EA = (IX) + A1
相对寻址EA = (PC) + A1
堆栈寻址EA = (SP) - 1 (入栈) 或 EA = (SP) (出栈)0(硬堆栈)或 1(软堆栈)

4.4 CISC 和 RISC 的基本概念

指令集的不同会导致一个处理器的基础结构不同。

最早的 CPU 出现在 20 世纪 70 年代,当时的集成电路技术制约了一块芯片上能实现什么,所以它们的指令集都非常有限;以 8 位 CPU 为主,Intel 8080、MOS 6502、MC 6800 就是其中的代表。之后随着超大规模集成电路技术的发展,微处理器发展得非常迅速,指令集也越来越复杂;并且出现了将处理器、RAM、ROM 和 I/O接口等计算机基本部件集成到一个芯片上的微控制器(MCU),这就是 单片机,Intel 的 MCS-51 系列就是其中的代表。

   

   

现代计算机按照处理器的 指令集架构(Instruction Set Architecture,ISA)主要可以分为两种:

  • CISC(Complex Instruction Set Computer,复杂指令集计算机)

  • RISC(Reduced Instruction Set Computer,精简指令集计算机)

面对越来越多的需求,计算机需要完成的任务越来越重,对计算机性能的要求也越来越高。关于如何提升计算机性能,两种指令集架构代表了不同的思路。

4.4.1 CISC

CISC 通过设置更多、更复杂的指令来实现更多的功能,这样就可以减少运行程序所需的指令数,依靠硬件提升运行速度。

这种架构的代表公司就是 Intel,它在 1978 年推出了著名的 16 位微处理器 8086,此后又推出了 80286 和 32 位的 80386、80486 以及 奔腾(Pentium)处理器,因而这一系列的处理器都被称为 “ x86 架构 ”。如今已经进入 64 位时代,这一家族采用的指令集架构称为 “ x86-64 ” 或简称 “ x64 ”,代表就是酷睿(Core)处理器。AMD 公司生产的 CPU 主要也是 x86/64 架构。

   

CISC 的主要特点如下:

  • 指令数目庞大且复杂。Intel 描述全套指令的文档有 1200 多页;

  • 指令字长是可变的。x86-64 的指令长度可以是 1 ~ 15 个字节;

  • 每个指令可以执行若干简单操作,例如存储器读取、存储、计算操作等,因此很多指令都能进行访存操作,可以对内存中的操作数直接进行算术和逻辑运算;

  • 寻址方式丰富。内存中操作数的指示符可以有各种组合,包括偏移量、基址和变址寄存器以及伸缩因子;

  • 逻辑控制电路复杂,寄存器较少,使用主存中的软堆栈来实现堆栈寻址;

  • 对机器级程序来说,实现细节是不可见的,全部由硬件完成。

因此,CISC 有着非常明显的优缺点。

  • 优点:

    • 运行程序所需的指令数少,有效提升性能;

    • 更加依赖硬件实现功能,编写软件代码较为简单。

  • 缺点:

    • 指令执行时间差异很大,复杂的指令需要若干时钟周期才可以实现;

    • 指令的使用频率差异很大,很多复杂指令的使用率并不高;

    • 有些指令非常复杂,以至于无法通过组合逻辑电路直接完成,所以需要采用 微程序控制

    • 编译器能做的优化有限。

4.4.2 RISC

庞大的指令系统设计起来非常复杂,研制周期变得很长,成本耗费巨大。而且对传统 CISC 的测试发现,典型程序中 80% 的语句只用到了系统中 20% 的指令,这被称为 80 - 20 规律。

 

于是人们开始了对指令系统合理性的研究,试图通过 “做减法” 来从另一个角度提升效率,这样就产生了 RISC。

RISC 的主要思路是减少指令种类、简化指令功能,通过降低单个指令的执行周期数(CPI)来提高 MIPS,从而提升运行速度。

由于简化了指令集,CPU 芯片就不需要太大的空间来制作逻辑控制电路,而可以加入更多的寄存器,这样就可以让数据运算更快。基于这样的想法,IBM 公司开发出了第一代 RISC 架构计算机;与此同时,斯坦福大学的 RISC 研究课题 MIPS,考虑到了对处理器流水线的优化,研究结果转化成了后来 MIPS 公司的 R 系列产品。

1985 年,Acom 公司设计出了基于 RISC 指令集的 32 位计算机,简称 ARM(Acorn RISC Machine)。1990 年,Acom 改组为 ARM 公司,专门研发芯片架构、出售芯片技术授权,它的指令集架构就被称为 “ ARM 架构 ”。由于 ARM 架构低功耗、低成本的特点,在嵌入式处理器中得到了非常广泛的应用:智能手机、智能汽车、智能家居等各种领域都可以看到 ARM 架构处理器的身影。

     

如今 RISC 架构已经发展到第五代,称为 RISC-V,这是一个完全开源的指令集架构,采用宽松的 BSD 协议,企业可以完全自由免费使用,同时也容许企业添加自有指令集进行拓展。

RISC 的主要特点有:

  • 指令数量比 CISC 要少得多;只使用频度较高的简单指令,通过简单指令的组合实现复杂指令功能。早期的 RISC 指令通常少于 100 个;

  • 指令字长是固定的。早期的 RISC 通常将所有的指令都编码为 4 个字节;

  • 允许访存的指令只有 load 和 store,这被称为 load / store 体系结构。因此只能对寄存器中的操作数进行算术和逻辑运算,不能直接对内存中的操作数进行运算;

  • 寻址方式简单,一般所有的 load / store 都通过寄存器中的内容和指令字段中的偏移量来实现;

  • 逻辑控制电路比较简单,有大量的通用寄存器,可以使用硬堆栈来实现堆栈寻址。

  • 对机器级程序来说,实现细节是可见的。因此编译器需要在一些约束条件下进行性能优化;

    因此,RISC 的优缺点和 CISC 相比也是非常明显的。

  • 优点:

    • CPI 大大降低,并且通过大量寄存器减少了访存次数,有效提升性能;

    • 采用流水线技术,大部分指令在一个时钟周期完成;采用超标量和超流水线技术,可以使每条指令的平均执行时间小于一个时钟周期;

    • 控制器便于设计和实现,采用组合逻辑控制(硬布线),不用微程序控制;

    • 可以利用编译器对程序性能进行优化。

    • 低功耗、低成本

  • 缺点:

    • 指令的种类和寻址方式都比较少,编写软件比较麻烦;有些早期的 RISC 机器甚至没有乘法指令,需要用一系列加法来实现;

    • 对编译器的要求比较高,编译器采用不同的优化策略可以显著改变运行性能;

    • 不同指令系统间兼容性差。

4.4.3 CISC 和 RISC 的比较

下表中详细列出了 CISC 和 RISC 的特点对比:

对比项目CISCRISC
指令数量较少
指令字长不固定固定
可访存指令无限制Load / Store
各种指令使用频率相差很大相差不大
各种指令执行时间相差较大绝大多数在一个时钟周期内完成
寻址方式
通用寄存器数量较少
堆栈寻址软堆栈硬堆栈
控制方式微程序控制组合逻辑控制(硬布线)
机器级程序实现细节不可见可见
编译器难以优化需要优化
指令流水线可以通过一定的方式实现必须实现
功耗较高较低
兼容性较好较差
主要应用领域PC 和服务器嵌入式设备

如今,RISC 机器在发展进化的过程中,逐渐引入了更多的指令;而 CISC 机器也会充分利用高性能的流水线结构。商品化的计算机一般都会将 RISC 和 CISC 结合起来,取长补短。

4.5 高级语言程序与机器级代码

4.5.1 x86 汇编指令基础
1. 相关寄存器

(1)通用寄存器

x86 架构的 CPU 中会设置一组 通用寄存器,用来存储整数数据和指针(地址)。

最初的 8086 有 8 个 16 位的寄存器,分别叫做 ax、bx、cx、dx、si、di、bp、sp,每个寄存器都有各自特殊的用途,这都体现在它们的名字中。当扩展到 32 位架构(标准名称为 IA32)时,这些寄存器也都扩展为 32 位,名称前加上了 ’‘e“ 表示扩展(extended)。

每个 32 位的通用寄存器,都可以将低 16 位当作一个 16 位寄存器独立使用,最低 8 位当作一个 8 位寄存器使用;而 ax、bx、cx、dx 的高低字节都可以分别作为两个 8 位寄存器,称为 ah、bh、ch、dh 和 al、bl、cl、dl。

 

这里用途最为特殊的,就是堆栈指针 ebp 和 esp,它们配合可以很容易地实现子过程的调用和返回;另外,一般也经常用 ebp + 偏移量 的形式来定位存放在栈中的局部变量。

扩展为 64 位的 x86-64 架构后,原先的 8 个 32 位寄存器全部扩展到 64 位,标号以 r 开头;此外还新增了 8 个通用寄存器,标号为 r8 ~ r15。

 

 

(2)指令指针寄存器

除通用寄存器外,x86 架构的 CPU 还会设置一系列特殊功能的寄存器。其中最为重要的就是 指令指针寄存器 IP(Instruction Pointer),它存放了下一条要执行的指令的地址;很明显,这其实就是我们之前介绍的 程序计数器 PC

最初的 ip 也是 16 位的;到了 IA32 架构下,指令指针寄存器也扩展为 32 位,称为 eip;而到了 x86-64 时代,对应也扩展成了 64 位的指令寄存器,称为 rip。

(3)标志寄存器

标志寄存器 flags 里面有众多标志位,记录了 CPU 执行指令过程中的一系列状态,大都由 CPU 自动设置和修改。

  • ZF 零标志

  • CF 进位标志

  • SF 符号标志

  • OF 溢出标志

  • PF 奇偶标志

  • TF 跟踪标志

  • IF 中断标志

  • ...

很显然,标志寄存器其实就是之前提到的 程序状态字 PSW。IA32 架构下标志寄存器为 32 位,称为 efl(eflags),除去一些不使用的保留位外,每一位都对应着一个状态标志;x86-64 架构下扩展为 64 位,不过扩展的高位都没有使用,相当于还是 32 位。

2. 汇编指令格式

对于 x86 指令集的汇编代码,也有两种不同的指令格式。

  • ATT 格式:由 AT&T 公司而得名,这是 GCC 等常用工具的默认格式。

  • Intel 格式:Intel 文档中和 Microsoft 编程工具采用的汇编格式。

这两种格式整体风格相似,但也有很多不同:

 

x86 是复杂指令集架构,支持多种寻址方式,两种格式的各种寻址方式如下:

 

下面是 mov 指令的一些示例。mov 指令用于移动数据,可以将立即数、寄存器和内存中的操作数,移动到寄存器或者内存中。需要注意,ATT 格式的数据传输方向是从左向右,而 Intel 格式恰好相反。

 

mov 指令不能将一个内存中的操作数,移动到另一个内存地址。

下面我们主要以 Intel 格式为例,来详细介绍 C 语言和汇编指令的对应关系。

3. 常用汇编指令

(1)数据传输指令

  • mov:在寄存器和内存之间移动数据;

  • lea:load effective address,加载有效地址,将一个内存地址加载到目的寄存器;

  • push:将数据压入栈,同时 esp 减去数据长度;

  • pop:将栈顶数据弹出栈,同时 esp 加上数据长度;

(2)算术和逻辑运算指令

主要可以按照操作数的个数分为两类:

  • 双操作数 —— add(加)、sub(减)、mul(无符号乘)、imul(有符号乘)、and(逻辑与)、or(或)、xor(异或)、sal/shl(左移)、sar(算术右移)、shl(逻辑右移)

格式为: op D, S

表示计算 D (op) S 的值,结果存入 D 中。

  • 单操作数 —— inc(自增)、dec(自减)、neg(取负)、not(取反)

计算的结果仍然存入操作数所在位置。

 

比较特别的是除法指令,它们都只有一个操作数,表示除数,被除数则放在 edx : eax 中;得到的结果商放在 eax 中,余数放在 edx 中:

  • div:无符号除

  • idiv:有符号除

(3)转移指令

  • 无条件转移:jmp

jmp 指令后面一般跟一个 “标签”(label),用来指明可以直接跳转到的目的地。它的底层编码一般都是 PC 相对的,也就是相对寻址。

  • 有条件转移

标志寄存器 efl 中有很多标志状态,也称为 “条件码”,它们记录了最近的算术逻辑操作的结果属性。通过检测这些寄存器中的条件码,就可以执行条件转移指令了。最常用的条件码有:ZF(零标志)、CF(进位标志)、SF(符号标志)、OF(溢出标志);一般会对它们进行组合,用来表示更加容易理解的控制条件。

 

上面的条件跳转指令,需要先做一个算术或逻辑运算、更改条件码;而很多时候我们只需要做一个简单比较即可,并不需要将运算结果保存。有两类特殊指令可以只改变条件码、而不改变其它任何的寄存器:

 

这两种指令,特别是 cmp 经常和条件转移指令配合使用,用来实现条件分支(选择)和循环结构的程序。

  • 调用和返回

    进行子过程(函数)调用时,使用 call 指令;返回原函数时使用 ret 指令。

4.5.2 从 C 语言程序到汇编程序
1. 编译器、汇编器和链接器

用高级语言编写好一段程序之后,需要经过一系列“翻译“过程,才能得到计算机能够执行的机器代码。比如,我们用 C 语言写了一个简单的 hello world 程序,源程序文件命名为 hello.c,用 GCC 编译器可以将它翻译成一个可执行目标程序 hello。具体的过程如下图所示:

 

第二步编译的结果,生成了汇编程序 hello.s,这就是汇编语言描述的机器指令;汇编程序再经过汇编就可以得到二进制的机器语言程序。

在一些集成开发环境(比如 Visual Studio)中,可以在调试(Debug)模式下对机器码进行 ”反汇编“,得到相应的汇编语言代码。

2. 进程的地址空间

C 语言程序运行之后,对应的进程都会有自己独立的地址空间;这就是操作系统为每个进程提供的 虚拟地址空间。对于 32 位系统,进程虚拟地址空间的大小就是 2^32^ B = 4 GB。

整个虚拟空间需要操作系统统一管理,因此进程的虚拟地址空间中必须保留一部分给操作系统内核使用。对于 Linux 系统(32 位),内核区大小为 1GB,地址从 0xc0000000 ~ 0xffffffff;而 Windows 系统默认情况下内核区大小为 2GB,地址从 0x80000000 ~ 0xffffffff。其余低地址部分则为用户区。

下面是一个 x86 Linux 进程的虚拟地址空间。

 

用户区主要包括这样几部分:

  • 程序代码和数据:主要包括 只读代码段读写段(.data 和 .bss)。对所有进程来说,代码都是从固定地址开始,紧接着就是 C 语言中的全局和静态数据。其中 .data 中是已初始化的全局和静态 C 变量,而 .bss 中是未初始化的全局和静态变量。

  • 堆(Heap):用于运行时的动态内存分配,向上(高地址)生长。代码和数据区,在进程开始运行时就被指定了大小;而通过调用 malloc 和 free 这样的 C 标准库函数,可以让堆区动态地扩展和收缩。

  • 共享库的内存映射区:用户区的中间部分是一块内存映射区域,用来存放像 C 标准库这样的共享库。

  • 用户栈(Stack):位于虚拟地址空间用户区顶部,向下(低地址)生长。一般用来存储局部变量和函数参数,结合堆栈指针可以方便地实现函数的调用和返回。

3. 利用栈实现函数调用

C 语言中的函数是一种重要的抽象,它将代码按功能封装起来,让程序结构更加清晰、可重用性更高。

每个函数内部可以定义局部变量,这些变量只具有局部作用域。所以,嵌套函数调用时(例如,在函数 P 中调用函数 Q),就可以利用栈数据结构 ” 后进先出 “(LIFO)的特点,在栈内依次保存函数 P 和 Q 的相关内容。

这样,进程中的每一个函数,都会在栈上有一块自己的空间,就叫做 ” 栈帧 “(stack frame);当前正在执行的函数的栈帧总是在栈顶。这样 esp 的内容就是栈顶地址,而 ebp 的内容就保存当前栈顶栈帧的 “底部” 地址。

 

于是,在调用一个函数 Q(子过程)时,可以在栈上继续给 Q 中的局部变量分配内存空间(入栈)。当 Q 调用结束,就将 Q 的所有局部变量释放(出栈)。

想要用机器级代码实现函数调用,还需要考虑下面几个问题:

① 参数传递:函数 Q 应该能获取到 P 传入的参数;这可以通过指定参数存放的位置(写入寄存器或者入栈)来实现。

 

② 转移控制:调用 Q 时,需要跳转到函数 Q 入口处执行指令,这可以用 call 指令实现;调用结束,还应返回到 P 中的调用点继续执行,这需要保存之前调用点的信息,将下一条指令地址入栈。

 

等到调用结束时,执行 ret 指令返回,就执行出栈操作,将栈中保存的地址交给 eip,继续执行 P 中的下一条指令。

 

③ 保存上下文:原函数 P 使用的寄存器的内容,应该进行保存;调用结束后,还应该进行恢复。

调用 Q 后,可以先将原函数 P 的上下文(寄存器值)做一个入栈保存;然后再分配内存给 Q 的局部变量。待 Q 调用结束后,先释放 Q 的局部变量,然后继续弹栈恢复 P 的上下文。

 

比较特殊的是栈基指针 ebp,在调用 Q 之后,它应该指向 Q 的栈帧的底部;所以应该先将之前的 ebp(P 的栈帧底部)入栈,然后将 ebp 移向 esp 的位置。之后再保存 P 其它寄存器的值、分配空间给 Q 的局部变量。

<span style="background-color:#f8f8f8"><span style="color:#333333">push  ebp
mov   ebp, esp</span></span>

当 Q 调用结束返回时,只要反向执行,让 esp 移向 ebp 的位置;再将原先保存的 ebp 的值弹出,并放入 ebp 中就可以了:

<span style="background-color:#f8f8f8"><span style="color:#333333">mov   esp, ebp
pop   ebp</span></span>

对于函数调用开始时(call 之后)两条对 ebp 的处理指令,可以用一条 enter 指令来代替;函数结束时(ret 之前)的两条指令,则可以用 leave 来代替。

④ 返回值传递:函数 Q 调用结束,执行 ret 指令,此时应该能将返回值传回原函数 P;可以通过指定某个寄存器(eax)接收返回值来实现。

这样,我们可以将完整的栈帧结果表示如下:

 

栈帧中主要由 4 部分构成:上一层函数的上下文(主要是寄存器的值)、当前函数的局部变量、调用下一层函数所需的参数,以及返回地址。对于当前执行的函数 Q,没有参数构造区和返回地址两部分。

4.5.3 选择结构语句的机器级表示

除顺序结构外,高级语言程序中一般还会有选择结构和循环结构。

选择结构 又称为 分支(branch)结构。C 语言中的选择语句主要有 if ... else 和 switch ... case,此外三目运算符 ? : 也可以实现选择结构。

很显然,通过设置条件码(标志位)、结合各类转移指令,就可以很容易地实现程序中的选择语句。

下面一段 C 语言代码使用 if - else 语句实现了选择结构:

<span style="background-color:#f8f8f8"><span style="color:#333333">int a = 23;
int b = 31;
if (a > b)
{
	a++;
}
else {
	a--;
}
printf(" a = %d\n", a);</span></span>

可以发现它等效于使用 2 个 goto,分别跳过 if 后面的分支和 else 后面的分支:

<span style="background-color:#f8f8f8"><span style="color:#333333">	if (a <= b)
		goto L1;
	a++;
	goto L2;
L1:
	a--;
L2:
	printf(" a = %d\n", a);</span></span>

这样,很容易得到对应的汇编代码:

 

4.5.4 循环结构语句的机器级表示

C 语言中的循环语句有 do - while、while 和 for 三种。汇编语言中,同样可以用条件测试和跳转指令的组合来实现循环的效果。在循环结构中,通常使用条件转移指令来判断循环的结束。

1. do - while 循环

下面是一段使用了 do - while 循环的 C 语言代码:

<span style="background-color:#f8f8f8"><span style="color:#333333">int a = 0;
do {
	a++;
} while (a < 5);
printf(" a = %d \n ", a);</span></span>

很明显,循环部分可以利用 if 判断和 goto 语言来实现:

<span style="background-color:#f8f8f8"><span style="color:#333333">	int a = 0;
L1:
	a++;
	if (a < 5)
		goto L1;
	printf(" a = %d \n ", a);</span></span>

这样,类比之前选择结构,可以得到对应的汇编表示如下:

 

2. while 循环

while 循环与 do - while 类似,区别在于第一次执行循环体之前就要先做条件判断。下面是一段 while 循环的 C 语言代码:

<span style="background-color:#f8f8f8"><span style="color:#333333">int a = 0;
while (a < 5) {
	a++;
}
printf(" a = %d \n ", a);</span></span>

如果用 goto 进行改写,可以参考 if - else 的实现,使用两个 goto 分别进行满足、不满足循环条件时的跳转:

<span style="background-color:#f8f8f8"><span style="color:#333333">	int a = 0;
L1:
	if (a >= 5)
		goto L2;
	a++;
	goto L1;
L2:
	printf(" a = %d \n ", a);</span></span>

于是对应的汇编程序如下:

 

3. for 循环

for 循环可以将循环变量和循环条件统一列出,因此对程序员来说是最友好的;但它直接转换成汇编语言会有一定的难度。for 循环的基本形式如下:

<span style="background-color:#f8f8f8"><span style="color:#333333">for( 初始化循环变量; 判断循环条件; 更新循环变量)
    循环体</span></span>

可以把它先改写成 while 循环的形式:

<span style="background-color:#f8f8f8"><span style="color:#333333">初始化循环变量;
while( 判断循环条件 )
{
    循环体;
    更新循环变量;
}</span></span>

这样,就可以用 goto 改写如下:

<span style="background-color:#f8f8f8"><span style="color:#333333">	初始化循环变量;
L1:
	if(循环条件不成立)
    	goto L2;
    循环体;
    更新循环变量;
	goto L1;
L2:
	循环外语句;</span></span>

当然,如果不改写成 while、完全按照 for 循环的执行顺序来处理,就会麻烦很多。比如下面是一段 for 循环的 C 语言代码:

<span style="background-color:#f8f8f8"><span style="color:#333333">int a = 10;
for (int i = 0; i < 5; i++)
{
	a--;
}
printf(" a = %d \n ", a);</span></span>

对应的汇编代码如下:

 

很明显,本质上三种循环都是等效的,而比较之下 do-while 循环的汇编指令最简洁。因此,大多数编译器会进行优化,将另两种循环语句转换为 do-while 语句形式来生成机器代码。

另外,x86 架构指令集还提供了一个 loop 指令专门用于循环的实现。它默认使用 ecx 寄存器作为循环计数器,每次执行到 loop 指令都会先对 ecx 做减 1 操作;然后判断 ecx 是否为 0,如果不为 0 则跳转到 loop 后面标号对应的位置,如果为 0 则循环结束继续向下执行。

<span style="background-color:#f8f8f8"><span style="color:#333333">mov ecx, 10
.L1:
mov eax, dword ptr [a]
sub eax, 1
mov dword ptr [a], eax
loop .L1</span></span>

上面的 loop 指令相当于:

<span style="background-color:#f8f8f8"><span style="color:#333333">dec ecx
cmp ecx, 0
jne .L1</span></span>

这样减少了指令数,汇编代码的可读性更高了。除 loop 外,还有类似的 loopz 和 loopnz 指令,它们判断循环继续的条件除了 ecx != 0 外,还有对 ZF 的要求。

4.5.5 过程(函数)调用对应的机器级表示

下面是一段 C 语言的函数调用过程。我们在 main 函数中调用了 add 函数,进行两个整数的求和。

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#008855">int</span> <span style="color:#0000ff">add</span>(<span style="color:#008855">int</span> <span style="color:#000000">x</span>, <span style="color:#008855">int</span> <span style="color:#000000">y</span>);
​
<span style="color:#008855">int</span> <span style="color:#0000ff">main</span>() {
    <span style="color:#008855">int</span> <span style="color:#000000">a</span> <span style="color:#981a1a">=</span> <span style="color:#116644">23</span>;
    <span style="color:#008855">int</span> <span style="color:#000000">b</span> <span style="color:#981a1a">=</span> <span style="color:#116644">31</span>;
​
    <span style="color:#008855">int</span> <span style="color:#000000">sum</span> <span style="color:#981a1a">=</span> <span style="color:#000000">add</span>(<span style="color:#000000">a</span>, <span style="color:#000000">b</span>);
​
    <span style="color:#000000">printf</span>(<span style="color:#aa1111">" sum = %d\n"</span>, <span style="color:#000000">sum</span>);
}
​
<span style="color:#008855">int</span> <span style="color:#0000ff">add</span>(<span style="color:#008855">int</span> <span style="color:#000000">x</span>, <span style="color:#008855">int</span> <span style="color:#000000">y</span>) {
    <span style="color:#008855">int</span> <span style="color:#000000">sum</span> <span style="color:#981a1a">=</span> <span style="color:#000000">x</span> <span style="color:#981a1a">+</span> <span style="color:#000000">y</span>;
    <span style="color:#770088">return</span> <span style="color:#000000">sum</span>;
}</span></span>

函数调用时,首先应该跳转之前,将需要的参数进行保存;然后执行 call 指令,同时将下一条指令地址入栈。主程序 main 对应的汇编代码如下:

 

调用函数 add 后,首先应该将原来的 ebp 入栈,并将 ebp 指向当前 esp 的位置(add 函数栈帧的底部);然后保存之前其它寄存器的值(入栈)。之后分配局部变量的空间,执行 add 内部指令。执行完毕后,做反向操作,弹栈恢复所有寄存器的值,返回 main 函数的调用处继续执行。

add 函数对应的汇编指令如下:

 

4.6 章节练习

一、单项选择题
  1. 【2010真题】下列寄存器中,汇编语言程序员可见的是 ( ) A.存储器地址寄存器(MAR) B.程序计数器(PC) C.存储器数据寄存器(MDR) D.指令寄存器(IR)

答案:B

  1. 【2021真题】下列寄存器中,汇编语言程序员可见的是 ( )

Ⅰ.指令寄存器 Ⅱ.微指令寄存器 Ⅲ.基址寄存器 Ⅳ.标志/状态寄存器

A.仅Ⅰ、Ⅱ B.仅Ⅰ、Ⅳ C.仅Ⅱ、Ⅳ D.仅Ⅲ、Ⅳ

答案:D

要点:汇编程序员可见的寄存器有基址寄存器、变址寄存器、状态/标志寄存器(PSW)、程序计数器 PC 和通用寄存器组;而 MAR、MDR、IR 是 CPU 内部工作寄存器, 对汇编程序员不可见。微指令寄存器属于微程序控制器的组成部分, 它是硬件设计者的任务,对汇编程序员是透明的。

  1. 【2022真题】下列选项中,属于指令集体系结构(ISA)规定的内容是 ( )

I. 指令字格式和指令类型 II. CPU 的时钟周期 III. 通用寄存器个数和位数 IV. 加法器的进位方式

A. 仅 I、 II B. 仅I、 III C. 仅 II、 IV D. 仅 I、 III、 IV

答案:B

要点:指令集处于软硬件的交界面上。指令字和指令格式、通用寄存器个数和位数都与机器指令有关,由 ISA 规定。两个 CPU 可以有不同的时钟周期,但指令集可以相同,CPU 的时钟周期不由 ISA 规定。 加法器的进位方式涉及电路设计, 也不由指令集规定。

  1. 【2009真题】下列关于 RISC 的叙述中, 错误的是 ( ) A. RISC 普遍采用微程序控制器 B. RISC 大多数指令在一个时钟周期内完成 C. RISC 的内部通用寄存器数量相对 CISC 多 D. RISC 的指令数、寻址方式和指令格式种类相对 CISC 少

答案:A

要点:相对于 CISC , RISC 的特点是以硬布线逻辑为主,不用或者少用微程序控制。

  1. 【2011真题】下列给出的指令系统特点中,有利于实现指令流水线的是 ( )

Ⅰ .指令格式规整且长度一致

Ⅱ .指令和数据按边界对齐存放

Ⅲ.只有 Load/Store 指令才能对操作数进行存储访问

A.仅Ⅰ 、 Ⅱ B.仅Ⅱ 、 Ⅲ C.仅Ⅰ 、 Ⅲ D. Ⅰ 、 Ⅱ 、 Ⅲ

答案:D

要点:以上三点都是 RISC 的特点,都可以有效简化指令流水线的复杂度。

  1. 【2016真题】某计算机主存空间为 4GB,字长为 32 位,按字节编址,采用 32 位字长指令字格式。若指令按字边界对齐存放,则程序计数器(PC)和指令寄存器(IR)的位数至少分别是 ( )

A. 30、 30 B. 30、 32 C. 32、 30 D. 32、 32

答案:B

要点:程序计数器(PC)给出下一条指令字的访存地址,取决于存储器的字数;指令寄存器(IR)用于存放取得的指令,取决于指令字长。

  1. 【2017真题】某计算机按字节编址,指令字长固定且只有两种指令格式,其中三地址指令 29 条,二 地址指令 107 条,每个地址字段为 6 位,则指令字长至少应该是 ( )

A. 24位 B. 26位 C. 28位 D. 32位

答案:A

要点:指令字长应是字节的整数倍。

  1. 【2022真题】设计某指令系统时,假设采用 16 位定长指令字格式,操作码使用扩展编码方式,地址码为 6位,包含零地址、 一地址和二地址 3 种格式的指令。 若二地址指令有 12 条,一地址指令有254 条,则零地址指令的条数最多为 ( )

A. 0 B. 2 C. 64 D. 128

答案:D

  1. 【2011真题】偏移寻址通过将某个寄存器内容与一个形式地址相加而生成有效地址。下列寻址方式中, 不属于偏移寻址方式的是 ( )

A.间接寻址 B.基址寻址 C.相对寻址 D.变址寻址

答案:A

  1. 【2017真题】下列寻址方式中,最适合按下标顺序访问一维数组元素的是 ( )

A.相对寻址 B.寄存器寻址 C.直接寻址 D.变址寻址

答案:D

  1. 【2020真题】某计算机采用 16 位定长指令字格式,操作码位数和寻址方式位数固定,指令系统有 48 条指令,支持直接、间接、立即、相对 4 种寻址方式。单地址指令中,直接寻址方式的可寻址范围是 ( )

A. 0 ~ 255 B. 0 ~ 1023 C. -128 ~ 127 D. -512 ~ 511

答案:A

要点:4 种寻址方式需要有 2 位寻址特征位。

  1. 【2009真题】某机器字长为 16 位,主存按字节编址,转移指令采用相对寻址,由两个字节组成, 第一字节为操作码字段,第二字节为相对位移量字段。假定取指令时,每取一个字节 PC 自动加 1。若某转移指令所在主存地址为 2000H,相对位移量字段的内容为 06H,则该转移指令成功转移后的目标地址是 ( )

A. 2006H B. 2007H C. 2008H D. 2009H

答案:C

  1. 【2013真题】假设变址寄存器 R 的内容为 1000H,指令中的形式地址为 2000H;地址 1000H 中的内容为 2000H,地址 2000H 中的内容为 3000H,地址 3000H中的内容为 4000H,则变址寻址方式下访问到的操作数是 ( )。

A.1000H B.2000H C.3000H D.4000H

答案:D

  1. 【2014真题】某计算机有 16 个通用寄存器,采用 32 位定长指令字, 操作码字段(含寻址方式位)为 8 位,Store 指令的源操作数和目的操作数分别采用寄存器直接寻址和基址寻址方式。若基址寄存器可使用任一通用寄存器, 且偏移量用补码表示, 则 Store 指令中偏移量的取值范围是 ( )

A. -32768-+32767 B. -32767-+32768 C. -65536-+65535 D. -65535-+65536

答案:A

要点:16 个寄存器需要 4 位地址码表示。

  1. 【2016真题】某指令格式如下所示

    其中 M 为寻址方式, I 为变址寄存器编号, D 为形式地址。若采用先变址后间址的寻址方式,则操作数的有效地址是 ( )

    A. I + D B. ( I ) + D C. ( ( I ) + D ) D. ( ( I ) ) + D

    答案:C

    要点:变址寻址中 EA = ( I ) + D;间接寻址中 EA = ( D )。

  2. 【2018真题】按字节编址的计算机中,某 double 型数组 A 的首地址为 2000H,使用变址寻址和循环结构访问数组 A,保存数组下标的变址寄存器初值为 0,每次循环取一个数组元素,其偏移地址为变址值乘以 sizeof(double),取完后变址寄存器内容自动加 1 。若某次循环所取元素的地址为 2100H,则进入该次循环时变址寄存器的内容是 ( )。

A.25 B.32 C.64 D.100

答案:B

要点:数组首地址就是形式地址 D,变址寄存器中存放的是数组下标。

  1. 【2018真题】减法指令“sub R1,R2,R3”的功能为“(R1)-(R2)→R3”,该指令执行后将生成进位/借位标志CF 和溢出标志 OF。若(R1)=FFFF FFFFH,(R2)=FFFF FFF0H,则该减法指令执行后,CF 与 OF 分别为 ( )。

A. CF = 0, OF = 0 B. CF = 1 , OF = 0 C. CF = 0, OF = 1 D. CF = 1, OF = 1

答案:A

要点:减法操作只需判断借位标志;当最高位进位和符号位进位的值不相同时才产生溢出。

  1. 【2019真题】某计算机采用大端方式,按字节编址。某指令中操作数的机器数为 1234 FF00H,该操 作数采用基址寻址方式,形式地址(用补码表示)为 FF12H,基址寄存器的内容为 F000 0000H, 则该操作数的 LSB(最低有效字节)所在的地址是 ( )

A. F000 FF12H B. F000 FF15H C. EFFF FF12H D. EFFF FF15H

答案:D

要点:基址寻址方式, EA = (BR) + A;大端方式编址,低位字节存放在字的高地址处。

二、综合应用题
  1. 【2017真题】在按字节编址的计算机 M 上,上题中 f1 的部分源程序(阴影部分)与对应的机器级代码(包括指令的虚拟地址)如下:

<span style="background-color:#f8f8f8"><span style="color:#333333">int f1(unsigned n){
	int sum=1, power=1;
	for(unsigned i=0;i<=n-1;i++){
		power *= 2;
		sum += power;
	}
	return sum;
} </span></span>
<span style="background-color:#f8f8f8"><span style="color:#333333">		int f1(unsigned n)
1		00401020	55				push ebp
		...			...				...
			for(unsigned i=0;i<=n-1;i++)
		...			...				...
20		0040105E	39 4D F4		cmp dword ptr [ebp-0Ch], ecx
		...			...				...
			{	power *= 2;
		...			...				...
23		00401066	D1 E2			shl edx, 1
		...			...				...
			return sum;
		...			...				...
35		0040107F	C3				ret</span></span>

其中,机器级代码行包括行号、虚拟地址、机器指令和汇编指令。请回答下列问题。

(1)计算机 M 是 RISC 还是 CISC? 为什么?

(2)f1 的机器指令代码共占多少字节?要求给出计算过程。

(3)第 20 条指令 cmp 通过 i 减 n-1 实现对 i 和 n-1 的比较。执行 f1(0) 过程中当 i=0 时, cmp 指令执行后,进/借位标志 CF 的内容是什么?要求给出计算过程。

(4)第 23 条指令 shl 通过左移操作实现了 power *2 运算,在 f2 中能否也用 shl 指令实现 power *2? 为什么? (注:将 f1 中的 int 都改为 float, 可得到计算 f(n) 的另一个函数 f2。)

答案:

(1)M 为 CISC。 因为 M 的指令长短不一,不符合 RISC 指令系统特点。

(2)f1 的机器代码占 96 B。

因为 f1 的第一条指令 “push ebp” 所在的虚拟地址为 0040 1020H,最后一条指令 “ret” 所在的虚拟地址为 0040 107FH,所以, f1 的机器指令代码长度为 0040 107FH - 0040 1020H + 1 = 60H = 96 个字节。

(3)CF = 1。

cmp 指令实现 i 与 n-1 的比较功能,进行的是减法运算。在执行 f1(0) 过程中, n=0,当 i=0 时,i = 0000 0000H,n-1 = FFFF FFFFH。因此,当执行第 20 条指令时,在补码加/减运算器中执行 “0 减 FFFF FFFFH” 的操作,即 0000 0000H+0000 0000H+1=0000 0001H,此时,进位输出 C=0,减法运算时的借位标志 CF=C⊕1=1。

(4)f2 中不能用 shl 指令实现 power*2。

因为 shl 指令用来将一个整数的所有有效数位作为一个整体左移;而 f2 中的变量 power 是 float 型,其机器数中不包含最高有效数位,但包含了阶码部分,将其作为一个整体左移时并不能实现 “乘 2” 的功能,因而 f2 中不能用 shl 指令实现 power*2。


  1. 【2019真题】已知 $f(n) = n! = n(n-1)(n-2)...2*1$ ,计算 f(n) 的 C 语言函数 f1 的源程 序(阴影部分)及其在 32 位计算机 M 上的部分机器级代码如下:

其中,机器级代码行包括行号、虚拟地址、机器指令和汇编指令,计算机 M 按字节编址, int 型数据占 32 位。请回答下列问题:

(1)计算 f(10) 需要调用函数 f1 多少次?执行哪条指令会递归调用 f1?

(2)上述代码中,哪条指令是条件转移指令?哪几条指令一定会使程序跳转执行?

(3)根据第 16 行的 call 指令,第 17 行指令的虚拟地址应是多少?已知第 16 行的 call 指令采用相对寻址方式,该指令中的偏移量应是多少(给出计算过程)?已知第 16 行的 call 指令的后 4 字节为偏移量, M 是采用大端方式还是采用小端方式?

(4) f(13) = 6227020800,但 f1(13) 的返回值为 1932053504,为什么两者不相等?要使 f1(13) 能返回正确的结果,应如何修改 f1 的源程序?

(5)第 19 行的 imul 指令(带符号整数乘)的功能是 R[eax]←R[eax]×R[ecx],当乘法器输出的高、低 32 位乘积之间满足什么条件时,溢出标志 OF = 1?要使 CPU 在发生溢出时转异常处理,编译器应在 imul 指令后应加一条什么指令?

答案:

(1)计算 f(10) 需要调用函数 f1 共 10 次,执行第 16 行的 call 指令会递归调用 f1。 ​ (2)第 12 行的 jle 指令是条件转移指令,其含义为小于等于时转移,本行代码的意义为:当 n≤1 时,跳转至地址 0040 1035H。

第 16 行的 call 指令为函数调用指令,第 20 行的 jmp 指令为无条件转移指令,第 30 行的 ret 指令为子程序的返回指令,这三条指令一定会使程序跳转执行。

(3)其长度计算机 M 上按字节编址,第 16 行的 call 指令的虚拟地址为 0040 1025H,长度为 5 字节,故第 17 行的指令的虚拟地址为 0040 1025H + 5 = 0040 102AH 。第 16 行的 call 指令采用相对寻址方式,即目标地址= (PC) +偏移量, call 指令的目标地址为 0040 1000H,所以偏移量=目标地址- (PC) = 0040 1000H - 0040 102AH = FFFF FFD6H。

根据第 16 行的 call 指令的偏移量字段为 D6 FF FF FF,可以确定 M 采用小端方式。

(4)因为 f(13) = 6227020800,其结果超出了 32 位 int 型数据可表示的最大范围,因此 f(13) 的返回值是一个发生了溢出的错误结果。为使 f1(13) 能返回正确结果,可将函数 f1 的返回值类型改为 double(或 long long,或 long double,或 float)类型。

(5)imul 指令最终结果放在 32 位的 eax 中,所以若乘积的高 33 位为非全 0 或非全 1,则 OF = 1。编译器应在 imul 指令后加一条 “溢出自陷指令”,使得 CPU 自动查询溢出标志 OF,当 OF=1 时调出“溢出异常处理程序”。


  1. 【2021真题】假定计算机 M 字长为 16 位,按字节编址,连接 CPU 和主存的系统总线中地址线为 20 位、数据线为 8 位,采用 16 位定长指令字,指令格式及其说明如下:

 

其中,op1 ~ op3 为操作码,rs、rt 和 rd 为通用寄存器编号,R[r] 表示寄存器 r 的内容,imm 为立即数,target 为转移目标的形式地址。请回答下列问题。

(1)ALU 的宽度是多少位?可寻址主存空间大小为多少字节?指令寄存器、主存地址寄存器(MAR)和主存数据寄存器(MDR)分别应有多少位?

(2)R 型格式最多可定义多少种操作?I 型和 J 型格式总共最多可定义多少种操作?通用寄存器最多有多少个?

(3)假定 op1 为 0010 和 0011 时,分别表示带符号整数减法和带符号整数乘法指令,则指令 01B2H 的功能是什么(参考上述指令功能说明的格式进行描述)?

若 1、2、3 号通用寄存器当前内容分别为 B052H、 0008H、0020H,则分别执行指令 01B2H 和 01B3H 后,3 号通用寄存器内容各是什么?各自结果是否溢出?

(4)若采用 l 型格式的访存指令中 imm(偏移量)为带符号整数,则地址计算时应对 imm 进行零扩展还是符号扩展?

(5)无条件转移指令可以采用上述哪种指令格式?

答案:

(1)ALU 的宽度为 16 位,ALU 的宽度即 ALU 运算对象的宽度,通常与字长相同。

地址线为 20 位,按字节编址,可寻址主存空间大小为 220 字节(或 1MB)。

指令寄存器有 16 位, 和单条指令长度相同。

MAR 有 20 位,和地址线位数相同。MDR 有 8 位,和数据线宽度相同。

(2)R 型格式的操作码有 4 位, 最多有 24 = 16 种操作。

I 型和 J 型格式的操作码有 6 位,因为它们的操作码部分重叠,所以共享这 6 位的操作码空间,且前 6 位全 0 的编码已被 R 型格式占用, 因此 I 和 J 型格式最多有 26 -1 = 63 种操作。

从 R 型和 I 型格式的寄存器编号部分可知, 只用 2 位对寄存器编码, 因此通用寄存器最多有 2^2^ = 4 个。

(3)指令 01B2H = 000000 01 10 11 0010B 为一条 R 型指令,操作码 0010 表示带符号整数减法指令,其功能为 R[3] $\leftarrow$ R[1] - R[2]。

执行指令 01B2H 后,R[3] = B052H-0008H = B04AH,结果未溢出。

指令 01B3H = 000000 01 10 11 0011B,操作码 0011 表示带符号整数乘法指令。执行指令 01B3H 后,R[3] = R[1] x R(2] = B052H x 0008H = 8290H,结果溢出。

(4)在进行指令的跳转时,可能向前跳转,也可能向后跳转。偏移量是一个带符号整数, 因此在地址计算时,应对 imm 进行符号扩展。

(5)无条件转移指令可以采用 J 型格式, 将 taget 部分写入 PC 的低 10 位,完成跳转。

第五章 中央处理器(CPU)

5.1 本章大纲要求与核心考点

5.1.1 大纲内容

(一)CPU 的功能和基本结构

(二)指令执行过程

(三)数据通路的功能和基本结构

(四)控制器的功能和工作原理

(五)异常和中断机制

  1. 异常和中断的基本概念

  2. 异常和中断的分类

  3. 异常和中断的检测与响应

(六)指令流水线

  1. 指令流水线的基本概念

  2. 指令流水线的基本实现

  3. 结构冒险、数据冒险和控制冒险的处理

  4. 超标量和动态流水线的基本概念

(七)多处理器基本概念

  1. SISD、SIMD、MIMD、向量处理器的基本概念

  2. 硬件多线程的基本概念

  3. 多核处理器(multi-core)的基本概念

  4. 共享内存多处理器(SMP)的基本概念

5.1.2 核心考点

本章知识点较多,综合性比较强。其中,指令流水线是历年考查的一个重点,数据通路、控制器原理出现的频率也很高,单项选择和综合应用题都有可能出现。本章单独出题一般以概念和原理为主,计算较少;往往会与第四章结合进行综合考查,有时也会结合第二章、第三章的内容。

需要掌握的内容包括:

  • CPU 的基本结构,运算器和控制器的组成,各种寄存器的功能和特性。

  • 指令周期的概念,指令执行的过程和方案。

  • 数据通路的结构,数据通路中的数据传送流程和控制信号。

  • 硬布线控制器的概念和原理;微程序控制器的概念和原理,微指令的编码方式及特点;两种控制器的比较。

  • 异常和中断的概念和分类;异常和中断的响应过程。

  • 指令流水线的概念、分类和原理;流水线冒险与处理方法;流水线的性能指标;超标量流水线。

  • 多处理器的基本概念和分类。

5.1.3 真题分布
考点考查次数
单项选择题综合应用题
指令流水线102
控制器的功能和工作原理65
CPU的功能和基本结构54
数据通路的功能和基本结构34
指令执行过程22
多处理器的基本概念10

5.2 CPU 的功能和基本结构

5.2.1 CPU 的功能

中央处理器(CPU)由 运算器控制器 组成。 

运算器负责数据加工,也就是对数据进行算术和逻辑运算;而控制器则需要完成计算机各部件之间的协调和控制,保障每一条指令按规定的执行步骤正确执行,还要处理各项紧急事件。

计算机核心的功能就是自动执行一系列指令,这项工作主要由 CPU 中的控制器来完成。具体来说,指令的执行过程包括了 取指令分析指令执行指令 三个步骤。

  • 取指令:控制器能够自动地从存储器中取出指令,并且按预定的顺序依次取下一条指令。这要求控制器可以自动形成要执行的指令地址,并发出取指命令将对应的指令从主存取到控制器中。

  • 分析指令:首先,控制器需要分析指令需要完成什么操作,从而发出对应的操作命令;其次,控制器需要分析操作数的地址,计算出有效地址。

  • 执行指令:根据分析出的 “操作命令” 和 “操作数地址”,控制器就可以形成操作控制信号序列, 通过对运算器、主存以及 I/O 设备的操作,执行每条指令。

除此之外,控制器还必须能控制程序的输入和运算结果的输出,以及对总线(Bus)的控制,甚至需要处理一些运行中的异常情况和特殊请求。

所以总结起来,CPU 的功能包括:

  • 数据加工:对数据进行算术和逻辑运算。

  • 指令控制:控制程序的顺序执行,完成取指令、分析指令和执行指令的操作。

  • 操作控制:产生完成每条指令的一系列操作命令,把各种控制信号送往相应的部件,使这些部件按指令的要求进行工作。

  • 时间控制:对各种操作加以时间上的控制,为每条指令按时间顺序提供应有的控制信号。

  • 中断处理:对计算机运行中出现的异常情况和特殊请求进行处理。

5.2.2 CPU 的基本结构

根据 CPU 的功能可以看出,数据的运算需要由运算器来完成,剩下的功能则由控制器完成。

 

1. 运算器

运算器需要对数据进行运算,所以核心部件是 算术逻辑单元(ALU),此外还有一系列的 寄存器 用来缓存数据、运算结果和状态。

运算器中的寄存器主要有暂存寄存器、累加寄存器(ACC)、通用寄存器组(GPRs)、程序状态字寄存器(PSW)等。

  • 暂存寄存器:用于暂存从主存读来的数据。

  • 累加寄存器(ACC):用于暂存 ALU 的运算结果,可以作为加法运算的一个操作数再次输入 ALU。

  • 通用寄存器组:通用功能的一组寄存器,主要用于存放操作数和地址信息。

  • 程序状态字寄存器(PSW):对于算术/逻辑运算指令或测试指令的运行结果,专门用一个寄存器 PSW 来保存各种状态信息,也叫 条件码寄存器。典型的条件码有溢出标志(OF)、符号标志(SF)、零标志(ZF)、进位标志(CF)等。

这些寄存器对于程序员来说,除了暂存寄存器一般是透明的,其它都是可见的。

2. 控制器

控制器是计算机中负责协调控制的部件,主要功能就是取指令、分析指令、执行指令。

控制器需要根据指令中操作码、指令的执行步骤以及当前状态字信息,来生成一系列对计算机各部件的控制信号(也叫做 “微操作”),这个专门的部分就叫做 “控制单元”(CU)

为了准确地取出指令,需要一个寄存器来保存下一条指令的地址,这就是 程序计数器(PC);取出的指令同样也需要先保存下来再做分析,存放指令的寄存器就是 指令寄存器(IR),另外还需要一个 指令译码器(ID)来对指令操作码进行译码。最后,还需要有 中断系统 来处理异常情况和特殊请求。

MAR 和 MDR 一般也会集成在控制器中。因此控制器内的寄存器主要有:

  • 程序计数器(PC):存放下一条指令的主存地址。

  • 指令寄存器(IR):存放取出的指令。

  • 主存地址寄存器(MAR):存放要访问的主存单元的地址。

  • 主存数据寄存器(MDR):存放要向主存写入或从主存读出的信息。

这些寄存器的功能主要是控制指令的执行过程,因此大多数情况下对用户是透明(不可见)的。不过一般情况下计算机允许汇编程序员访问程序计数器(比如 x86 架构下的 IP),因此 PC 可以认为对汇编程序员来说是可见的。

除此之外,CPU 中一般还会包含 高速缓存(Cache)内存管理单元(MMU),以及用于数据传输的内部数据总线和产生时序信号的时序电路。

5.3 指令周期

5.3.1 指令周期的概念

计算机取出并执行完一条指令所需的全部时间,就叫做 指令周期

 

我们已经知道,一条指令的执行过程可以分为 取指令分析指令执行指令 三个步骤,因此一个指令周期也应该包含这三部操作需要的时间。由于分析指令时可以用硬件实现操作码的译码,因此这步操作用时很短,一般可以将取指令和分析指令合并为一个阶段,对应的时间称为 取指周期;而执行指令阶段所需的时间就称为 执行周期

 

很明显,各种指令的取指周期是一样的,但由于具体的执行操作不同,执行周期各不相同;因此指令周期是可以不同的。

前面我们将 ”分析指令“ 这一步合并入了取指周期,是因为操作码的译码非常简单;但分析指令不仅仅要对操作码进行译码,还需要根据形式地址得到有效地址。在间接寻址的方式下,指令字中给出的是 ”地址的地址“,所以需要先访问主存一次、取出有效地址,然后再次访问主存取出操作数。这个阶段称为 间址周期

 

此外,计算机在执行过程中,可能会出现异常情况或者特殊请求,这时需要进行中断处理。所以 CPU 会在每条指令执行阶段结束前,发出 ”中断查询信号“,检测是否有某个 I/O 设备提出了中断请求;如果有,则 CPU 进入中断响应阶段,称为 中断周期。中断周期中 CPU 需要将程序断点保存到存储器中。

 

所以,一个完整的指令周期应该包括 取指、间址、执行 和 中断 4 个部分。间址和中断周期不一定会包含在指令周期中。这 4 个周期内都会有访存操作,又可称为 CPU 的 ”工作周期“。

  • 取指:访问主存取出指令

  • 间址:访问主存获取有效地址

  • 执行:从主存中取操作数,或将结果写入主存

  • 中断:将程序断点保存到主存

在 CPU 的硬件底层,为了区分不同的工作周期、更方便地设计控制单元 CU,往往会设置一组 CPU 工作周期的 ”标志触发器“。

 

上面的 4 个 D 触发器 FE、IND、EX 和 INT 就对应了指令周期的取指(fetch)、间址(indirect addressing)、执行(execute)、中断(interrupt) 4 个阶段,以 ”1“ 状态来表示有效。在取指阶段,只要设置 FE 为 1,就可以由它控制取指阶段的各步操作;当取指结束、进入间址周期时,只要将 FE 置 0、IND 置 1 就可以了。

5.3.2 时钟周期和机器周期
1. 时钟周期

计算机是由 时钟信号 来控制时间顺序的。

时钟信号是由机器中的主振电路(比如晶体振荡器)发出脉冲信号后,经整型或分频后产生。时钟信号的宽度称为 时钟周期,时钟信号的频率就是 CPU 的 主频,时钟周期和机器主频互为倒数。时钟周期是 CPU 工作的最小时间单位。

 

用时钟信号控制节拍发生器,可以产生 节拍;每个节拍的宽度就对应着一个时钟周期。在一个节拍内,机器可以完成一个最小的操作(微操作),或者是几个需要同时执行的操作。

2. 机器周期

每条指令都可以包含四个不同的阶段,每个阶段都至少会访问一次主存。

由于访问主存占据了大部分时间,需要一次访存的操作耗时都差不多,因此我们可以设置一个统一的 ”基准时间“,在这个基准时间内,所有阶段的操作都可以完成;这个基准时间就被称为 机器周期

 

可以看到,一个指令周期包含了若干个机器周期,而一个机器周期又包含若干时钟周期。

上面的例子中,每个机器周期都是相等的,包含 4 个节拍(时钟周期),这是定长的机器周期;如果每个机器周期中包含的节拍数不等,就是不定长的机器周期:

 

不定长的机器周期更适合比较简单的指令,它可以跳过一些不需要的时钟周期,从而让整个指令周期变短。我们可以默认给一个较短的机器周期,对于比较复杂的操作,则通过增加节拍、通过延长机器周期来解决。

5.3.3 处理器的设计方式

针对指令周期采用不同的方式,可以设计出不同类型的 CPU。

1. 单周期处理器

单周期处理器中,指令周期固定为 一个时钟周期

在这种方式下,所有的指令都在相同的时间内执行完成,把这个时间就设置为时钟周期,因此 CPI 为 1。因为指令是串行执行的,这样,时钟周期就会以最慢的指令的执行时间为准,从而导致 CPU 主频较低、运行速度较慢。

单周期处理器实现比较简单,但性能较差,不能充分利用硬件资源。

2. 多周期处理器

多周期处理器,则是指每条指令需要若干个时钟周期来完成。

一般来说,多周期处理器会将整个 CPU 的指令执行过程分成几个阶段,每个阶段又用若干个时钟周期去完成,然后开始下一条指令的执行。这样,各种指令执行需要的时钟周期数就可能不同。

多周期 CPU 可以更加充分地利用硬件资源,提升执行效率。不过由于每种指令的执行时间不同,需要对指令进行分类,实现起来较为复杂。

3. 流水线处理器

流水线处理器同样会将 CPU 执行指令的过程分为几个阶段,而每个阶段都占用相同的时间(机器周期);在同一时间,在计算机的各个部件上可以依次执行每条指令的不同阶段,就像在流水作业线上处理指令一样。

这样的方式使得指令可以并行执行,大大提升了系统运行效率。每个机器周期,CPU 都会取出一条新的指令;如果以机器周期作为时钟周期,那么理想情况下,流水线的执行效率可以做到接近于每个时钟周期处理 1 条指令,即 CPI 为 1。

5.4 指令流水线

对于计算机系统而言,提升器件性能和改进系统结构,是提高整体性能的两大途径。指令流水线 就是改进处理器架构的一项并行处理技术,可以极大地提高 CPU 的工作效率。

计算机中的并行性体现在不同的级别上。通常可以分为以下 4 个级别:

  • 作业级/程序级

  • 任务级/进程级

  • 指令之间级

  • 指令内部级

前两个级别是粗粒度的,又称为 过程级,一般用软件算法实现;而后两个级别是细粒度的,又称为 指令级,一般用硬件实现。指令流水线就是一项实现指令级并行的技术。

5.4.1 指令流水线的基本概念和原理
1. 流水线的概念和原理

类似于工厂中流水装配线的思想,指令的执行过程也可以分成不同的阶段,每个阶段需要的 CPU 部件是不同的;所以 CPU 各个部件可以同时对不同的指令进行处理,这就是指令流水线。

如果只把指令处理过程分成两个阶段:取指令和执行指令,那么没有采用流水线的计算机会串行处理每条指令:

 

取指令的操作可以由取指部件完成,执行指令的操作则可以由执行部件完成。所以这种顺序执行的方式尽管实现简单,但是对硬件的利用率不高。

如果采用流水线,则可以在时间上将不同指令的执行 ”重叠“ 起来,实现并行的效果:

 

划分两个阶段,可以同时有两条指令重叠,这称为指令的 二级流水

理论上讲,二级流水线同时有两条指令执行,相当于可以将指令周期减半、速度提升一倍。

2. 六级流水线

为了进一步提升处理速度,可以将指令的处理过程划分成更为细致的几个阶段:

  • 取指(FI):从主存取出一条指令,并暂存在缓冲区(IR)中。

  • 指令译码(DI):确定操作方式和操作数的寻址方式。

  • 计算操作数地址(CO):根据寻址方式,计算操作数的有效地址。

  • 取操作数(FO):从主存中取出操作数(如果在寄存器中,则可以跳过这个阶段)。

  • 执行指令(EI):执行指令的具体操作,将结果存放在目的位置(寄存器)。

  • 写操作数(WO):将结果写入主存。

共有 6 个阶段,所以可以构建出六级流水线。流水线中各阶段应该有相同的机器周期,这里我们可以假设以上各段时间相同。对应的指令六级流水时序图如下:

 

这样的流水线处理器中,应该设计 6 个操作部件,可以同时处理 6 条指令,从而大大提高了程序的运行速度。假设机器周期就等于时钟周期,也就是每个阶段用时是 1 个时钟周期,那么流水线可以做到 CPI 接近于1。

当然,实际的流水线远远达不到这样的性能。流水线也有很多问题:

  • 实际处理器中,每条指令不一定包含了完整的 6 个阶段;

  • 实际的处理器中,指令处理的各个阶段时间不会相同;

  • 这里需要假设不存在同时访存的冲突,所以所有指令都可以同时并行;实际情况下,指令可能存在冲突;

  • 当遇到条件转移指令时,下一条指令是无法提前确定的,只能根据前一条指令的执行结果来判断,时间上会有损失。

3. 流水线处理器的设计原则

流水线处理器设计的基本原则,就是:

  • 机器周期应该是定长的,并且以用时最长的处理阶段为准;它对应着每个流水线阶段(简称 ”流水段“ )的时间长度。

  • 流水段的个数,应该以最复杂指令的处理阶段数量为准;

很明显,如果 CPU 采用的指令集比较复杂,流水线的效率就会大打折扣。所以更有利于实现流水线的指令集架构,应该满足:

  • 尽量采用定长指令字;

  • 尽量简化指令的功能,每条指令只完成最基本的功能;

  • 简化寻址方式,每条指令都可以在较短的时间内得到需要的有效地址;

  • 使用 load / store 体系结构,只有 load / store 指令可以进行访存操作;

  • 使用大量寄存器,尽量减少访存操作。

可以看出,这些都是 RISC 的特点。所以说,RISC 更加有利于实现流水线;而具体实现中,一般会将每个阶段的机器周期设为一个时钟周期,这样大部分指令都可以在一个时钟周期完成完成。

5.4.2 流水线冒险

在指令流水线中,往往会出现一些造成 ”断流“ 的情况,导致流水线无法正确处理指令,这就是 流水线冒险

流水线冒险主要有三种:结构冒险数据冒险控制冒险

我们可以先考虑一个具体的案例。假设现在有一个五级流水线,5个阶段设计分别为:取指令(FI)、指令译码/读寄存器(ID)、执行/计算有效地址(EX)、访存(MEM)、结果写回寄存器(WB)。那么不同类型的指令,在各流水段的操作也有所不同:

 

接下来考虑三种不同的流水线冒险。

1. 结构冒险

当流水线中多条指令重叠执行时,不同指令可能会争用同一功能部件而产生资源冲突,这就是 结构冒险,也称为资源相关。

例如,取指阶段 IF 和访存阶段 MEM 都需要访问主存,而冯诺依曼架构的计算机大多会把指令和数据保存在同一个存储器中、且只有唯一的访问口。那么如果在某个时钟周期内,流水线上某一条指令处在 IF 阶段、另一条指令处在 MEM 阶段,就会发生访存冲突。

 

上表中,在第 4 个时钟周期,第 i 条指令 LOAD 处于 MEM 段,正在访问主存;而同时第 i + 3 条指令处于 IF 段,也需要访问主存取指:于是产生了冲突。

解决方案是可以让后一条指令暂停一个时钟周期,等前一条指令完成访存操作后,再开始取指。

 

另一种方案是,可以设置两个独立的存储器,分别存放指令和数据,这样就可以从根本上避免冲突。另外也可以采用 ”指令预取“ 技术,在 CPU 中设置指令队列,将指令预先取出来放到队列中排队。

2. 数据冒险

在流水线中,指令之间可能会有数据的关联。如果一条指令的执行,需要用到之前指令的计算结果;那么当之前的指令尚未执行结束时,下一条指令就直接开始读取数据,就会产生冲突。这种情况被称为 数据冒险

例如,流水线中有下面两条连续的指令:

ADD R~1~,R~2~,R~3~ (R~2~)+(R~3~)→ R~1~

SUB R~5~,R~1~,R~4~ (R~1~)-(R~4~)→ R~5~

这里,第一条指令将 R~2~ 和 R~3~ 中的数据相加,结果放入 R~1~;然后第二条指令又将 R~1~ 中的数据取出,跟 R~4~ 的值做减法,结果写入 R~5~。

在不采用流水线时,按照顺序执行是完全没有问题的;但当采用了流水线结构后,这种 ”先写后读“ 的顺序就发生了变化:

 

在第 3 个时钟周期,SUB 指令就开进入指令译码、读取寄存器 R~1~ 的数据了;而要到第 5 个时钟周期,ADD 指令才会将真正的计算结果写回。”先写后读“ 就变成了 ”先读后写“,产生了数据相关的冲突。

根据指令间对数据读写操作的先后顺序,数据冒险可以分成三类:

  • 写后读(Read After Write,RAW):也就是先写后读,如果试图写入前就读取,就会读出错误的 内容;

  • 读后写(Write After Read,WAR):也就是先读后写,如果试图读取前就写入,就会读出错误的 内容;

  • 写后写(Write After Write,WAW):两次连续的写入,如果改变了写入顺序,最后保存的数据就是先写入的值;

如果只考虑按顺序流动的流水线,那其实只会出现 RAW 相关的情况;而假如是非按序流动的流水线,允许后面的指令超过前面的指令、先流出流水线,那就还可能发生 WAR 和 WAW 的情况。

解决数据冒险可以采用下面的方法:

(1)后推法

要想解决数据冒险,最简单的解决方案,还是先将后面的指令暂停,等前面指令完成、生成所需的结果之后再继续进行。这种方法称为 后推法

例如,我们再增加几个指令,构成一个指令序列:

ADD R~1~,R~2~,R~3~ (R~2~)+(R~3~)→ R~1~

SUB R~5~,R~1~,R~4~ (R~1~)-(R~4~)→ R~5~

AND R~7~,R~1~,R~6~ (R~1~)AND(R~6~)→ R~7~

OR R~9~,R~1~,R~8~ (R~1~)OR(R~8~)→ R~9~

XOR R~11~,R~1~,R~10~ (R~1~)AND(R~10~)→ R~11~

第一条 ADD 指令将 R~2~ 和 R~3~ 相加的结果写入 R~1~,之后的 4 条指令 SUB、AND、OR、XOR 都要使用 R~1~ 中的值作为一个源操作数。这里就出现了 RAW 数据冒险。

 

如果采用后推法,将后续指令延迟到 ADD 指令完成写回 WB 阶段之后,就可以解决数据冒险:

 

(2)专用通路技术(数据旁路技术)

另一种解决方案是采用定向技术,也叫 专用通路技术旁路技术

基本思想是,上一条指令的运行结果,在 EX 阶段就已经产生;那就不必等到上一条指令后续阶段全部完成(写回寄存器),可以直接将结果送到后续指令需要的地方。这样流水线就可以不发生停顿。

由于需要对后续指令进行数据的定向传送操作,所以应该加入另外的部件。

 

上图就是一个带有旁路技术的 ALU 部件。ADD 指令执行的结果,会存入 暂存器 中;而暂存器的结果又会通过旁路通道,经多路开关直接送回 ALU 参与后续的计算。这里的定向传送,只发生在 ALU 内部。

3. 控制冒险

控制冒险 主要是由 转移指令 引起的。当遇到条件转移指令(分支指令)时,由于只有上一条指令执行结束才能知道是否跳转,因此一般我们只能采用猜测法,默认不发生跳转、继续取下一条指令;如果真的发生了跳转,这时之前的操作全部作废,需要重新按照 PC 跳转的位置取指执行。这就破坏了流水线的连续流动。

还是用之前的六级流水线的例子,假设指令 3 是一条条件转移指令。

 

指令 3 是条件转移指令,所以只有在第 7 个时钟周期(时间单元)、指令 2 执行完毕之后才能判断是否进行跳转:如果不跳转,则继续执行指令 4;如果跳转,则执行指令 15。

采用猜测法,默认不跳转,所以流水线继续取指令 4,并正常向前流动;但当到第 7 个时钟周期时,发现结果满足条件,需要进行跳转,则之前第 4、5、6、7 个时钟周期所做的操作全部作废,第 8 个时钟周期重新取指令 15,继续流水线的流动。在第 9 ~12 个时钟周期内,没有指令完成,这是预测失败带来的转移损失。

据统计,转移指令大约占到了程序总指令数的 1/4,所以控制冒险会严重影响流水线的性能。为了解决控制冒险,可以采用下面的方法:

  • 尽早判别转移是否发生,尽早生成转移目标地址。

  • 预取转移成功和不成功两个控制流方向上的目标指令。

  • 加快和提前形成条件码。

  • 提高转移方向的猜测率。

5.4.3 流水线的性能指标

流水线的性能,一般用三项指标来衡量:吞吐率加速比效率

 

1. 吞吐率

单位时间内流水线所完成的指令数,或者输出结果的数量,称为流水线的 吞吐率。吞吐率又有两种:

(1)最大吞吐率

最大吞吐率是指流水线在连续流动达到稳定状态后,所获得的吞吐率。对于 m 段的指令流水线,假设各段的时间均为 Δt,那么达到稳定状态后,每个 Δt 都会完成一条指令;所以最大吞吐量就是:

$$
T_{pmax} = \frac{1}{Δt}
$$

流水线只有在达到稳定、连续流动的时候,才能获得最大吞吐率。实际上,流水线在开始时有一段建立时间,结束时又有一段排空时间;另外还有各种冒险因素使流水线无法连续流动,所以实际吞吐率总是小于最大吞吐率。

(2)实际吞吐率

假设流水线中总共有 n 条指令,完成 n 条指令的总时间为 t,那么实际吞吐率就是 n / t。同样,对于 m 段指令流水线,若各段时间为 Δt,那么连续处理 n 条指令时,除了第 1 条指令需要 m · Δt 时间,其它 n - 1 条指令都是每隔 Δt 就会完成一条。所以:

$$
t = m \cdot Δt \space + (n-1)\cdot Δt
$$

这样,实际吞吐率的计算公式为:

$$
T_p = \frac{n}{t} = \frac{n}{m \cdot Δt \space + (n-1)\cdot Δt} = \frac{n}{(m+n-1)\cdot Δt}
$$

带入最大吞吐率的公式,可以得到:

$$
T_p = \frac{n}{m+n-1} \cdot T_{pmax} = \frac{1}{1+(m-1)/n} \cdot T_{pmax}
$$

很明显,实际吞吐率 T~p~ < T~pmax~,当 n → +∞ 时,T~p~ 趋近于 T~pmax~。

2. 加速比

流水线的加速比,指的是采用流水线处理指令的速度,和同样功能的非流水线的速度之比。

如果流水线每段时间均为 Δt,那么同样是完成 n 条指令,使用 m 段流水线需要的时间为:

$$
t = m \cdot Δt \space + (n-1)\cdot Δt
$$

而不使用流水线时需要时间为

$$
t' = n \cdot m \cdot Δt
$$

则加速比 S~p~ 为:

$$
S_p = \frac{v}{v'} = \frac{t'}{t} = \frac{n \cdot m \cdot Δt}{m \cdot Δt \space + (n-1)\cdot Δt} = \frac{n \cdot m}{m+n-1} = \frac{m}{1+(m-1)/n}
$$

可以看出,当 n → +∞ 时,S~p~ 趋近于 m,也就是流水线的最大加速比等于流水线的段数。

3. 效率

流水线的效率,是指流水线中各功能段的利用率。

因为流水线有建立时间和排空时间,所以各功能段设备不会一直处于工作状态,会有一段空闲时间。假设 m 段流水线各段时间均为 Δt,我们可以将每个功能段(空间)在每个 Δt(时间)内的工作状态画出来,这就是流水线的 “时空图”:

 

上图是一个 4 段流水线(m = 4)的时空图,那么处理 n 条指令一共需要时间为 m · Δt + (n-1) · Δt,在时空图上总的时空区域为:m · (m+n-1) · Δt ;而各段真正处于工作的时空区域为:m · n · Δt。

一般就用流水线各段处于工作的时空区域,和总时空区域的比值,来衡量流水线的效率。计算公式为:

$$
E = \frac{m \cdot n \cdot Δt}{m \cdot (m+n-1) \cdot Δt} = \frac{n}{m+n-1} = \frac{S_p}{m} = T_p \cdot Δt
$$

5.4.4 流水线的多发技术

流水线技术大大提升了 CPU 的处理效率,给计算机系统结构带来了重大改进。通过开发流水线的多发技术,又可以进一步地对流水线进行改进和提升。

所谓的多发技术,就是设法提升流水线的并行效果,尽量在一个时钟周期内可以完成更多的指令。常见的多发技术有 超标量技术超流水线技术超长指令字技术

1. 超标量技术

超标量(SuperScalar)流水线技术也叫做动态多发射技术,它是指在每个时钟周期内,可以同时并发多条独立的指令;也就是以并行操作的方式,将两条或两条以上的指令编译执行。

 

上图是普通流水线和超标量流水线的对比。这里是一条四级流水线,处理一条指令分为 4 个阶段:取指(IF)译码(ID)执行(EX)写回(WR)。假设每段时间就是一个时钟周期。

可以看到,普通流水线每个时钟可以产生一条指令的结果;而超标量流水线每个时钟周期都可以产生多条指令的结果。

要实现超标量技术,CPU 中要配置多个功能部件和指令译码电路,以及多个寄存器端口和总线,以便能够实现同时执行多个相同阶段的操作。另外,还需要编译器采用编译优化技术,找到能并行执行的指令进行调配。

2. 超流水线技术

超流水线(SuperPipeline)技术是将一些流水线寄存器插入到流水线段中,相当于把每个阶段再进行细分。

 

上图中原来的一个时钟周期又分成了三段。这样超流水线的处理器可以在一个时钟周期内,让功能部件处理 3 条指令;相当于流水线是以 3 倍于原时钟频率的速度运行。

3. 超长指令字技术

超长指令字技术(VLIW)也称静态多发射技术,它和超标量技术有共同特点,都是采用多条指令在多个部件中并行处理的架构,从而在一个时钟周期内能够流出多条指令。

 

跟超标量技术的区别在于:超标量的指令来自同一标准的指令流;而超长指令字则是由编译器挖掘出指令间潜在的并行性,然后把多条能并行的指令直接组合成一条指令——这是一条具有多个操作码字段的超长指令。这条超长指令可以控制机器中的多个独立的功能部件同时操作,相当于同时执行了多条指令。

超长指令字技术比超标量有更高的并行处理能力,但对编译器优化的要求更高,对 Cache 容量的要求更大。

5.5 数据通路和控制信号

5.5.1 数据通路的概念和功能

计算机中,将各个功能部件连接起来、可以进行数据传送的路径称为 数据通路

 

数据通路的主要功能,就是实现计算机中不同部件间的信息传送和数据交换。在这里我们主要讨论 CPU 内部的数据通路,它描述了信息在 CPU 内各部件之间传递的路径,也包括了路径上流经的部件,比如 ALU、寄存器、中断系统等。

5.5.2 指令周期中的数据流

CPU 的指令周期可以分为取指、间址、执行和中断四个阶段,在不同的阶段,控制器应该产生不同的控制信号。我们可以先来分析一下每个阶段中数据的流动方向,这就是指令周期中的 数据流

1. 取指周期

这里我们只讨论 CPU 中最重要的 4 个寄存器 PC、IR、MAR 和 MDR。MAR 与地址总线相连,存放要访问的存储单元地址;MDR 与数据总线相连,存放从主存中读出的或者要写入主存的数据;PC 存放要执行的指令地址,有 “自动加 1” 的计数功能;IR 存放正在执行的指令。

 

① 将 PC 中存放的当前指令的地址,送到 MAR;

② 将 MAR 中的地址送至地址总线;

③ CU 发出读命令,经控制总线到达主存;

④ 主存中对应存储单元的内容(指令)经数据总线,传送给 MDR;

⑤ MDR 中的内容送至 IR,并将操作码交给 CU 进行译码;

⑥ CU 控制 PC 内容加 1,形成下一条指令的地址。

2. 间址周期

取指周期结束后,CU 就会检查 IR 中的内容,判断寻址方式;如果有间接寻址,就执行间址周期中的操作。

 

① 取 MDR(或 IR)中的地址码,记作 Ad (MDR),送至 MAR;

② 将 MAR 中的地址送至地址总线;

③ CU 发出读命令,经控制总线到达主存;

④ 主存中对应存储单元的内容(地址)经数据总线,传送给 MDR;

⑤ 将有效地址传送到 IR 的地址字段(在一些机器中,这一步可以省略)。

3. 执行周期

在执行周期,可能会涉及到对 ALU 的操作、寄存器之间的数据传递,以及对主存的读写操作。因为不同的指令在执行周期会有不同的操作,对应的数据流也会有所不同,所以不能用同一的数据流图来表示。

4. 中断周期

CPU 进入中断周期后,会进行一系列的操作;核心部分就是要将当前程序执行的状态保存下来,等到中断服务程序执行完毕后再返回继续执行。

 

① CU 将一个特殊地址(比如 0 地址)送至 MAR,这个地址对应的存储单元保存程序的断点信息,主要就是 PC 的值;

② 将 MAR 中的地址送至地址总线;

③ CU 发出写命令,经控制总线到达主存;

④ 将 PC 的内容(程序断点位置)传送给 MDR;

⑤ 将 MDR 中的断点信息经数据总线,存入主存中;

⑥ CU 将中断服务程序的入口地址送至 PC,准备执行中断服务程序。

5.5.3 指令周期的微操作

数据通路上的每条线路,都会有一个允许数据进出的 “门”,可以控制当前线路是否开启数据的传送;一般可以用三态门来实现。

 

上面的三态门可以等效地看成:

 

在不同的时钟周期,给出不同的三态门控制端 EN 信号,就可以精确地控制每条线路依次导通。

CPU 的控制器可以根据每条指令的具体操作,生成一系列的控制信号,控制数据通路上每个三态门在不同时钟周期的 “开” 和 “关”,从而起到控制指令执行的过程。这样的控制信号,就被称为 “微操作” 或者 “微指令”。

下面我们依然根据指令周期的 4 个阶段,分别分析对应的微操作命令。

1. 取指周期

对应上一小节分析的取指令过程,可以归纳为以下几个微操作:

 

2. 间址周期

 

3. 执行周期

在执行周期,不同指令的微操作是不同的。这里我们将指令按照功能分为非访存指令、访存指令和转移指令来进行分析。

(1)非访存指令

这类指令一般只是执行一步简单的操作,在执行周期不访问主存。

 

(2)访存指令

这类指令在执行阶段需要访问存储器,例如一个操作数在主存中的加法指令,以及从主存中存取数据的指令。简单起见,我们这里只讨论直接寻址的情况。

 

(3)转移指令

转移指令只是通过对 PC 的更改,实现指令执行的跳转,因此执行阶段也不需要访问主存。

 

4. 中断周期

 

5.5.3 数据通路的基本结构和控制信号

CPU 外部和主存、I/O 设备的连接,一般采用总线方式。而 CPU 内部数据通路的结构,其实就是 CPU 内部的连线方式。根据 CPU 内是否采用总线方式进行连接,数据通路可以分为两种结构。

1. 不采用 CPU 内部总线

如果不采用 CPU 内部总线的方式,就需要将 CPU 内的所有需要进行数据传递的部件单独连接起来,相当于为两者之间搭建了一条 “专线”,所以这种方式也叫做 专用数据通路 方式。

这种专用数据通路中所有的路径都是 “专线专用”,不存在冲突,因此性能更好;但所有部件之间都需要进行线路连接,布线比较复杂,硬件成本较高,可扩展性比较差。

 

上图就是未采用总线方式时的数据通路和控制信号。为了简便这里没有画出每个部件输入输出通路的控制门,只用一个小圆圈代替,上面用箭头标出了每个门电路的控制信号 C~i~ 。


接下来以一条采用了间接寻址的加法指令为例:

<span style="background-color:#f8f8f8"><span style="color:#333333">   ADD @ X</span></span>

这条指令中 @ 是间接寻址标志,X 是存放操作数地址的存储单元地址。需要间接寻址取出操作数,与寄存器 ACC 的值相加,得到的结果再写回 ACC 中。我们依然按照指令周期的不同阶段来进行分析:

(1)取指周期

 

① 控制信号 C~0~ 有效,打开 PC 送往 MAR 的控制门:(PC)→ MAR;

② C~1~ 有效,打开 MAR 送往地址总线(AB)的输出门:(MAR)→ M(主存);

③ CU 通过控制总线(CB)向主存发出读命令(R):1 → R;

④ C~2~ 有效,打开数据总线(DB)送至 MDR 的输入门:M (MAR) → DB → MDR;

⑤ C~3~ 有效,打开 MDR 和 IR 之间的控制门,将指令传送至 IR:(MDR)→ IR;

⑥ C~4~ 有效,打开指令的操作码送往 CU 的输出门:OP(IR) → CU;

CU 在指令操作码和时钟信号的控制下,就可以进一步产生各种控制信号了。

⑦ PC 内容加 1: (PC)+ 1 → PC

(2)间址周期

 

① C~5~ 有效,打开 MDR 和 MAR 之间的控制门,将指令中的形式地址送至 MAR:Ad (MDR) → MAR;

② C~1~ 有效,打开 MAR 送往地址总线(AB)的输出门:(MAR)→ AB → M(主存);

③ CU 通过控制总线(CB)向主存发出读命令(R):1 → R;

④ C~2~ 有效,打开数据总线(DB)送至 MDR 的输入门,将有效地址写入 MDR:M (MAR) → DB → MDR

至此,就获取到了指令操作数的有效地址。

(3)执行周期

 

① C~5~ 有效,打开 MDR 和 MAR 之间的控制门,将有效地址送至 MAR: (MDR) → MAR;

② C~1~ 有效,打开 MAR 送往地址总线(AB)的输出门:(MAR)→ AB → M(主存);

③ CU 通过控制总线(CB)向主存发出读命令(R):1 → R;

④ C~2~ 有效,打开数据总线(DB)送至 MDR 的输入门,将操作数存入 MDR:M (MAR) → DB → MDR

至此,就获取到了指令需要的操作数。

⑤ C~6~、C~7~ 同时有效,打开寄存器 ACC 和 MDR 连接 ALU 的控制门;

⑥ 通过 CPU 内部控制线对 ALU 发出 “ADD” 加法指令的控制信号,完成 ACC 和 MDR 内容的相加;

⑦ C~8~ 有效,打开 ALU 通往 ACC 的控制门,将计算结果存入 ACC:(ACC)+(MDR)→ (ACC)

至此,就完成了加法运算的执行过程。


还有一些控制信号这条指令没有涉及到:C~9~ 和 C~10~ 分别是控制 PC 的输出和输入的控制信号;C~11~ 和 C~12~ 分别是控制 ACC 的输出和输入的控制信号。

2. 采用 CPU 内部总线

类似于系统总线,CPU 内部也可以采用总线方式连接,这种总线也叫做 片内总线。片内总线就是 CPU 内部所有部件的公共数据通路,寄存器之间、寄存器与 ALU 之间都由片内总线连接。

如果只有一条片内总线,这样的结构称为 CPU 内部单总线。这种结构比较简单,硬件容易实现和进行扩展,但存在数据冲突的情况,性能较差。

 

上图中就是采用了 CPU 内部总线方式(单总线)的数据通路和控制信号。每个寄存器都与总线直接相连,这里同样省略了控制门的符号,用一个小圆圈代替,上面用箭头标出了对应的控制信号。控制信号的下标为 i 表示这是输入端的控制,下标为 o 表示输出端的控制。

这里的 ALU 增加了两个寄存器 Y 和 Z,主要是因为 ALU 是组合逻辑电路,本身没有存储数据的功能,运算时必须要求两个输入端同时有效;而单总线上同一时刻(一个时钟周期)只能传送一个数据。因此我们可以设置一个输入寄存器 Y 保存其中一个操作数,让它保持不变,另一个操作数则从总线上获得。同样道理,ALU 的输出也不能直接连接总线输出,否则会影响总线上的输入数据,只能先在输出寄存器 Z 中暂存,等到下个时钟周期再输出。


接下来我们依然以加法指令 “ADD @ X” 为例,按指令周期的不同阶段来分析 CU 发出的控制信号。

(1)取指周期

 

① PC~o~ 和 MAR~i~ 有效,PC 中的内容经内部总线送往 MAR:(PC)→ MAR;

② CU 通过控制总线向主存发出读命令(R):1 → R;

③ 主存通过数据总线,将 MAR 中地址所对应存储单元的内容(指令)送至 MDR:M (MAR) → MDR;

④ MDR~o~ 和 IR~i~ 有效,将 MDR 的内容送至 IR:(MDR)→ IR;

至此,指令送至 IR,操作码字段交给 CU,就可以进一步产生各种控制信号了。

⑤ PC 内容加 1: (PC)+ 1 → PC

(2)间址周期

 

① MDR~o~ 和 MAR~i~ 有效,将指令的形式地址经内部总线送至 MAR:Ad (MDR) → MAR;

② CU 通过控制总线向主存发出读命令(R):1 → R;

③ 主存通过数据总线,将 MAR 中地址所对应存储单元的内容(有效地址)送至 MDR:M (MAR) → MDR;

至此,就获取到了指令操作数的有效地址。

(3)执行周期

 

① MDR~o~ 和 MAR~i~ 有效,将有效地址经内部总线送至 MAR:(MDR)→ MAR;

② CU 通过控制总线向主存发出读命令(R):1 → R;

③ 主存通过数据总线,将 MAR 中地址所对应存储单元的内容(数据)送至 MDR:M (MAR) → MDR;

至此,就获取到了操作数。

④ MDR~o~ 和 Y~i~ 有效,将操作数送至寄存器 Y,直接作为 ALU 的一个输入:(MDR)→ Y;

⑤ ACC~o~ 和 ALU~i~ 有效,同时 CU 向 ALU 发出 “ADD” 加法指令的控制信号,完成 ACC 和 MDR 内容的相加;得到的结果直接送往寄存器 Z:(ACC)+(Y)→ Z;

⑥ Z~o~ 和 ACC~i~ 有效,将运算结果写入 ACC:(Z)→ ACC

至此,就完成了加法运算的执行过程。


现代计算机的 CPU 都集成在一个芯片内,所以采用片内总线的方式可以极大地节省连线,使芯片密度更高、布局更为合理,也更加容易扩展。

而为了提升性能,解决单总线结构的数据冲突问题,可以增加总线的数量,这就是 CPU 内部多总线方式。这种结构的 CPU 内部有多条公共通路;相比单总线一个时钟内只允许传送一个数据,多总线可以同时传送多个数据,效率更高。

5.6 控制器的原理和设计

控制单元 CU 控制一条指令执行的过程,实质上就是发出控制信号、依次执行一个微操作序列的过程。很明显,不同的指令中包含的微操作数量不等,每个微操作的复杂程度也不同,所以每条指令的执行时间都是不同的。

在 CPU 内,可以通过时钟信号产生节拍,来控制每个微操作的执行。一个指令周期可以包含多个机器周期,每个机器周期又可以包含多个节拍,这样就构成了 多级时序系统

控制不同的微操作序列,需要产生不同的时序控制信号。至于如何形成这些控制信号,可以采用不同的时序控制方式,这就被称为 CU 的 控制方式

5.6.1 CU 的控制方式

CU 的控制方式,主要有 同步控制异步控制联合控制人工控制 四种。

1. 同步控制

最常见的控制方式就是 同步控制。这种方式下 CPU 有一个统一的时钟,所有指令的每一个微操作,执行顺序都是事先确定好的,根据时钟信号在不同的时钟周期依次执行。

根据机器周期的不同设置方式,同步控制又有三种不同的方案:

(1)采用定长的机器周期

这是最简单的方式,不论指令对应的微操作有多少、也不管微操作有多复杂,一律采用统一的机器周期来执行各种不同的指令,每个机器周期包含相同的节拍数,每个节拍的宽度完全一致。

 

这样的话,机器周期的选择必须要以执行最慢的指令和微操作为准,对于比较简单的指令就会有时间上的浪费。

(2)采用不定长的机器周期

采用不定长的机器周期时,每个机器周期内的节拍数可以不等,这样就可以解决微操作执行时间不统一的问题了。

 

一般会把大多数微操作安排在一个较短的机器周期内完成,而对于比较复杂的微操作,则通过延长机器周期来解决。

(3)中央控制和局部控制相结合

这种方式相当于是定长和不定长机器周期的结合。将机器的大部分指令安排在统一的机器周期内完成,称为 中央控制,这个机器周期一般比较短;而将少数复杂指令中的一些操作,通过插入一些额外的节拍来进行处理,这称为 局部控制

 

局部控制的每个节拍跟中央控制的节拍宽度相同,个数则是不确定的。

2. 异步控制

异步控制方式不存在统一的时钟信号,没有固定的周期节拍和时钟同步;每条指令和每个微操作耗费的时间都以它们在电路中完成为准。

这种控制方式 CPU 没有空闲时间,利用率更高;但微操作的时序就需要有专门的 “应答线路” 来进行控制,结构会更加复杂。

3. 联合控制

联合控制方式就是同步控制和异步控制的结合。这种方式下,对于指令中大部分一致的微操作,采用同步控制;而对于小部分特殊的、时间难以确定的操作,则采用异步控制。

4. 人工控制

为了调试机器和软件开发的需要,可以在机器面板或者内部设置一些开关或者按键,由人工进行操作控制。例如,一些机器设有 Reset(复位)键,可以人工恢复初始状态。


确定了 CU 的控制方式,就可以设计具体的控制单元了。根据产生微操作控制信号的方式不同,控制器又可以分为 组合逻辑控制器微程序控制器

5.6.1 组合逻辑控制器

组合逻辑控制器直接通过组合逻辑电路的设计,根据当前的指令操作码、工作周期标志以及时钟信号生成对应的控制信号(微操作)序列,实现控制器的各种功能。也叫做 硬布线控制器

1. 基本原理和结构框图

组合逻辑控制器的控制信号,都是由控制单元(CU)产生的。它的输入是时钟信号、操作码译码之后的信号以及条件码标志位。

 

存放在 IR 中的 n 位操作码可以经过一个译码器,产生 2^n^ 个输出;于是每种操作码都会对应一个输出信号送至 CU。

CU 的时钟信号就是一个脉冲序列,让它通过一个节拍发生器(计数器)就可以产生一个宽度等于时钟周期的节拍序列。在每个节拍内,可以保证完成一次微操作,数据信息能够完成数据通路中的流动。这样,CU 就可以按照节拍来发出控制信号的序列了。

2. 微操作的节拍安排

接下来就可以将每个工作周期中的微操作,安排到对应的节拍中了。

安排微操作的节拍应该遵循以下基本原则:

① 微操作的先后顺序不能发生改变;

② 如果是控制不同部件的微操作,可以同时执行的话,应该尽可能安排在同一个节拍内;

③ 如果有些微操作耗时不长,那可以将多个这样的微操作安排在一个节拍内完成,并且允许它们有先后次序。

现在我们假设采用定长机器周期的同步控制方式,每个机器周期包含 3 个节拍。以上一节讨论的 10 条不同指令为例,可以安排微操作的节拍如下:

(1)取指周期

 

(2)间址周期

 

(3)执行周期

同样,还是针对不同类型的指令,分别分析微操作的节拍安排。

1)非访存指令

非访存指令一般都只有很简单的操作,因此可以直接把微操作安排在一个节拍内。可以选择 T~0~ ~ T~2~ 的任一节拍,其余节拍空闲。

这里以 CLA 指令为例,把它的微操作安排在 T~2~ 节拍内:

 

2)访存指令

访存指令中需要进行访问主存操作,微操作的节拍安排可以跟取指、间址周期类似。

 

3)转移指令

同样,转移指令微操作比较简单,可以在 T~0~ ~ T~2~ 任选一个节拍进行安排。

 

(4)中断周期

在指令周期的最后阶段,CPU 会向所有中断源发出中断查询信号,若检测到有中断请求,在允许中断的条件下就会进入中断周期。中断周期的微操作节拍安排如下:

 

3. 组合逻辑设计

安排好了指令微操作的节拍,接下来就可以设计组合逻辑电路,产生需要的控制信号了。

组合逻辑设计主要由三步来完成:列出所有微操作命令的操作时间表;写出每一个微操作对应控制信号的逻辑表达式;根据逻辑表达式画出组合逻辑电路图。

(1)列出微操作命令的操作时间表

根据微操作的节拍安排,可以将所有指令中每个工作周期、每个节拍需要做的微操作全部列出来,这就是 操作时间表。下表中列出了我们当前所考虑的 10 条指令,如果某条指令需要执行某个微操作,那么对应的格子填 “1”。

 

这样,我们就知道了对于某一条指令,在某个节拍内需要给出什么样的控制信号。

(2)写出微操作的逻辑表达式

根据操作时间表,可以对每个微操作控制信号进行真值判断,得到微操作命令的初始逻辑表达式。经过化简就可以得到最简逻辑表达式,从而可以通过电路进行实现。

例如,对于微操作 M(MAR)→ MDR,逻辑表达式为:

$$
\begin{align*} &M(MAR) → MDR \newline &= FE\cdot T_1 \,+ IND\cdot T_1 \,(ADD+STA+LDA+JMP+BAN) \,+\,EX\cdot T_1(ADD+LDA) \newline &= T_1 \, \{\, FE\, +\, IND \, (ADD+STA+LDA+JMP+BAN) \, + EX \, (ADD+LDA)\, \} \end{align*}
$$

这里的 FE、IND、EX 是工作周期标志,T~1~ 是节拍,ADD、STA、LDA、JMP、BAN 代表对应指令,来自操作码译码器的输出选择。

(3)画出微操作命令的逻辑电路图

对于每一个微操作,有了它的逻辑表达式,就可以画出对应的组合逻辑电路图。例如,微操作 M(MAR)→ MDR 的逻辑电路图为:

 

组合逻辑控制器的特点是纯硬件电路实现,因此速度更快、性能更好;设计起来思路清晰、简单明了。但是由于每个微操作命令都对应着一套逻辑电路,因此结构比较复杂,也不够规范;而且逻辑线路是固定的,不易扩展和修改。

组合逻辑控制器通常应用在 RISC 架构中。

5.6.2 微程序控制器

组合逻辑控制器线路庞杂,为了克服这个缺点,可以采用类似 “存储程序” 的方法,来形成具体的微操作序列;这被叫做 “微程序”,采用这种技术的控制器就被称为 微程序控制器

1. 基本原理和结构框图

微程序控制器的基本思想是,将每条机器指令编写成一个 微程序,每一个微程序包含若干条 微指令,每一条微指令对应一个或几个微操作命令。然后把这些微程序存入一个 控制存储器(简称控存,CM)中,用读取指令的方式将它们读出依次执行就可以了。

 

这样,每一条机器指令都可以对应着一个微程序。对于取指、间址、中断周期,所有指令的操作都是相同的,因此可以统一编制成一个微程序;而每条指令执行阶段的操作则单独对应一个微程序。这样,控制存储器中微程序的个数,就是机器指令的数量,再加上对应取指、间址、中断周期的 3 个微程序。

微指令的基本格式,应该包含两个字段,前面是操作控制字段,对应着微操作命令,可以发出各种控制信号;后面是顺序控制字段,可以指出下条微指令的地址(简称 下地址),从而控制微指令序列的顺序执行。

 

微程序控制单元的结构如下所示:

 

  • 控制存储器:微程序控制单元的核心部件,存放着全部的微程序;显然控存可以采用 ROM 实现。

  • CMAR:控存地址寄存器,用来存放准备读取的微指令地址;

  • CMDR:控存数据寄存器,用来存放从控存读出的指令;

  • 顺序逻辑:用来形成下一条微指令的地址,用来控制微指令序列的执行顺序。

2. 微程序控制器的工作过程

我们可以结合一个具体的例子,来说明微程序控制器的工作过程。假设一段用户程序,有下面的两条汇编指令:

<span style="background-color:#f8f8f8"><span style="color:#333333">  LDA  X
  ADD  Y
  ...</span></span>

当执行到第一条指令 LDA X 时,PC 的值为这条指令在主存中存放的地址。控制单元 CU 将执行以下的操作。

(1)取指阶段

首先进入取指周期,微程序控制单元将执行取指阶段的每一个微操作。

 

① 将取指周期的微程序首地址 M 送至 CMAR:M → CMAR;

② 取微指令,将控存 M 地址单元中的微指令取出,送至 CMDR:CM (CMAR) → CMDR;

③ 产生微操作命令,微指令的操作控制字段中为 “1” 的各位发出控制信号,执行微操作 (PC) → MAR,1 → R;

④ 形成下一条微指令的地址。当前微指令的顺序控制字段指出了下地址为 M + 1,所以将 M + 1 送至 CMAR:Ad (CMDR) → CMAR;

⑤ 取下一条微指令,将控存 M + 1 地址单元中的微指令取出,送至 CMDR:CM (CMAR) → CMDR;

⑥ 产生微操作命令,微指令的操作控制字段中为 “1” 的各位发出控制信号,执行微操作 M (MAR) → MDR,(PC)+ 1 → PC;

⑦ 形成下一条微指令的地址。当前微指令的顺序控制字段指出了下地址为 M + 2,所以将 M + 2 送至 CMAR:Ad (CMDR) → CMAR;

⑧ 取下一条微指令,将控存 M + 2 地址单元中的微指令取出,送至 CMDR:CM (CMAR) → CMDR;

⑨ 产生微操作命令,微指令的操作控制字段中为 “1” 的各位发出控制信号,执行微操作 (MDR)→ IR,

OP (IR) → CU。

至此,第一条指令 “LDA X” 已经取出并保存到了 IR 中。

(2)执行阶段

简单起见,我们假设这条指令采用了直接寻址,因此跳过间址周期,直接进入执行周期。

 

① 根据指令的操作码,形成取数指令的微程序首地址 Q,并送至 CMAR:OP (IR) → 微地址形成部件 → CMAR;

② 取微指令,将控存 Q 地址单元中的微指令取出,送至 CMDR:CM (CMAR) → CMDR;

③ 产生微操作命令,微指令的操作控制字段中为 “1” 的各位发出控制信号,执行微操作 Ad (IR) → MAR,1 → R;

④ 形成下一条微指令的地址。当前微指令的顺序控制字段指出了下地址为 Q + 1,所以将 Q + 1 送至 CMAR:Ad (CMDR) → CMAR;

⑤ 取下一条微指令,将控存 Q + 1 地址单元中的微指令取出,送至 CMDR:CM (CMAR) → CMDR;

⑥ 产生微操作命令,微指令的操作控制字段中为 “1” 的各位发出控制信号,执行微操作 M (MAR) → MDR;

⑦ 形成下一条微指令的地址。当前微指令的顺序控制字段指出了下地址为 Q + 2,所以将 Q + 2 送至 CMAR:Ad (CMDR) → CMAR;

⑧ 取下一条微指令,将控存 Q + 2 地址单元中的微指令取出,送至 CMDR:CM (CMAR) → CMDR;

⑨ 产生微操作命令,微指令的操作控制字段中为 “1” 的各位发出控制信号,执行微操作 (MDR)→ ACC。

至此,就完成了取数指令 LDA X 的全部操作。

最后一条微指令的顺序控制字段为 M,所以接下来 CPU 又将进入下一条指令 ADD Y 的取指周期,从控存中依次读出微程序的每条微指令、发出一系列的控制信号。

微程序控制单元设计的过程中,关键问题是微指令的操作控制字段怎样去发出控制信号,以及如何具体产生下一条微指令的地址。这跟 微指令的编码方式微地址的形成方式 有关。

3. 微指令的编码方式

微指令的编码方式,主要是指怎样对微指令的操作控制字段进行编码,进而发出对应的控制信号。所以也称为 微指令的控制方式

微指令的编码方式主要有以下几种。

(1)直接编码(直接控制)

最简单的方案,就是让微指令的操作控制字段中,每一位都对应着一个微操作命令;这种编码方式就称为 直接编码方式

 

当控制字段的某位为 “1” 时,就表示控制信号有效,进而打开对应数据通路的控制门;为 “0” 时则表示控制信号无效,控制门关闭。

这种方式实现简单、含义清晰,而且只要微指令从控存中读取出来就立刻可以发出对应的控制信号,速度非常快。不过由于微操作数量众多,因此控制字段的位数也会非常多,可能达到几百位,造成控存容量过大。

(2)字段直接编码

想要减少控制字段的位数,可以通过加一个译码器来解决。不过由于可能会有多个微操作命令同时有效(并行)的情况,因此可以根据并行发出的微命令个数将控制字段再做 “分段”,每段内分别进行译码、对应一个微操作命令。

这种方式由字段直接译码发出微命令,所以叫做 字段直接编码方式,也称为 显式编码

 

这里所谓的 “微命令” 就是 CU 发出的控制信号,而 “微操作” 就是微命令生效后执行的过程,它们是一一对应的关系,所以一般我们不再过多区分。

指令执行过程中,有一些微命令可以同时产生,我们称它们是 “相容” 的;而如果一组微命令不能同时发生,则称它们是 “互斥” 的。字段直接编码时,应该将互斥的一组微命令放在一个字段内;而不同字段的微命令,则是相容的。每个字段的长度可以不同。此外,每个字段还应该留出一种状态,表示不发出这一组中的任何微命令。


例如,某机器的微指令格式中,有 8 个控制字段,每个字段可以分别发出 5、8、3、16、1、7、25、4 种控制信号。那么采用直接编码和字段直接编码方式设计微指令,需要的操作控制字段至少是多少位?

(1)直接编码

微指令操作控制字段的位数,就是总的控制信号数:

$$
5+8+3+16+1+7+25+4=69
$$

(2)字段直接编码

每个字段需要保留一个编码,表示不做任何操作;因此 8 个字段分别需要表示 6、9、4、17、2、8、26、5 种情况。对应的编码位数应该是:3、4、2、5、1、3、5、3,因此总位数为:

$$
3+4+2+5+1+3+5+3=26
$$

很明显,使用字段直接编码可以有效地减少控制字段的位数。


(3)字段间接编码

这种方式在字段直接编码的基础上,又增加了一步转换,即一个字段的某些微命令,还需要受到另一字段译码输出的控制,因此被称为 字段间接编码,也叫 隐式编码

 

这种方式可以进一步减少控制字段位数,但电路更加复杂、而且削弱了微指令的并行控制能力,因此一般只是作为字段直接编码的辅助手段。

(4)混合编码

将直接编码和字段编码(直接或者间接)混合使用,就成了 混合编码方式。这种方式主要是综合考虑微指令字长、灵活性和执行速度各方面的要求。

4. 微指令格式

微指令的格式跟微指令的编码方式有关,通常可以分为水平型微指令和垂直型微指令。

(1)水平型微指令

水平型微指令的主要特点,就是一次能定义并执行多个并行操作的微命令。从编码方式看,前面介绍的直接编码、字段直接编码、字段间接编码以及混合编码都属于水平型微指令。

(2)垂直型微指令

垂直型微指令不强调并行控制的功能,而是采用了类似机器指令中操作码的方式,在操作控制字段中用 “微操作码” 来规定微指令的功能。通常一条微指令中,只有 1 ~ 2 个微命令,控制 1 ~ 2 种操作。

 

相比而言,水平型微指令并行操作能力更强、速度更快、效率更高、灵活性强;而垂直型微指令与机器指令更加类似,语义更加清晰。水平型微指令用较长的微指令结构,换取较短的微程序;而垂直型微指令是以较长的微程序结构,换取较短的微指令结构。

5. 微指令序列地址的形成

微指令执行过程中,在不同阶段,需要采用不同的方式形成后续微指令的地址。

(1)直接由当前微指令的下地址字段给出

大部分微指令都会在下地址字段直接指出下一条微指令的地址,在每一段微程序内部一般都是这种方式。也被称为 断定方式

(2)根据指令的操作码形成

取指周期结束、机器指令被存放入 IR 后,接下来执行阶段微指令的地址,会由微地址形成部件根据操作码产生。

所谓的微地址形成部件,其实也就是一个编码器,它的输入是指令操作码,输出是对应微程序执行阶段的首地址。它可以用 PROM 来实现,以指令的操作码作为 PROM 的地址,相应的存储单元存放微程序首地址。

对于不同的方式,可以用一个 多路选择器,通过对当前一些状态标志的判断来进行选择。

 

除了上面最基本的两种方式,还可以采用以下方法形成微指令序列地址。

(3)增量计数器法

可以发现,大多数情况下后续微指令的地址都是连续的,所以可以借鉴 PC 的功能,采用 “增量计数法” 形成后续微指令的地址:(CMAR) + 1 → CMAR。

(4)分支转移

当遇到转移指令时,微指令也会出现分支,这时必须根据转移方式指明的判别条件,根据各种标志决定下一条微指令的地址。

(5)通过测试网络形成

这种方式微指令的地址码分两部分,高段为非测试地址,需要直接保留;低段则为测试地址,需要结合测试源的信号、通过测试网络来生成。

(6)由硬件生成微程序入口地址

电源接电后,程序第一条微指令的地址(入口地址),可以由专门的硬件电路产生。

另外,当有中断请求且条件满足时,CPU 进入中断周期,这时也可以由硬件产生中断周期微程序的入口地址。同理,当有间接寻址时,也可以由硬件产生间址周期微程序的入口地址。

6. 微程序设计

设计微程序控制器,主要任务其实就是编写各条指令的微程序。

具体步骤也可以分为三步:首先写出所有指令的全部微操作以及节拍安排;其次确定微指令格式;最后编写出每条微指令的二进制代码(称为 微指令码点)。

(1)写出机器指令的微操作以及节拍安排

这里我们用跟组合逻辑设计相同的案例,只考虑 10 条机器指令。简单起见,我们这里不考虑间接寻址和中断的情况,只讨论取指和执行周期。

1)取指阶段

跟组合逻辑设计类似,取指阶段的微操作可以安排在三个节拍内:

 

如果一个节拍内的微操作,都对应一条微指令,那么上面的取指微程序对应着 3 条微指令。

不过我们发现,微程序控制器的所有控制信号都来自于微指令,而微指令都存放在控制存储器中。所以每条微指令执行之前,必须有一个将微指令地址送至 CMAR 的过程。或者说,每条微指令执行结束,应该形成后续地址,交给 CMAR;这必须额外耗费一个时钟周期(节拍)。

 

这样,取指操作就需要 6 条微指令来完成。所有微指令都会由时钟信号的上升沿打入 CMDR 中。

2)执行阶段

同样,执行阶段的微操作是由指令操作码决定的;每条微指令完成之后同样要考虑后续指令地址的形成。

a)非访存指令

 

b)访存指令

 

c)转移指令

 

以上总共有微指令 38 条,不同的微操作有 20 个。

(2)确定微指令格式

确定微指令格式,主要是确定微指令的编码方式、后续微指令地址的形成方式,以及微指令字长 3 个方面。

  • 微指令编码方式:微操作不多,可以采用直接编码方式;

  • 后续地址形成方式:根据指令操作码和微指令的下地址,确定后续微指令地址的形成;

  • 微指令字长:20 个微操作,对应 20 位操作控制字段;38 条微指令,需要 6 位下地址字段。微指令字长至少取 26 位。

进一步分析可以发现,38 条微指令中,19 条都是将后续地址送入 CMAR 的操作。这不仅在微指令上有所浪费,而且相当于每两个时钟周期,才能取出并执行一条微指令。

我们可以将 CMDR 的下地址字段 Ad (CMDR) 直接接到控制存储器的地址线上,在下一个时钟信号的上升沿,就将对应地址的内容(下一条微指令)读取到 CMDR 中。这样就省去了 CMAR,直接用 Ad (CMDR) 取代,而且做到了每一个时钟周期完成一条微指令,大大提升了运行速度。

同样,指令操作码 OP (IR) 通过微地址形成部件产生的后续地址,也可以直接送到控制存储器的地址线上。

 

通过这样的化简,我们就省去了 19 条微指令,并同时省去了 Ad (CMDR) → CMAR 和 OP (IR) → 微地址形成部件 → CMAR 两个微操作。于是总共只需要 38 - 19 = 19 条微指令和 20 - 2 = 18 个微操作。

这样,最少只需要 18 位控制字段和 5 位地址字段。不过为了便于以后扩展,我们可以取 24 位操作控制字段和 6 位下地址字段。微指令格式如下:

 

操作控制字段的第 0 ~ 17 位分别控制的微操作为:

 

(3)编写微程序码点

根据每条微指令的操作控制字段和下地址,就可以得到每一条微指令对应的二进制代码(码点)了。这里我们省略了码点中所有的 0。

 

7. 其它微程序设计技术

微程序控制器中,控制信号是以二进制代码的形式出现,因此不需要像组合逻辑那样考虑逻辑表达式的化简,因此设计更加简便,更容易调试和修改。当然,由于执行每个微操作都要访问控制存储器,所以对控制存储器的速度要求较高。微程序控制器通常应用在 CISC 架构中,目前大多数计算机都会采用微程序设计技术。

微程序设计技术也可以有其它一些方式的扩展。

(1)静态微程序设计

通常指令系统是固定的,对应每一条机器指令的微程序,都是计算机的设计者预先编写好的,这种微程序设计技术称为 静态微程序设计,其控制存储器一般采用 ROM。

(2)动态微程序设计

如果可以通过改变微指令和微程序,来改变机器的指令系统,这种微程序设计技术称为 动态微程序设计。这种方式可以在一台机器上实现不同类型的指令系统,有利于仿真和研究测试,其控制存储器需要采用 EPROM。

(3)毫微程序设计

如果采用两级微程序的设计方法,用第一级微程序来解释机器指令,再用第二级微程序(称为 “毫微程序”)来解释第一级微程序,这样的设计方式就称为 毫微程序设计。组成毫微程序的 毫微指令 是用来解释微指令的。

采用毫微程序设计计算机的优点是,可以用更少的控制存储器空间来达到高度的并行;不过同时运行速度会受到一定程度的影响。

5.7 中断和异常

5.7.1 基本概念和分类

计算机在执行程序的过程中,有时会遇到一些异常情况或者特殊请求;这时就需要计算机暂停正在运行的程序,转而先去处理这些异常或特殊请求,处理结束之后再返回程序的断点处继续执行。这种处理方式就被称为 “中断”,计算机的这种功能就称为 中断处理;实现这种功能所需的软硬件技术统称为 中断技术

很显然,对于一些突发情况和实时传来的外部信号,相比时刻查询的方式,使用中断技术可以大大地提高计算机的整体效率。

   

能够引发中断的因素有很多,比如电源掉电、打印机请求、运算溢出、缺页等等。通常将能够引起中断的各种因素称为 中断源。根据各种中断源的类型,可以对中断进行不同的分类。

1. 中断和异常

从中断的来源看,直观上可以分成两类:一类是由外部设备向 CPU 发出的请求(比如打印),或者突发的外部事件或环境对计算机造成了影响(比如电源掉电),在 CPU 外部产生;另一类是 CPU 在执行指令时遇到的异常情况,在 CPU 内部产生。

通常将 CPU 内部产生的中断称为 异常(Exception),也称作 内中断。异常一般是 CPU 的控制单元(CU)在执行指令时遇到了意外情况、因而必须暂停处理;由于一定会在一条指令执行结束后才发出中断,因此也被叫做 同步中断

与之对应,来自于 CPU 外部其它硬件设备的中断,就被称为 外中断,或者直接叫做(狭义的) 中断(Interrupt)。它们通常是外部设备依照 CPU 时钟随机产生的,因此也被叫做 异步中断

 

事实上,对于中断(狭义)和异常,CPU 的处理流程都是先中止当前正在执行的程序、转而去执行相应的中断处理程序(称为 “中断服务程序”),完成中断响应之后,再回到断点处继续执行。因此,一般可以把它们统称为(广义的)中断

2. 中断和异常的分类

中断请求并不一定都非常急迫、必须立刻响应。CPU 可以对中断进行判别、选择,屏蔽某些中断源的请求;根据是否可以被屏蔽,中断(狭义)又可以分成两类:

  • 可屏蔽中断:可屏蔽中断有 “屏蔽” 和 “非屏蔽” 两种状态;处在屏蔽状态的中断,CPU 将不予以响应。I/O 设备的中断请求都属于可屏蔽中断。

  • 不可屏蔽中断:一些急迫事件 CPU 必须予以响应,发出的中断不能屏蔽。例如电源掉电就属于不可屏蔽中断。

而对于异常,由于它跟指令的执行有关,因此根据中断处理完成后、接下来指令执行的情况,可以进一步进行分类:

  • 故障(Fault):通常可以纠正,一旦纠正则程序可以重新执行原先产生中断的那条指令;也称为 “程序性事故”。例如运算溢出、缺页异常。

  • 陷阱(Trap):这种异常是人为设置的,表示自愿停止现行程序、转入中断处理,因此也叫 “自愿中断”、“自陷”。一般计算机中都会设置陷阱指令,执行到它就会转至别处做特殊处理,完成之后就返回到陷阱指令的下一条指令继续执行。例如调试程序时的断点设置、执行系统调用等。

  • 异常中止(Abort):如果发生了严重的错误,程序无法继续执行,那么就只能把控制权交给异常中止处理程序;而现行程序只能强制停止运行。例如硬件故障、系统表中无效的值等。

3. 中断系统

中断改变了计算机执行程序的顺序,因此需要 CPU 进行特别的处理。为了实现中断处理的功能,CPU 中一般会设置专门的处理机构,这就是 中断系统

中断系统主要需要解决以下一些问题:

  • 中断源怎样向 CPU 发出中断请求;

  • 当有多个中断源同时发出请求时,怎样确定响应顺序;

  • CPU 怎样响应中断请求;

  • CPU 响应中断之后,怎样保护现场;

  • CPU 响应中断后,怎样暂停原程序的执行、找到中断服务程序的入口地址;

  • 中断处理结束后,CPU 如何恢复现场、返回原程序的断点处;

  • 如果中断处理时又有新的中断请求,应该怎样处理。

    接下来我们就通过学习中断系统,来解决这些问题。

5.7.2 中断的检测

在 CPU 的指令周期中,最后一个阶段就是 中断周期。在一条指令执行阶段的最后时刻,CPU 会发出 中断查询信号,检测是否有中断请求。如果有,则进入中断周期进行中断处理。

1. 中断请求标记

不同的中断源都可以向 CPU 发出中断请求,为了区分中断源,中断系统中会专门设置一个 中断请求标记触发器,简称 中断请求触发器,用 INTR 表示。当某个中断源的触发器 INTR~i~ 为 “1” 时,就表示该中断源发来了请求。

这些触发器可以分散在各个中断源的接口电路中,也可以集成在 CPU 中,组成一个 中断请求标记寄存器

 

寄存器里的每一位就对应着一个中断源的请求触发器。任意一个触发器为 1,就表示对应的中断源发出了中断请求。

2. 中断判优

在中断系统中,同一时刻只能响应一个中断源的请求。如果有多个中断源同时发出了请求,那就需要中断系统按照设定好的优先顺序来依次响应;这被称为 中断判优。中断源的优先级是依据重要性和急迫性而定的。

中断判优就是要对一组中断请求按优先级进行 “排队”,可以用硬件和软件两种方式实现。

(1)硬件排队

硬件排队是直接用硬件电路实现中断请求的排队,具体方法很多,主要有两种:一种是在 CPU 内统一设置一个 “排队器”;另一种是在中断源设备的接口电路中分别设置排队器。

 

  • 在 CPU 内设置排队器:各中断源的请求触发器按优先级依次排列,高优先级的请求信号通过接入一个多输入的与非门,来 “封住” 低优先级的中断请求。

 

  • 在接口电路中设置排队器:每个接口电路中都设置一个反相器和一个与非门,与非门的输出接入下一级,就可以实现对更低优先级中断请求的封锁。它们彼此之间直接连成链状,因此也称为 “链式排队器”。

(2)软件排队

软件排队,就是直接通过编写查询程序实现中断请求的排队。

 

程序按照中断源的优先级,从高到低依次判断是否有中断请求,这样就保证了中断响应的顺序。

5.7.3 中断的响应
1. 中断响应的条件

由于在处理中断时,CPU 不应受到新中断请求的打扰,因此需要有 “开关中断” 的功能。在中断系统中,这是由 允许中断触发器 EINT 和专门的指令来实现的。

EINT 可以被 开中断指令 置 “1”,这时表示 CPU 打开了中断功能,允许响应中断源的请求;EINT 也可以被 关中断指令 置 “0”,这意味着 CPU 关闭中断功能,禁止响应中断。

在 x86 架构的 CPU 中,EINT 就对应着 标志寄存器(程序状态字)efl 中的一位,用 IF 表示。

 

因此可以总结,当 EINT = 1 且有中断请求(INTR~i~ = 1)时,CPU 就可以响应中断。

2. 中断响应的时间

很多情况下中断请求的产生是随机的,为了不影响 CPU 的正常工作,需要周期性地在统一的时刻由 CPU 发出中断查询信号,判断当前是否有中断请求。

 

因此 CPU 响应中断的时间,一定是在每条指令执行阶段的结束时刻;相当于在指令周期最后又加入了一个阶段,这就是 中断周期。执行周期结束后,如果有中断请求,则 CPU 进入中断周期;如果没有则直接进入下一条指令的取指周期。

3. 中断响应的过程

CPU 响应中断之后,就会进入中断周期。在中断周期 CPU 会执行一系列的操作,这些操作都是由一条 中断隐指令 完成的。所谓的隐指令,指的是指令系统中本身没有这条指令,它是由硬件自动完成的。具体操作如下:

(1)保护程序断点

将当前 PC 的内容(程序断点位置)保存到主存中。可以存入特定的某个地址(比如 0 地址),也可以存入堆栈中。

(2)寻找中断服务程序的入口地址

在中断周期内,可以用两种方法寻找中断服务程序的入口地址:硬件向量法和软件查询法,分别对应着硬件和软件实现。

(3)关中断

为了在响应中断后,不受到新的中断请求的干扰,在中断周期需要自动做 “关中断” 的操作,禁止 CPU 再次响应中断请求。关中断的具体操作就是将允许中断触发器 EINT 置 “0”,一般直接由硬件电路实现。

对应的微操作如下:

 

5.7.4 中断服务程序
1. 中断服务程序的入口地址

不同的中断请求,需要执行不同的中断服务程序。寻找中断服务程序的入口地址,通常可以采用两种方法:硬件向量法软件查询法

(1)硬件向量法

所谓硬件向量法,就是使用硬件直接产生一个 向量地址,再用这个向量地址找到中断服务程序的入口地址。向量地址由中断向量地址形成部件产生。

中断向量地址形成部件的输入,是来自排队器的输出 INTP~i~ ,它的输出则是一个二进制的 中断向量。一般可以让一个中断源对应一个向量地址,因此这个部件本质上就是一个编码器。

 

通过向量地址寻找中断服务程序入口地址可以采用两种方法。

 

  • 在向量地址对应内存单元放一条 无条件转移指令。这样 CPU 响应中断时,只要将向量地址交给 PC,然后执行这条指令,就可以跳转到中断服务程序入口地址了。

  • 设置 向量地址表。向量地址表存放在主存中,向量地址就是对应存储单元的地址,而存放的内容就是中断服务程序的入口地址。

硬件向量法寻找入口地址速度快,现代计算机中普遍使用这种方法。

(2)软件查询法

用软件寻找中断服务程序入口地址的方法,就叫做软件查询法。各个中断源对应的入口地址,是由程序员(或系统)预先定好、写入程序的;可以结合软件排队的中断判优,确定要响应某个中断请求时,直接跟上一条转移指令、跳转至定义好的入口地址就可以了。

软件查询法不涉及硬件,更加容易维护和扩展,但查询的时间更长。

2. 中断服务程序的流程

不同的中断请求对应的中断服务程序不同,不过它们的处理流程是类似的,一般包括 保护现场中断服务恢复现场中断返回 4 个部分。

(1)保护现场

保护现场通常包含两层含义:一是保存程序的断点,这由中断隐指令完成;二是保存通用寄存器和状态寄存器的内容,这需要中断服务程序完成。

具体操作就是在中断服务程序的起始部分,安排几条存数(Store)指令,将寄存器的内容全部保存到主存中;或者直接用入栈(Push)指令将寄存器内容推入栈中。

(2)中断服务

中断服务是中断服务程序的主体部分,针对不同的中断源,需要做出对应的操作内容。

(3)恢复现场

中断服务程序的结尾部分,还需要恢复现场。对应着之前的保护现场,这一步可以用取数(Load)指令或者出栈(Pop)指令,将保存在主存中的信息恢复到寄存器中。

(4)中断返回

中断服务程序的最后一条指令,通常是一条 中断返回指令,从而返回到原程序的断点处继续执行。

 

如果在 CPU 执行中断服务程序时,又来了一个新的中断请求,这时有两种处理方式:一种是直接不予响应,即 CPU 在响应中断的过程中保持 “关中断” 状态,直到中断服务程序执行结束、返回之前才开中断;另一种则是 CPU 立即响应新的中断请求,这需要在中断服务程序的开始阶段 “开中断”。

5.7.5 中断屏蔽技术
1. 多重中断

如果 CPU 在响应某个中断请求的时候,另一个中断源又提出了一个新的中断请求;而 CPU 立即响应这个请求,暂停正在运行的中断服务程序、转而去运行新的中断服务程序,这就是 中断嵌套,也称为 多重中断。 与之对应,如果 CPU 在响应中断时不去响应新的中断请求,则称为 单重中断

 

实现多重中断,需要满足两个基本条件:

  • 提前设置 “开中断” 指令

由于进入中断周期后,中断隐指令会自动将 EINT 置为 0,因此默认会关中断、不再响应新的中断请求;这时如果希望处理多重中断,就必须在中断服务程序的开始阶段设置 “开中断” 指令来打开中断。

  • 优先级高的中断源有权中断优先级低的中断源

只有优先级别更高的中断源,才能打断正在执行的中断服务程序、优先被响应。


例如,有 A、B、C、D 四个不同的中断源,优先级为 A > B > C > D。在 CPU 执行程序时,在某个指令周期,同时查询到了 B、C 发来的中断请求;之后在执行中断服务程序的过程中,又先后查询到了 D、A 的请求。那么如果允许多重中断,中断处理的时间顺序如下:

 


中断请求如果同时到来(比如上例中的 B、C),我们可以进行中断判优、利用排队器决定它们的先后顺序。但对于多重循环,还需要在中断响应过程中继续判断优先级、决定是否可以打断当前的中断服务程序。这是通过 中断屏蔽技术 来实现的。

2. 中断屏蔽触发器

为了实现对每个中断源的屏蔽,可以在中断请求触发器 INTR 之前增设一个 中断屏蔽触发器 MASK。当 MASK 为 “1” 时,表示该中断源被屏蔽,中断请求不被响应。

屏蔽触发器同样既可以设置在接口电路中、也可以直接集成在 CPU 内。以 I/O 设备作为中断源为例,I/O 设备的接口电路中会有一个 完成触发器 D,它为 “1” 则表示设备已经处于就绪状态、要向 CPU 发出中断请求了。一般情况下,D 的输出可以直接接到中断请求触发器 INTR 的输入;现在则可以与 MASK 的输出经与非门之后、再交给 INTR。

 

这样,当设备已经准备就绪(D = 1),且设备未被屏蔽(MASK = 0)时,来自 CPU 的中断查询信号就会将 INTR 置 “1”,表示该设备发出了中断请求信号。

同样,排队器内也可以加上屏蔽条件,就组成了具有屏蔽功能的排队器。例如集成在 CPU 内的排队器如果加上屏蔽功能,只需要另外将屏蔽信号也接入与非门就可以了:

 

3. 屏蔽字和屏蔽技术

显然,对于每个中断源,中断请求触发器 INTR 和 中断屏蔽触发器 MASK 都是成对出现的。如果将所有屏蔽触发器组合在一起,也可以构成一个 屏蔽寄存器。屏蔽寄存器的内容就称为 屏蔽字

屏蔽字可以在中断服务程序中进行设置,屏蔽字和中断源的优先级是一一对应的。以 16 个中断源 I~1~ ~ I~16~ 的屏蔽字为例,每个屏蔽字都是 16 位:

 

在不同中断源的中断服务程序中,可以设置适当的屏蔽字,就能对优先级不同的中断源进行屏蔽。

例如,在 I~1~ 的服务程序中,将屏蔽字设为全 1,就表示它的优先级最高:在响应 I~1~ 中断请求的过程中,不会再去响应任何新的请求(包括 I~1~ 自己发来的);这相当于就是单重中断。而对于 I~3~ ,在服务程序中将屏蔽字前两位设为 0、后面为 1,表示 I~1~、I~2~ 优先级更高、不被屏蔽,而后面的中断源(包括自己)被屏蔽;于是开中断之后,I~1~、I~2~ 发来的新中断请求就可以打断当前 I~3~ 的中断服务程序,实现了多重中断。

4. 改变处理优先级

使用屏蔽技术还可以改变中断源的优先等级。中断源的优先级,包括两层含义:响应优先级处理优先级

  • 响应优先级:CPU 响应各中断源请求的先后次序,跟排队器有关,一般是硬件线路设定好的,不易改动;

  • 处理优先级:CPU 实际执行中断服务程序的先后次序,可以通过屏蔽技术进行改动。

如果不采用屏蔽技术,那么处理的优先顺序就是按照响应优先级来的。例如,A、B、C、D 四个中断源,优先级别为 A > B > C > D,根据这一次序设计出排队器之后,当它们同时发来中断请求时,处理顺序就是响应顺序:

 

如果采用了屏蔽技术,就可以在不改变响应顺序的前提下,改变 CPU 处理中断的顺序。假如我们希望将它们的处理次序改为 A → D → C → B,那么每个中断源的中断服务程序中应该重新设置屏蔽字:

 

这样,同样还是 A、B、C、D 同时发出中断请求,这时的处理顺序就会变为:

 

显然,采用屏蔽技术改变处理优先级,需要在中断服务程序中重置屏蔽字。这样,中断服务程序的处理流程就会变为:

 

跟之前相比,增加了 置屏蔽字恢复屏蔽字 两步操作。而为了防止在恢复阶段又出现新的中断,需要先关中断、等到恢复现场和屏蔽字之后再次开中断。

5.8 多处理器基本概念

5.8.1 SISD、SIMD、MIMD 和向量处理器

通过改进系统结构,可以有效地提升计算机的性能,一个方向就是 并行处理。指令流水线就是一种典型的并行处理技术,它提供的是 指令 的并行;而另一种思路则是考虑让 数据 的处理并行起来。

1. 费林分类法

美国计算机科学家迈克尔 · 费林(Michael Flynn)根据指令和数据是否并行,将计算机体系结构分为四类:

 

  • SISD(Single Instruction stream, Single Data stream)单指令流单数据流。普通的串行执行结构,CPU 同时只能执行一条指令,处理一条数据;

  • SIMD(Single Instruction stream, Multiple Data streams)单指令流多数据流。单条指令可以同时对应处理多个数据,从而实现了数据的并行处理;

  • MISD(Multiple Instruction streams, Single Data stream)多指令流单数据流。多条指令同时执行,但只处理一条数据,这种架构非常罕见,只是作为一种理论模型;

  • MIMD(Multiple Instruction streams, Multiple Data streams)多指令流多数据流。同时执行多条不同的指令,它们分别处理不同的数据;这种方式实现了线程、指令和数据的全方位并行处理。

2. SIMD 和向量处理器

SIMD 的特点是,设置了多个并行的执行单元,而所有的执行单元都是同步的,执行的指令从同一个 PC 中取出,由同一个控制单元来进行调度管理。SIMD 对应的程序设计风格可以跟 SISD 非常相近,但要求程序中必须存在大量同构的数据,以便实现 数据级并行

向量处理器是 SIMD 架构的一种具体实现,可以并行执行一组数据(向量)的计算任务。

向量处理器的基本原理是,从主存中收集数据,将它们按照顺序放到一组 向量寄存器 中,使用流水线式的执行单元在寄存器中依次进行操作,然后将结果写回主存。

5.8.2 硬件多线程

相比 SIMD,MIMD 提高了更高的并行程度。MIMD 需要同时执行不同的指令流,这就要依赖多个 进程(process) 或者 线程(thread) 同时执行,让处理器时刻保持忙碌状态。

对于一个处理器,可以允许多个线程以重叠的方式,共享处理器的功能部件;当一个线程停顿时,就切换到另一个线程,从而更加充分地利用了硬件资源。这种技术就称为 硬件多线程(hardware multithreading)。

在支持硬件多线程的 CPU 中,需要为每个线程提供单独的寄存器堆和 PC 等资源,这样就可以在共享资源的同时保持线程的独立状态。

硬件多线程主要有两种实现方法。

  • 细粒度多线程

每条指令执行后就进行线程切换,从而实现多线程在时间上的交叉执行。这种交叉执行是轮转进行的,并且会跳过停顿的任何线程。

  • 粗粒度多线程

仅在发生高开销的停顿时(例如末级 cache 失效),才进行线程切换。对于流水线中的停顿,需要清空或者冻结流水线,因此流水线的重启开销比较大。

除此之外,硬件多线程还可以结合流水线多发技术,进一步降低成本、提高并行效率,这就是 同时多线程(Simultaneous Multithreading,SMT)。同时多线程是硬件多线程的一种变体,它结合使用多发射、动态调度流水线的处理器资源,来实现 线程级指令级 并行。

英特尔(Intel)公司的 “ 超线程 “(Hyper-Threading)技术,就是一种在单个 CPU 上实现硬件多线程的方式。

5.8.3 多核处理器和 SMP
1. 多核处理器

另一种实现线程级并行的方式,就是在一个处理器芯片上,集成多个处理单元。为了区分不同的概念,每个处理单元就称为一个 ” “(core),有多个核的处理器就被称为 多核处理器

要想充分利用多核处理器的性能,应该采用多线程的方式执行程序。这样,多个线程可以在不同的核上并行执行,大大提升运行效率。

多核处理器一般也采用硬件多线程技术,为了进行区分,有时会将单个核心上同时运行多个线程称为有多个 ”逻辑内核“,而对应的真正的核心则称为 ”物理内核“。

 

在多核处理器中,一般所有内核会有自己独立的寄存器和 L1、L2 缓存,而共享更低级的 cache(L3)和主存。

 

如果不对处理单元进行集成,也可以用同样的架构将多个处理器联合在一起,这就是所谓的 多处理器。广义上讲,多核处理器也是一种多处理器。

2. 共享内存多处理器(SMP)

编写运行在多处理器上的并行程序会更加复杂。在架构上,一个很重要的影响因素是,是否为所有处理器提供一个共享的统一物理地址空间;换句话说,所有处理器(核心)是否可以访问同一个主存储器。于是多处理器又可以划分为两种架构。

共享内存多处理器(Shared Memory Processor,SMP)为所有处理器提供统一的物理地址空间。处理器通过存储器中的 共享变量 进行通信,所有处理器都能通过加载(Load)和存储(Store)指令访问任意的主存位置。

 

尽管物理地址空间是统一的、共享的,但如果存储器本身也采取了并行架构(例如多模块存储器),不同的处理器对存储器不同部分的访问可能就会有区分。因此 SMP 又可以有两种类型。

  • 统一内存访问(Unified Memory Access,UMA)多处理器

不管是来自哪个处理器的访存指令,延迟都是一致的。这样,每个处理器对所有存储单元的访问时间都大致相同,是对称的系统架构。

  • 非统一内存访问(Non-Unified Memory Access,NUMA)多处理器

访存指令的延迟不一致,取决于哪个处理器去访问哪个存储单元。一般架构中,每个处理器(CPU)会单独直接连接一部分主存,称为 本地内存;其它主存部分则通过总线进行访问,称为 远程内存

3. MPP 和集群

如果不采用共享内存的方式,而是让每个处理器都拥有私有的地址空间,这样就可以让各个处理器更加独立、获得更好的并行性和可扩展性。这种架构被称为 大规模并行处理(Massive Parallel Processing,MPP)。

 

采用 MPP 架构的多处理器,必须通过显式的 消息传递 进行通信,因此传统上也把这种类型的计算机称为 显式消息传递计算机

更进一步,我们可以通过标准网络交换机上的 I/O 接口进行连接,用一组计算机的集合来构建出消息传递多处理器,这种形式就被称为 集群Cluster)。集群是一种分布式系统,已经成为如今并行处理计算机最典型的架构,也是大数据领域的技术基础。

5.9 章节练习

一、单项选择题
  1. 【2011真题】假定不采用 Cache 和指令预取技术,且机器处于“开中断” 状态,则在下列有关指令执行的叙述中, 错误的是 ( )

A.每个指令周期中 CPU 都至少访问内存一次

B.每个指令周期一定大于或等于一个 CPU 时钟周期

C.空操作指令的指令周期中任何寄存器的内容都不会被改变

D.当前程序在每条指令执行结束时都可能被外部中断打断

答案:C

要点:每个指令执行完成后,PC的值都会改变,从而可以继续执行下一条指令;即使是空操作也会执行 PC + 1 的动作。

  1. 【2016真题】单周期处理器中所有指令的指令周期为一个时钟周期。下列关于单周期处理器的叙述中,错误的是 ( )

A.可以采用单总线结构数据通路

B.处理器时钟频率较低

C.在指令执行过程中控制信号不变

D.每条指令的 CPI 为 1

答案:A

要点:单总线结构下,所有部件需要争用唯一的总线,不支持并发传送操作;因此单周期处理器不可以采用单总线结构数据通路。

  1. 【2019真题】下列有关处理器时钟脉冲信号的叙述中,错误的是 ( )

A.时钟脉冲信号由机器脉冲源发出的脉冲信号经整形和分频后形成

B.时钟脉冲信号的宽度称为时钟周期,时钟周期的倒数为机器主频

C.时钟周期以相邻状态单元间组合逻辑电路的最大延迟为基准确定

D.处理器总是在每来一个时钟脉冲信号时就开始执行一条新的指令

答案:D

要点:CPU 从内存中取出并执行一条指令所需的全部时间称为指令周期,指令周期又由若干机器周期组成,一个机器周期又包含若干时钟周期。

  1. 【2020真题】下列给出的处理器类型中,理想情况下,CPI 为 1 的是 ( )

    Ⅰ.单周期 CPU Ⅱ.多周期 CPU Ⅲ.基本流水线 CPU Ⅳ.超标量流水线 CPU

    A. 仅Ⅰ、Ⅱ B. 仅Ⅰ、Ⅲ C. 仅Ⅱ、Ⅳ D. 仅Ⅲ、Ⅳ

    答案:B

要点:多周期 CPU 指的是将整个 CPU 的执行过程分成几个阶段,每个阶段用一个时钟周期去完成,然后开始下一条指令的执行,而每种指令执行时所用的时钟数可以不同。基本流水线理想情况下 CPI 为 1;而超标量流水线可以通过增加功能部件实现并行,CPI 可以小于1。

  1. 【2009真题】某计算机的指令流水线由四个功能段组成,指令流经各功能段的时间(忽略各功能段之间的缓存时间)分别为 90ns、 80ns、 70ns 和 60ns,则该计算机的 CPU 时钟周期至少是 ( )

A. 90ns B. 80ns C. 70ns D. 60ns

答案:A

  1. 【2010真题】下列选项中, 不会引起指令流水线阻塞的是 ( )

A.数据旁路(转发) B.数据相关 C.条件转移 D.资源冲突

答案:A

要点:数据旁路技术是解决数据冒险的一种方法。

  1. 【2016真题】在无转发机制的五段基本流水线(取指、译码/读寄存器、运算、访存、写回寄存器)中,下列指令序列存在数据冒险的指令对是 ( )

<span style="background-color:#f8f8f8"><span style="color:#333333">I1: add R1, R2, R3; (R2) +(R3) → R1
I2: add R5, R2, R4; (R2) +(R4) → R5
I3: add R4, R5, R3; (R5) +(R3) → R4
I4: add R5, R2, R6; (R2) +(R6) → R5  </span></span>

A. I1 和 I2 B. I2 和 I3 C. I2 和 I4 D. I3 和 I4

答案:B

要点:数据冒险也叫数据相关,指在程序中存在必须等前条指令执行完才能执行后一条指令的情况。

  1. 【2017真题】下列关于超标量流水线特性的叙述中,正确的是 ( )

Ⅰ . 能缩短流水线功能段的处理时间

Ⅱ.能在一个时钟周期内同时发射多条指令

Ⅲ.能结合动态调度技术提高指令执行并行性

A.仅Ⅱ B.仅Ⅰ、Ⅲ C.仅Ⅱ、Ⅲ D.Ⅰ、Ⅱ和Ⅲ

答案:C

要点:超标量流水线每个时钟周期可以并行完成一条以上的指令,但不能缩短功能段的处理时间。

  1. 【2018真题】若某计算机最复杂指令的执行需要完成 5 个子功能,分别由功能部件 A~E 实现,各功能部件所需时间分别为 80ps、50ps、50ps、70ps 和 50ps,采用流水线方式执行指令,流水段寄存器延时为 20ps,则CPU 时钟周期至少为 ( )

A.60 ps B.70 ps C.80 ps D.100 ps

答案:D

要点:流水线上每个流水段的时间相同,应该取最大的功能部件运行时间,并且加上寄存器的延时。

  1. 【2019真题】在采用 “取指、译码/取数、执行、访存、写回” 5 段流水线的处理器中,执行如下指令序列,其中 s0、 s1、 s2、 s3 和 t2 表示寄存器编号。

<span style="background-color:#f8f8f8"><span style="color:#333333">I1: add s2,s1,s0 	// R[s2] ← R[s1] + R[s0]
I2: load s3,0(t2) 	// R[s3] ← M[R[t2]+0]
I3: add s2,s2,s3 	// R[s2] ← R[s2] + R[s3]
I4: store s2,0(t2) 	// M[R[t2]+0] ← R[s2]</span></span>

下列指令对中,不存在数据冒险的是 ( )

A.I1和 I3 B. I2 和 I3 C. I2 和 I4 D. I3 和 I4

答案:C

要点:A、B、D 选项的指令对都发生了 “写后读” 的情况,存在数据冒险。

  1. 【2021真题】下列关于数据通路的叙述中,错误的是 ( )

A.数据通路包含 ALU 等组合逻辑(操作)元件

B.数据通路包含寄存器等时序逻辑(状态)元件

C.数据通路不包含用于异常事件检测及响应的电路

D.数据通路中的数据流动路径由控制信号进行控制

答案:C

要点:指令执行过程中数据所经过的路径, 包括路径上的部件, 称为数据通路。 ALU、通用寄存器、状态寄存器、Cache、MMU、浮点运算逻辑、异常和中断处理逻辑等, 都是指令执行过程中数据流经的部件, 都属于数据通路的一部分。

  1. 【2009真题】相对于微程序控制器,硬布线控制器的特点是 ( )

A.指令执行速度慢,指令功能的修改和扩展容易

B.指令执行速度慢,指令功能的修改和扩展难

C.指令执行速度快,指令功能的修改和扩展容易

D.指令执行速度快,指令功能的修改和扩展难

答案:D

  1. 【2017真题】下列关于主存储器(MM)和控制存储器(CS)的叙述中,错误的是 ( )

A. MM 在 CPU 外, CS 在 CPU 内

B. MM 按地址访问, CS 按内容访问

C. MM 存储指令和数据, CS 存储微指令

D. MM 用 RAM 和 ROM 实现, CS 用 ROM 实现

答案:B

要点:控制存储器按照微指令的地址访问。

  1. 【2012真题】某计算机的控制器采用微程序控制方式,微指令中的操作控制字段采用字段直接编码法,共有 33 个微命令,构成 5 个互斥类,分别包含 7、 3、 12、 5 和 6 个微命令,则操作控制字段至少有 ( )

A. 5 位 B. 6 位 C. 15 位 D. 33 位

答案:C

  1. 【2014真题】某计算机采用微程序控制器,共有 32 条指令, 公共的取指令微程序包含 2 条微指令,各指令对应的微程序平均由 4 条微指令组成,采用断定法(下地址字段法)确定下条微指令地址, 则微指令中下地址字段的位数至少是 ( )

A. 5 B. 6 C. 8 D. 9

答案:C

要点:微指令下地址字段的位数,与微指令的条数有关。总共有 4 × 32 + 2 = 130 条微指令。

  1. 【2020真题】下列关于“自陷”(Trap,也称陷阱)的叙述中,错误的是 ( )

A.自陷是通过陷阱指令预先设定的一类外部中断事件

B.自陷可用于实现程序调试时的断点设置和单步跟踪

C.自陷发生后CPU将转去执行操作系统内核相应程序

D.自陷处理完成后返回到陷阱指令的下一条指令执行

答案:A

要点:自陷产生于 CPU 内部,属于内中断。

  1. 【2009真题】下列选项中,能引起外部中断的事件是 ( )

A.键盘输入 B.除数为 0 C.浮点运算下溢 D.访存缺页

答案:A

  1. 【2015真题】内部异常(内中断)可分为故障(fault)、陷阱(trap)和终止(abort)三类。下列有关内部异常的叙述中,错误的是 ( )

A.内部异常的产生与当前执行指令相关 B.内部异常的检测由CPU内部逻辑实现

C.内部异常的响应发生在指令执行过程中 D.内部异常处理后返回到发生异常的指令继续执行

答案:D

  1. 【2017真题】下列关于多重中断系统的叙述中, 错误的是 ( )

A.在一条指令执行结束时响应中断 B.中断处理期间 CPU 处于关中断状态

C.中断请求的产生与当前指令的执行无关 D. CPU通过采样中断请求信号检测中断请求

答案:B

要点:实现多重中断,中断处理期间必须开中断。

  1. 【2022真题】下列关于并行处理技术的叙述中,不正确的是 ( )

A. 多核处理器属于 MIMD 结构 B. 向量处理器属于 SIMD 结构

C. 硬件多线程技术只可用于多核处理器 D. SMP 中所有处理器共享单一物理地址空间

答案:C

二、综合应用题
  1. 【2014真题】某程序中有如下循环代码段P:

<span style="background-color:#f8f8f8"><span style="color:#333333">for(int i = 0; i < N; i++) sum+=A[i];</span></span>

假设编译时变量 sum 和 i 分别分配在寄存器 R1 和 R2 中。常量 N 在寄存器 R6 中,数组 A 的首地址在寄存器 R3 中 。 程序段 P 起始地址为 0804 8100H,对应的汇编代码和机器代码如下表所示。

 

执行上述代码的计算机 M 采用 32 位定长指令字, 其中分支指令 bne 采用如下格式:

 

OP 为操作码;Rs 和 Rd 为寄存器编号;OFFSET 为偏移量,用补码表示。 请回答下列问题,并说明理由。

1)M 的存储器编址单位是什么?

2)已知 sll 指令实现左移功能, 数组 A 中每个元素占多少位?

3)表中 bne 指令的 OFFSET 字段的值是多少?已知 bne 指令采用相对寻址方式, 当前 PC 内容为 bne 指令地址, 通过分析表中指令地址和 bne 指令内容 , 推断出 bne 指令的转移目标地址计算公式。

4)若 M 采用如下 “按序发射、按序完成” 的 5级指令流水线: IF (取值 )、ID (译码及取数)、 EXE (执行)、 MEM (访存)、 WB (写回寄存器 ),且硬件不采取任何转发措施, 分支指令的执行均引起 3个时钟周期的阻塞, 则 P 中哪些指令的执行会由于数据相关而发生流水线阻塞?哪条指令的执行会发生控制冒险?为什么指令 1 的执行不会因为与指令 5 的数据相关而发生阻塞?

答案:

1)M 的存储器编址单位为字节。因为 M 采用 32 位定长指令字,每个指令占 4 个字节;而指令间的地址差为 4,所以每个地址对应 1 个字节。

2)A 中每个元素占 32 位。左移 2 位相当于乘以 4,所以每个元素占 4 个字节。

3)bne 指令 OFFSET 字段的机器补码为 FFFAH,真值为 -6。正常情况下,OFFSET 乘以指令字长就应该是偏移的地址差:

$$
OFFSET × 4 = -6 × 4 = -24
$$

不过在这里,bne 指令地址和要跳转到的 loop 地址差为

$$
0804 8100H - 0804 8114H = -14H = (-20)_{10}
$$

所以很明显,发生跳转的时候,应该基于 bne 下一条指令的地址 0804 8118H ,再加上 OFFSET × 4,就是转移的目标地址。相当于 PC 先做了 +1 操作(对应地址 +4):

$$
(PC)+4+OFFSET×4
$$

4)由于数据相关而发生阻塞的指令为第 2、 3、 4、 6 条,因为第 2、 3、 4、 6 条指令都与各自前一条指令发生数据相关。

第 6 条指令会发生控制冒险。

 

当前循环的第 5 条指令与下次循环的第 1 条指令虽然有数据相关,但由于第 6 条指令后有 3 个时钟周期的阻塞,因而消除了该数据相关。


  1. 【2022真题】某 CPU 中部分数据通路如图所示, 其中, GPRs 为通用寄存器组;FR 为标志寄存器,用于存放 ALU 产生的标志信息;带箭头虚线表示控制信号,如控制信号 Read、Write 分别表示主存读、 主存写, MDRin表示内部总线上数据写入MDR,MDRout 表示 MDR 的内容送内部总线。

 

请回答下列问题。

1)设 ALU 的输入端 A、 B 及输出端 F 的最高位分别为 A15 、 B15 及 F15 , FR 中的符号标志和溢出标志分别为 SF 和 OF,则 SF 的逻辑表达式是什么?A 加 B、 A 减 B 时 OF 的逻辑表达式分别是什么?要求逻辑表达式的输入变量为 A15 、 B15 及 F15

2)为什么要设置暂存器 Y 和 Z?

3)若 GPRs 的输入端 rs、 rd 分别为所读、 写的通用寄存器的编号, 则 GPRs 中最多有多少个通用寄存器?rs 和 rd 来自图中的哪个寄存器?已知 GPRs 内部有一个地址译码器和一个多路选择器, rd 应连接地址译码器还是多路选择器?

4)取指令阶段(不考虑 PC 增量操作) 的控制信号序列是什么?若从发出主存读命令到主存读出数据并传送到 MDR 共需5个时钟周期, 则取指令阶段至少需要几个时钟周期?

5)图中控制信号由什么部件产生?图中哪些寄存器的输出信号会连到该部件的输入端?

答案:

1)SF 表示运算结果的正负性,所以 SF = F~15~

A 加 B 时,A + B → F。若 A 和 B 都为负且 F 为正;或者,若 A 和 B 都为正且 F 为负,则溢出。所以溢出标志逻辑表达式为:

$$
OF = A_{15} \cdot B_{15} \cdot \overline{F_{15}} + \overline{A_{15}} \cdot \overline{B_{15}} \cdot F_{15}
$$

A 减 B 时,A - B → F。若 A 为负、B 为正,同时 F 为正,则溢出;或者,若 A 为正、B 为负,同时 F 为负,则溢出。所以溢出标志逻辑表达式为:

$$
OF = A_{15} \cdot \overline{B_{15}} \cdot \overline{F_{15}} + \overline{A_{15}} \cdot {B_{15}} \cdot F_{15}
$$

2)因为在单总线结构中,每个时刻总线上只有一个数据有效,而 ALU 有两个输入端和一个输出端。因此,当 ALU 运算时,首先需要用暂存器 Y 缓存其中一个输入端的数据,在通过总线传送另一个输入端的数据。当ALU的输出端产生运算结果,但由于该节拍总线被占用,因此需要暂存器 Z,用来缓存 ALU 的输出端数据。

3)可以看到 rs 和 rd 的编号都为 4 位,那么 GPRs 中最多有 2^4^ = 16 个通用寄存器。

寄存器编号用于在指令中表示寄存器寻址的地址,指令寄存器 IR 中存储指令,所以 rs 和 rd 来自指令寄存器IR。

rd 是寄存器编号,应该连接地址译码器。多路选择器用于从多个输入信号中选择一个输出,它的输出信号取决于控制信号。

4)取指阶段微操作:

 

对应的控制信号序列如下:

① PCout, MARin 有效,将指令的地址写入 MAR

② Read 信号有效,读取主存数据(经系统总线写入MDR)

③ MDRout,IRin 有效,将 MDR 的内容写入指令寄存器 IR

其它步骤省略。

步骤 ① 需要1个时钟周期,步骤 ② 需要 5 个时钟周期,步骤 ③ 需要1个时钟周期,因此取指令阶段至少需要7 个时钟周期。

5)图中控制信号由控制单元CU产生。

指令寄存器 IR 和标志寄存器 FR 的输出信号会连接到控制部件的输入端。

第六章 总线和输入输出(I/O)系统

6.1 本章大纲要求与核心考点

6.1.1 大纲要求

(一)总线概述

  1. 总线的基本概念

  2. 总线的组成及性能指标

  3. 总线事务和定时

(二) I/O 接口( I/O 控制器)

  1. I/O 接口的功能和基本结构

  2. I/O 端口及其编址

(三)I/O 方式

  1. 程序查询方式

  2. 程序中断方式

  3. DMA 方式:DMA 控制器的组成,DMA 传送过程。

6.1.2 核心考点

本章内容相对比较简单,以概念和原理为主,一般以单项选择题的形式出现;综合应用题以 I/O 方式的考察为主,主要是程序中断方式和 DMA 方式,涉及与第五章内容的结合。总线的性能指标可能会涉及到一些计算(以总线带宽为主),I/O 设备和 I/O 接口的考察则可能会与第三章内容相结合。

需要重点掌握的内容包括:

  • 总线的基本概念、组成、主要性能指标。

  • 总线事务的定义,突发传送方式,总线定时方式,异步定时方式的 3 种类型。

  • I/O 接口的功能和结构,I/O 端口两种编址方式的原理及各自的优缺点。

  • 程序查询方式的概念、原理、特点。

  • 程序中断方式的概念、原理、特点。

  • DMA 方式的概念、原理、特点,DMA 方式的传送方式和传送过程,与中断方式的比较。

6.1.3 真题分布
考点考查次数
单项选择题综合应用题
程序中断方式153
总线概念和常见总线标准92
外部设备和 I/O 接口71
总线的性能指标61
DMA 方式33
程序查询方式11

6.2 总线

6.2.1 总线基本概念
1. 分散连接和总线连接

计算机系统的五大部件之间的互连方式有两种,一种是各部件之间使用单独的连线,称为 分散连接; 另一种则是将各部件连到一组公共信息传输线上,称为 总线连接

早期的计算机大多数用分散连接方式,以运算器为中心的结构,其内部连线十分复杂,尤其是当 I/O 与存储器交换信息时,都需经过运算器,致使运算器停止运算,严重影响了CPU的工作效率。

 

后来,虽然改进为以存储器为中心的分散连接结构,I/O 与主存交换信息可以不经过运算器,又采用了中断、DMA 等技术,使 CPU 工作效率得到很大的提高,但是仍无法解决I/O设备与主机之间连接的灵活性。

 

随着计算机应用领域的不断扩大,I/O 设备的种类和数量也越来越多,采用分散连接方式很难实现随时增添或减撤设备,而总线连接方式则可以解决这个问题。

 

总线是连接多个部件的信息传输线,是各部件共享的传输介质。

2. 总线的特点和组成

当多个部件连接到总线上时,如果两个或两个以上部件同时向总线发送信息,就会导致信号冲突,传输无效。因此,在某一时刻,只允许有一个部件向总线发送信息,而多个部件可以同时从总线上接收相同的信息。

所以,分时共享 是总线的两个特点。

总线实际上是由许多传输线或通路组成,每条线都可以通过电信号传递一位二进制代码;对于一串二进制代码,可以在一段时间内一位一位传输完成。多条传输线,则可以同时传输多位二进制代码。

 

6.2.2 总线的分类

总线可以应用在各种场景,从不同角度可以有不同的分类方法。

  • 按数据传送方式:并行传输总线串行传输总线

  • 按传输数据宽度:在并行传输总线中,又可按宽度分为 8 位、16 位、32 位、64 位等传输总线。

  • 按使用范围:计算机总线测控总线网络通信总线 等。

而更加常用的一种分类方式,则是按连接部件的不同,可以分为三类:片内总线系统总线通信总线

1. 片内总线

片内总线是指芯片内部的总线,如在 CPU 芯片内部,寄存器与寄存器之间、寄存器与运算逻辑单元 ALU 之间,都是由片内总线连接的。

2. 系统总线

系统总线是指 CPU、主存、I/O 设备各大部件之间的 信息传输线。由于这些部件通常都安放在主板或各个插件板(插卡)上,故又称 板级总线板间总线。从物理上看,就是由许多导线直接印制在电路板上,延伸到各个部件。

 

在系统总线中,按系统总线传输信息的不同,又可分为三类:数据总线地址总线控制总线

(1)数据总线

数据总线用来传输各功能部件之间的 数据信息,它是双向传输总线,其位数与机器字长、存储字长有关,一般为 8 位、16 位或 32 位。

数据总线的位数称为 数据总线宽度,它是衡量系统性能的一个重要参数。如果数据总线的宽度为 8 位,指令字长为 16 位,那么,CPU 在取指阶段必须两次访问主存。

(2)地址总线

地址总线主要用来指出数据总线上的源数据或目的数据在 主存存储单元的地址I/O 设备的地址

地址总线上的二进制码就是一个 地址,由 CPU 输出,单向传输。地址线的位数与存储单元的个数有关。

(3)控制总线

控制总线是用来发出各种 控制信号 的传输线。

由于数据总线、地址总线都是被挂在总线上的所有部件共享的,要使各部件能在不同时刻占有总线使用权,就需要依靠控制总线来完成分配和调度。

通常对任一控制线而言,它的传输是单向的。但对于控制总线总体来说,又可认为是双向的。例如,CPU 可以向主存、I/O 设备发出读/写控制信号,也可以接收外部设备发来的中断请求。

3. 通信总线

这类总线用于计算机系统之间,或计算机系统与其他系统(如控制仪表、移动通信等)之间的通信。

通信总线通常按传输方式分为两种:串行通信并行通信

  • 串行通信:是指数据在单条 1 位宽的传输线上,一位一位地按顺序分时传送。

  • 并行通信:是指数据在多条并行的 1 位宽传输线上,同时由源传送到目的地。

并行通信适宜于近距离的数据传输,通常小于30m;串行通信适宜于远距离传送,可以从几米达数千公里。通信总线的数据传输速率一般都与距离成反比。

6.2.3 总线结构

总线结构通常可以分为 单总线结构多总线结构

1. 单总线结构

单总线结构将 CPU、主存、I/O 设备(通过I/O接口)都挂在一组总线上,允许 I/O 设备之间、I/O 设备与 CPU之间或 I/O 设备与主存之间直接交换信息。

 

这种结构简单,也便于扩充,但所有的传送都通过这组共享总线,因此极易形成计算机系统的瓶颈。

它也不允许两个以上的部件在同一时刻向总线传输信息,这就必然会影响系统工作效率的提高。这类总线多数被小型计算机或微型计算机所采用。

2. 双总线结构

双总线结构的特点是,将速度较低的 I/O 设备从单总线上分离出来,形成 主存总线I/O总线 分开的结构。

 

图中通道是一个具有特殊功能的处理器,CPU 将一部分功能下放给通道,让通道来统一管理 I/O 设备;这样就可以不经过 CPU,直接完成外部设备与主存储器之间的数据传送,其系统的吞吐能力可以相当大。这种结构大多用于大、中型计算机系统。

3. 三总线结构

如果继续将速率不同的 I/O 设备进行分类,然后将它们连接到不同的通道上,就可以进一步提升计算机的效率。这样就发展出了三总线结构。

 

主存总线用于 CPU 与主存之间的传输;I/O 总线供 CPU 与各类 I/O 设备之间传递信息;DMA 总线用于高速 I/O 设备(磁盘、磁带等)与主存之间直接交换信息。在三总线结构中,任一时刻只能使用一种总线。主存总线与 DMA 总线不能同时对主存进行存取,I/O 总线只有在 CPU 执行 I/O 指令时才能用到。

如果将高速缓存 Cache 的数据交换也考虑进来,又可以构建出不同的三总线结构,甚至可以进一步发展出四总线结构。

6.2.4 总线的性能指标
1. 总线宽度

通常是指数据总线的根数,用 bit(位)表示,如 8 位、16 位、32 位、64 位(即 8 根、16 根、32 根、64 根数据总线)。

2. 总线带宽

总线带宽代表了总线的 数据传输速率,即单位时间内总线上传输数据的位数,通常用每秒传输信息的字节数来衡量,单位用 MBps(兆字节每秒)表示。

例如,总线工作频率为 33 MHz,总线宽度为 32 位,则总线带宽为

$$
33 × (32 ÷ 8) = 132 \, MBps
$$

3. 信号线数

地址总线、数据总线和控制总线三种总线数的总和。

4. 时钟同步/异步

总线上的数据与时钟同步工作的总线,称为 同步总线;与时钟不同步工作的总线称为 异步总线

5. 总线复用

一条信号线上分时传送两种信号。

例如,通常地址总线与数据总线在物理上是分开的两种总线,地址总线传输地址码,数据总线传输数据信息。为了提高总线的利用率,可以让地址总线和数据总线共用一组物理线路,在这组物理线路上分时传输地址信号和数据信号,即为总线的多路复用。

6. 总线控制方式

包括突发工作、自动配置、仲裁方式、逻辑方式、计数方式等。

其中,突发传输方式也称为 猝发传输,指对于在主存中连续存放的数据,访问时只需要给出一个首地址,耗费一个时钟周期;然后每个数据传送各占一个时钟周期,不必再给出地址就可以传输多个连续的数据。这样就可以大大提升数据传输速率。

6.2.5 总线标准

总线标准,就是系统与各模块、以及各模块之间进行连接的一个标准界面。

这个界面对两端的模块来说,都是透明的,只需要按照总线标准完成自己的接口功能即可。这就使得计算机软硬件的接口设计更加通用和方便。

常见的总线标准有:

(1)ISA

ISA 总线也称 AT 总线,由 IBM 公司推出,用于早期的 8 位 / 16 位计算机。它使用独立的总线时钟,因此 CPU 的时钟频率可以更高,有利于 CPU 性能的提高。不过 ISA 总线没有总线仲裁的硬件逻辑,因此不支持多台主设备系统;而且数据传送必须通过 CPU 管理,传输效率不够高。

ISA 总线时钟频率为 8 MHz,最大传输率为 16 MBps,数据线 16 位,地址线 24 位。

(2)EISA

EISA 总线是一种在 ISA 基础上扩展的总线标准,与 ISA 完全兼容。EISA 总线从 CPU 中分离出了总线控制权,有效提升了传输效率;并且支持多个总线主控器和突发传输方式。

EISA 总线时钟频率为 8 MHz,最大传输率为 33 MBps,数据线 32 位,地址线 32 位。

(3)VESA(VL-BUS)

VESA 总线是由视频电子标准协会(Video Electronic Standard Association,VESA)提出的局部总线标准,由 CPU 总线演化而来。所谓 局部总线,是指不连接 CPU、而是为其它重要模块(比如显卡)提供直接相连的高速数据传输通道的总线。

VESA 总线时钟频率达 33 MHz,最大传输率达 133 MBps,数据线 32 位,并且可通过扩展槽扩展至 64 位。

(4)PCI

PCI(Peripheral Component Interconnect,外围部件互连)是 Intel 公司 90 年代提出的高性能局部总线标准,支持突发传输方式,兼容性好、支持即插即用。

PCI 总线提供了一个高速数据传输通道,自身采用 33 MHz 和 66 MHz 总线时钟,与 CPU 时钟频率无关,数据线为 32 位,且可扩展至 64 位。PCI 总线的数据传输速率为 132 MBps(33 MHz 时钟,32 位数据通路),可以升级至 528 MBps(66 MHz 时钟,64 位数据通路)。

之后 Intel 又推出了 PCI - Express(PCIe)总线,采用串行传输方式进一步提升了传输速率,最新版本的 PCIe 总线数据传输速率可达 8 GBps,已经成为如今应用最为广泛的总线标准。

(5)AGP

AGP(Accelerated Graphics Port,加速图形端口)是 Intel 公司推出的显卡专用局部总线标准,基于 PCI 2.1 版规范扩充修改而成。

AGP 总线采用点对点通道方式,以 66.7 MHz 的频率直接与主存相连,以主存作为帧缓冲器实现高速存取。AGP 的数据线为 32 位,最大数据传输速率为 266 MBps。此外 AGP 还采用了一种新技术,能在一个时钟信号的上下沿双向传输数据,这称为 ”双激励“ 技术,这样 AGP 实际的传输频率就达到了 133 MHz,最大传输速率增至 533 MBps。后来推出的 AGP 新版本,数据传输速率又有进一步的提升,可达 2.1 GBps。

不过随着 PCIe 的出现,AGP 总线的应用已经越来越少了。

(6)RS-232C

RS-232C 是由美国电子工业协会 EIA 推荐的一种串行通信总线标准,它是应用于串行二进制数据交换的数据终端设备(DTE)和数据通信设备(DCE)之间的标准接口。

RS-232C 命名中,”RS“ 指 ”Recommended Standard“(推荐标准),232 为标识号,C 表示修改次数。RS-232-C 总线标准设有25条信号线,包括一个主通道和一个辅助通道,一般用于 20m 以内的通信。

(7)USB

USB(Universal Serial Bus,通用串行总线)是 Compaq、Intel、IBM、Microsoft、DEC、NEC 和 Northern Telecom 七大公司联合推出的计算机串行接口总线标准,1996 年发布了 USB 1.0 版本。

 

USB 基于通用连接技术,做到了真正的即插即用,具有很强的连接和扩展能力,标准统一而且可以为外设提供 +5V 电源,给用户使用提供了极大方便。标准 USB 低速传输距离为 5m,通过 HUB 或中继器可达 30m。USB 1.0 的数据传输速率,采用普通无屏蔽双绞线时为 1.5 Mbps,采用带屏蔽双绞线可达 12 Mbps;USB 2.0 数据传输率可达 480 Mbps(60 MBps),USB 3.0 更是可以高达 5.0 Gbps(500 MBps)。

6.2.5 总线判优(总线仲裁)

总线上连接的各种设备,一些是对总线有控制权的,称为 主设备(主模块);另一类则对总线没有控制权,只能响应从主设备发来的命令,称为 从设备(从模块)

总线上信息的传递都是由主设备发起的;当某个主设备需要占用总线向另一个设备发送信息时,首先需要发出 总线请求信号。如果多个主设备同时发出请求、希望占用总线,就需要一个专门的 总线控制器 来做出判断, 按照一定的优先级确定哪个主设备可以使用总线。这个过程就称为 总线判优,或者 总线仲裁

总线判优的实现可以分为两种形式:

  • 集中式:将控制逻辑集中在一处(比如 CPU 中);

  • 分布式:将控制逻辑分散在与总线连接的各个部件或者设备上。

以集中式为例,常见的总线判优方式有以下三种。

1. 链式查询

顾名思义,链式查询的硬件连接方式就是将设备接口电路排成一条链,依次进行查询。原理跟中断判优的 ”链式排队器“ 类似。

 

上图中,控制总线中有 3 根线专门用于总线的控制:

  • BS(Bus State,总线状态):为 1 时表示 ”总线忙“,即总线已被占用;为 0 时表示 ”总线空闲“;

  • BR(Bus Request,总线请求):传递各设备接口电路发来的总线请求信号;

  • BG(Bus Grant,总线同意):由总线控制器经过总线判优之后、发出的允许某个设备使用总线的信号。

BG 信号是按照链式排列顺序,串行地依次向下传递的。

I/O 设备 0 的优先级最高,因此 BG 先到达 I/O 接口 0。如果这时接口 0 有总线请求,那么 BG 就不再向下传递,并且发出 ”总线忙“ BS 信号,表示设备 0 获得了总线使用权。如果接口 0 没有总线请求,则 BG 信号继续传递至 I/O 接口 1,依次查询判断。

可见,链式查询中,设备距离总线控制器 越近,优先级就 越高

这种方式实现简单,很容易实现扩展;但对电路故障很敏感,而且优先级低的设备可能很难获取总线使用权。

2. 计数器定时查询

计数器定时查询的基本思路是,为每个设备定一个 设备地址,总线控制器中设置一个 计数器,按照设备地址号依次计数;当计数器的值是某个设备的地址时,该设备的总线请求就可以被响应、从而获取总线的使用权。

 

跟链式查询相比,计数器定时查询增加了一组 设备地址线,省去了总线同意信号线 BG。

当总线控制部件接收到 BR 上的总线请求时,如果当前总线没有被占用(BS = 0),那么就让计数器开始计数;并通过设备地址线,发出一组地址信号。如果地址线选中的设备,发出了总线请求信号,那么就可以获得总线使用权,此时计数器停止计数。

  • 如果我们设定计数器每次都从 ”0“ 开始计数,那么设备的优先级就是按照地址 0,1,2,... 依次排列;

  • 如果我们每次停止计数后不清零 、而是继续循环计数,那就相当于所有设备可以轮流占用总线、优先级相同;

  • 如果我们通过程序来设置计数器的初始值,还可以灵活改变优先级;

这种方式对电路故障没有那么敏感,不过增加了控制线的数量(设备地址线一般有多条),控制也比较复杂。

3. 独立请求方式

这种方式更加直接,每个设备独立发出总线请求;总线控制器内设置排队电路,根据优先级单独向每个设备发送总线同意信号。

 

上图中,每个设备都有一对总线请求线 BR~i~ 和总线同意线 BG~i~ 。当某个设备要使用总线时,就通过自己的请求线发出请求信号;总线控制器通过内部的排队电路进行判优,决定响应哪一个设备的请求,发出对应的同意信号。

这种方式响应速度快,优先次序也可以通过程序设置灵活改变,但控制线数量更多、总线控制更加复杂。

对于 n 个设备的系统,要确定总线使用权属于哪个设备,链式查询只用了 2 条线(BR、BG);计数器定时查询则需要进行设备编址,大致需要 log~2~ n 条设备地址线和 1 条 BR 线;而独立请求方式则需要 2n 条线。

6.2.6 总线事务和总线周期

从一个设备发出总线请求、获取到总线使用权,到使用完成、将总线释放,这个完整的过程就是一次 总线操作,一般称为 总线事务。完成一次总线事务的时间,就称为 总线周期

总线事务中包括一些具体操作,对应着总线周期中的不同阶段:

  • 申请分配阶段:主设备发出 总线请求,经过总线控制器中总线仲裁机构的 判优,决定将总线使用权交给某个设备(申请者);

  • 寻址阶段:取得使用权的主设备,通过总线发出要访问的从设备地址和有关命令,启动从设备;

  • 传输阶段:主设备和从设备进行数据交换,数据从源模块发出,经数据总线流入目的模块;

  • 结束阶段:系统总线上撤除主设备的有关信息,让出总线的使用权。

6.2.7 总线通信控制(总线定时)

在总线周期中,核心阶段是数据的传输。由于涉及到两个不同模块,因此双方在交换数据时什么时候传输开始、什么时候传输结束,以及如何进行协调配合,应该有一个统一的规则。这被称为 总线通信控制;因为主要涉及的是时间上的控制协调,所以也叫 总线定时

总线通信控制通常有四种方式:同步通信异步通信半同步通信分离式通信

1. 同步通信

通信双方由统一时钟信号来控制数据传送,这种方式称为 同步通信

时钟信号通常由 CPU 的总线控制部件发出,送到总线上的所有部件;也可以由每个部件各自的时序发生器发出,但必须由总线控制部件发出的时钟信号对它们进行同步。

  • 优点:规定明确、统一,模块间的配合简单一致;

  • 缺点:主、从模块时间配合属于强制性“同步”,必须在限定时间内完成规定的要求;并且对所有从模块都用同一限时,这就势必造成,对各不相同速度的部件而言,必须按最慢速度的部件来设计公共时钟,严重影响总线的工作效率,也给设计带来了局限性,缺乏灵活性。

同步通信一般用于总线长度较短、各部件存取时间比较一致的场合。

2. 异步通信

异步通信则没有公共的时钟标准,不要求所有部件严格的统一操作时间,而是采用 应答方式(又称 握手方式),当主模块发出 请求(Request)信号时,一直等待从模块反馈回来 “响应”(Acknowledge)信号后,才开始通信。

异步通信克服了同步通信的缺点,允许各模块速度的不一致性,给设计者充分的灵活性和选择余地。但要求主、从模块之间增加两条应答线(握手交互信号线),而且控制逻辑更加复杂,数据传输率也不及同步传输。

异步通信的应答方式,又可分为 不互锁半互锁全互锁 三种类型。

 

(1)不互锁方式

主设备发出请求信号后,不必等待接到从设备的响应信号,而是经过一段时间便撤销其请求信号;从设备接到请求信号后,发出响应信号,并且经过一段时间自动撤销响应信号。

可见通信双方完全独立,没有互锁关系。

(2)半互锁方式

主设备发出请求信号,必须等接到从设备的响应信号后,再撤销其请求信号,因此有互锁关系;

而从设备在接到请求信号后,发出响应信号,但不必等待主设备撤销请求信号,而是隔一段时间后自动撤销其响应信号:这两者之间没有互锁关系。

因此这种方式称为 半互锁方式

(3)全互锁方式

主设备发出请求信号,必须等从设备回答后,再撤销其请求信号;

同样,从设备发出回答信号,必须等主设备撤销请求信号后,再撤销其回答信号。

双方存在互锁关系,所以称为 全互锁方式

3. 半同步通信

半同步通信是同步通信和异步通信的结合。

半同步通信既保留了同步通信的基本特点,如所有的地址、命令、数据信号的发出时间,都严格参照系统时钟的某个前沿开始,而接收方都采用系统时钟后沿时刻来进行判断识别;同时又像异步通信那样,允许不同速度的模块和谐地工作,为此需要增设一条 “等待”(WAIT)响应信号线。

4. 分离式通信

分离式通信的基本思想是,将一个传输周期(或者总线周期)分解为两个子周期。

在第一个子周期,主设备 A 获取总线使用权后,将命令、地址及其它有关信息都发到系统总线上,经总线传输,由从设备 B 接收下来。这些信息的传输只占很短的时间,发送完后 A 立即放弃总线使用权,交给其它模块使用。

在第二个子周期,设备 B 接收到了 A 发来的所有信息,经过一系列内部操作,将 A 需要的数据准备好;此时便由 B 重新申请总线使用权,获准之后再进行数据传输。

这样,节省了等待 B 准备数据的时间,两个子周期中总线上都只有单向的信息流,两个设备都成为了主设备。

6.3 I/O 系统概述

在计算机中,除 CPU 和主存两大模块之外,第三个重要部分就是 输入输出模块,也叫做 输入输出系统,简写为 I/O 系统

I/O 系统的主要功能,就是对不同类型数据信息的输入输出,进行高效的控制管理。

6.3.1 I/O 系统的发展

I/O 系统的结构,从简单到复杂,发展主要经历了四个阶段。

1. 早期阶段

早期的输入输出设备比较少,计算机的结构是以 CPU 为中心的分散连接方式;所有 I/O 设备都直接连接到 CPU ,与主存交换数据必须通过 CPU。

 

这样,每个 I/O 设备都必须配备一套独立的逻辑电路与 CPU 连接,线路复杂、难以维护和扩展;而且输入输出数据会打断 CPU 运行,效率比较低。

2. 接口模块和 DMA 阶段

之后计算机的连接方式发展出了总线结构,I/O 设备通过接口模块与总线相连,通过总线进行数据传输。

 

通常,在接口模块中都会设有 数据通路控制通路。数据可以通过接口起到缓冲作用,也可以完成串并转换;控制通路则可以传送 CPU 发来的控制命令,或者向 CPU 发送来自 I/O 设备的反馈信号。许多接口还可以满足中断请求处理的要求,使 I/O 设备和 CPU 可以并行工作。

为了进一步提高 CPU 的效率,又出现了 直接存储器存取(Direct Memory Access,DMA)技术。在 I/O 设备和主存之间增加一条专门的数据通路,让它们可以直接交换信息,而无需经过 CPU。

 

在现代的小型或微型计算机中,一般都会采用接口模块和 DMA 的接口来实现 I/O 设备和主存的数据交换。

3. 通道结构阶段

对于大中型计算机,I/O 设备数量庞大、数据传输非常频繁,采用 DMA 方式需要为每个设备都配置专用的 DMA 接口,CPU 也需要对众多的 DMA 接口进行管理;控制非常复杂、工作效率也会受到很大影响。

因此在大中型计算机中,采用 I/O 通道 的方式来进行数据交换。

 

通道 可以看作一种从属于 CPU 的专用处理器,专门负责管理 I/O 设备,实现主存和 I/O 设备之间的数据交换。采用通道结构的计算机,I/O 设备的数据传输就全部交给通道来处理,提高了 CPU 的资源利用率。

4. 处理机阶段

I/O 系统进一步发展,出现了 I/O 处理机。I/O 处理机基本独立于主机工作,又称为 外围处理机。 具有 I/O 处理机的 I/O 系统与 CPU 的并行性更高,进一步解放了 CPU ,提升了 CPU 的工作效率。

6.3.2 I/O 系统的组成

I/O 系统主要由两部分组成: I/O 软件I/O 硬件

1. I/O 软件

I/O 系统软件的主要功能包括:

  • 将用户编写的程序输入主机;

  • 将运算结果传送给用户;

  • 协调 I/O 系统和主机的工作。

不同结构的 I/O 系统采用的软件技术不同。

生活中常见的计算机一般采用接口模块方式,指令系统中需要设置专门的 I/O 指令;调用 I/O 指令并配合系统软件中的管理程序,就可以实现 I/O 设备与主机的协调工作。

如果采用通道管理方式,除 I/O 指令外,还需要有 通道指令 和相应的操作系统。

(1)I/O 指令

I/O 指令也是一种机器指令,它的指令格式与普通的机器指令类似。

 

上面的 I/O 指令格式中,分为三个字段:

  • 操作码:指明当前是一条 I/O 指令;

  • 命令码:指明 I/O 设备的具体操作;

  • 设备码:用来对多台 I/O 设备进行区分和选择,相当于设备地址。

其中,命令码一般会包括以下几种情况:

  • 将数据从 I/O 设备输入主机;

  • 将数据从主机输出至 I/O 设备;

  • 状态测试。检测 I/O 设备的状态(”忙“ 还是 ”准备就绪“),来确定接下来是否可以进行数据交换;

  • 形成具体的操作命令。例如磁盘驱动器的磁头读扇区、写扇区、寻找磁道等等。

(2)通道指令

通道指令是具有通道的 I/O 系统专门设置的指令,又称为 通道控制字(Channel Control Word,CCW),是通道用来执行 I/O 操作的指令。

这类指令位数一般会比较多,主要功能有:

  • 指明要交换的一组数据在主存中的首地址;

  • 指明要传送数据的字节数,或者在主存中的末地址;

  • 指明所选设备的设备码,以及要完成的具体操作的命令码。

对于采用了通道结构的计算机,CPU 执行的 I/O 指令就只负责启、停 I/O 设备,查询通道和设备状态以及对通道进行操作控制;而具体的数据传输则交给通道来完成。

2. I/O 硬件

I/O 系统的硬件组成是多种多样的,不同结构的 I/O 系统硬件组成也会有所不同。

  • 采用接口模块结构的 I/O 系统,硬件主要包括 接口模块I/O 设备 两大部分。I/O 设备通过接口连接到主机;

  • 采用通道或处理机结构的 I/O 系统,硬件主要包括 通道/处理机设备控制器I/O 设备;一个通道/处理机可以和多个设备控制器相连,一个设备控制器又可以控制多台同类型的设备。

    此外,连接各模块的数据通路(I/O 总线)也是 I/O 硬件的一部分。

6.3.3 I/O 设备

计算机硬件系统中,主要由三部分组成:CPU 、主存储器和输入输出设备。输入输出设备和它们对应的接口模块,就构成了输入输出系统。

CPU 与主存合称 主机,主机之外的大部分硬件设备都可以称为 I/O 设备,统称为 外部设备,简称 外设

 

1. I/O 设备分类

从应用场景和功能上看,I/O 设备可以分为三大类:

(1)人机交互设备

实现用户(操作者)和计算机之间信息交流的设备,又可以分为两类:

  • 输入设备:将人们熟悉的信息形式转换为机器能识别的信息形式,如键盘、鼠标、摄像头等。

  • 输出设备:将机器运算结果转换为人们熟悉的信息形式,如打印机、显示器等。

(2)信息存储设备

系统软件和各种有用信息,都需要进行存储保留,这就需要能够持久化保存的外部存储设备。这类存储设备一般可以作为计算机的辅助存储器,比如磁盘、光盘、固态硬盘等。

(3)机-机通信设备

多台计算机之间、或者是计算机和其它系统之间如果需要进行通信,必须借助专门的通信设备。比如调制解调器(Modem),A/D、D/A 转换设备等。

2. I/O 设备的组成

I/O 设备的基本组成如下:

 

  • 设备控制器用来控制 I/O 设备的具体动作,不同的 I/O 设备需要完成的控制功能也不同;

  • 机、电、磁、光部件是 I/O 设备的工作部件,与具体功能有关;内部结构涉及到机、电、磁、光工作原理;

  • 现代 I/O 设备一般还会通过 I/O 接口与主机相连。

6.4 I/O 接口(I/O 控制器)

两个系统或两个部件之间的交接部分,一般就称为 接口。接口可以是硬件上两种设备间的连接电路;也可以是两个软件之间交互的逻辑边界。

主机与 I/O 设备之间,专门设置一套硬件电路、配合相应的软件控制,实现两者间的信息交互,这就是 I/O 接口。不同的 I/O 设备都有其相应的设备控制器,而它们往往都是通过 I/O 接口与主机取得联系的。

6.4.1 I/O 接口的功能

I/O 接口主要的功能如下:

  • 选址功能:根据 I/O 指令中的设备码进行设备选择;

  • 数据缓冲:I/O 设备速度不一,与 CPU 相差比较大,需要进行数据的缓冲达到速度匹配、防止丢失数据;

  • 串并转换:有些 I/O 设备采用串行传输方式,而 CPU 一般为并行传输,需要进行数据格式转换;

  • 电平转换:I/O 设备的输入输出电平可能与 CPU 不同,需要进行电平转换;

  • 传送控制命令:CPU 会向 I/O 设备发出各种控制信号,需要相应的传输通路;

  • 反馈状态信息:I/O 设备需要将工作状态(比如 “忙” “准备就绪” “错误” “中断请求” 等)报告给 CPU,并且可能需要将状态信息进行保存,供 CPU 查询。

6.4.2 I/O 接口的基本结构

总线结构的计算机中,每一台 I/O 设备都是通过 I/O 接口挂到 I/O 总线上的。

1. 总线连接的数据通路

由于需要实现设备选择、数据缓冲、传送命令和状态等功能,总线中必须有相应的数据通路:

 

上图中的 I/O 总线,就包含了 数据线设备选择线命令线状态线

  • 数据线

数据线是 I/O 设备与主机之间传送数据的线路。

数据线的根数一般等于存储字长的位数,通常是 双向的。若采用单向数据总线,则必须用两组才能实现数据的输入和输出功能,而双向数据总线只需一组即可。

  • 设备选择线

设备选择线用来传送设备码,如果把设备码看做是地址号,那么设备选择线又可称为 地址线

设备选择线的根数取决于 I/O 指令中设备码的位数,决定了能够连接设备的数量。设备选择线一般是一组,也可以有两组,其中一组用于主机向 I/O 设备发送设备码,另一组用于 I/O 设备向主机回送设备码。

  • 命令线

命令线主要用来传输 CPU 向设备发出的各种命令信号,比如启动、清除、屏蔽、读、写等。

命令线是一组单向总线,根数与命令信号的数量有关。

  • 状态线

状态线主要用来向主机报告 I/O 设备的状态信号,比如设备是否准备就绪、是否向 CPU 发出中断请求等。

状态线也是一组单向总线。

2. I/O 接口的基本组成

根据 I/O 接口的功能,以及总线结构中整体的数据通路,就可以推出接口应该具有的硬件配置。

(1)选址功能

设备码通过设备选择线(地址线)送至所有设备的接口,因此需要每个接口都必须具有选址功能。

当设备选择线上的设备码与本设备码相符时,发出一个 设备选中信号 SEL,这种功能可通过接口内的设备选择电路来实现。

(2)传送命令的功能

当 CPU 向 I/O 设备发出命令时,要求 I/O 设备能做出响应,因此通常在 I/O 接口中设有存放命令的 命令寄存器 以及 命令译码器

命令寄存器用来存放 I/O 指令中的命令码,它受设备选中信号 SEL 控制。命令线和所有接口电路的命令寄存器相连,只有被选中设备的 SEL 信号有效,才会将命令线上的命令码存入命令寄存器。

 

(3)传送数据的功能

接口处于主机与I/O设备之间,因此主机与 I/O 设备之间进行数据传输必须经过接口。这就要求接口中具有数据通路,完成数据传送。这种数据通路还应具有 缓冲 能力,也就是能将数据暂存在接口内。

接口中通常设有 数据缓冲寄存器(Data Buffer Register,DBR),它用来暂存 I/O 设备与主机准备交换的信息,与 I/O 总线中的数据线是相连的。

(4)反映 I/O 设备工作状态的功能

为了使 CPU 能及时了解 I/O 设备的工作状态,接口内必须设置一些反映设备工作状态的触发器。

比如,可以用 完成触发器 D工作触发器 B 来标志设备所处的状态。

  • 当 D = 1,B = 0 时,表示 I/O 设备已经准备就绪;

  • 当 D = 0,B = 1 时,表示 I/O 设备正处于工作状态;

  • 当 D = 0,B = 0 时,表示 I/O 设备处于暂停状态。

由于现代计算机系统中大多采用中断技术,因此接口电路中一般还设有 中断请求触发器 INTR,当为 “1” 时,表示该设备向 CPU 发出中断请求;

接口内还有 中断屏蔽触发器 MASK,它与中断请求触发器配合使用,完成设备的屏蔽功能。

所有的状态标志触发器都与I/O总线中的状态线相连。此外,不同的 I/O 设备的接口电路中还可根据需要增设一些其他状态标志触发器。

这样就可以得到 I/O 接口的基本组成:

 

目前大多数 I/O 设备所共用的电路都制作在一个芯片内,作为 通用接口芯片。另一些 I/O 设备专用的电路,制作在 I/O 设备的 设备控制器 中。

6.4.3 I/O 端口及其编址

需要注意区分 “接口” (Interface)和 “端口” (Port)的概念:

端口 指的是接口电路中的一些寄存器,这些寄存器用来存放数据信息、控制信息和状态信息,相应的端口就称为 数据端口控制端口状态端口

CPU 执行 I/O 指令中的输入操作时,从端口读入信息;执行输出操作时,将寄存器(比如 ACC)的信息写入到端口中。这样,CPU 对 I/O 设备的操作,就可以转换为对 I/O 端口的操作。一些端口,再加上对应的控制逻辑,就组成了接口。

CPU 要想访问某个 I/O 端口,就需要对 I/O 端口进行统一编号,每个端口对应一个 端口地址。对 I/O 端口的编址,可以选择跟内存统一,也可以完全独立。

1. 统一编址

统一编址就是把 I/O 地址看作存储器地址的一部分,也就是把 I/O 端口当做存储器单元统一进行地址分配。也称为 存储器映射方式

这样,CPU 访问 I/O 端口时就可以直接用访存指令,而不需要专门的 I/O 指令,CPU 控制 I/O 设备更加方便;而缺点是端口会占用内存地址空间,减少了主存容量。

2. 不统一编址

不统一编址就是 I/O 地址和存储器地址完全分开,两者都有自己独立的地址空间,所以又叫 独立编址。也称为 I/O 映射方式

不统一编址时,CPU 访问一个地址时,就无法从地址码上进行区分;所以必须通过设置专门的 I/O 指令 来访问 I/O 端口。这样就不会占用主存地址空间,用专门的 I/O 指令编写程序也会更加清晰;缺点是让指令系统更加复杂,CPU 需要提供对内存和 I/O 设备的两套读/写控制信号,硬件成本也更高。

6.4.4 I/O 接口的类型

I/O 接口按不同的方式,可以有以下几种分类。

  • 按数据传送方式:分为 并行接口串行接口

  • 按功能选择的灵活性:分为 可编程接口不可编程接口

  • 按通用性:分为 通用接口专用接口

  • 按数据传送的控制方式:分为 程序型接口DMA 型接口

程序型接口用于连接速度较慢的 I/O 设备,如显示终端、键盘、打印机等;具体的控制方式包括 程序查询方式程序中断方式。DMA 型接口用于连接高速 I/O 设备,如磁盘、磁带等;控制方式采用 DMA 方式

6.5 I/O 方式

I/O 设备与主机交换信息时,对信息传送的控制方式一共有五种:程序查询方式程序中断方式直接存储器存取方式(DMA)、I/O 通道方式、I/O 处理机方式。

其中,前两种方式都是通过 CPU 中程序指令的执行来控制的。

6.5.1 程序查询方式

程序查询方式是由 CPU 通过程序不断查询 I/O 设备是否已做好准备,从而控制 I/O 设备与主机交换信息。

1. 程序查询基本流程

采用这种方式实现主机和 I/O 设备交换信息,要求 I/O 接口内设置一个 状态标记,用来反映 I/O 设备是否准备就绪。CPU 通过检测这个标记,就可以了解 I/O 设备的准备情况。

 

当 I/O 设备较多时,CPU 就需要按 I/O 设备在系统中的 优先级 进行逐级查询。

 

为了完成这个查询的流程,CPU 通常需要执行以下 3 条指令:

  • 测试指令:用来查询 I/O 设备是否准备就绪;

  • 传送指令:当 I/O 设备准备就绪时,执行数据的传送指令;

  • 转移指令:如果 I/O 设备未准备就绪,应执行转移指令;转至测试指令,继续测试 I/O 设备的状态。

当执行一段程序,需要启动某个 I/O 设备进行数据交互时,就把查询流程插入到运行的程序中。具体的查询流程如下:

 

① 这种方式传送数据时要占用 CPU 中的寄存器,所以首先要将寄存器原内容保护起来;

② 传送的往往是一批数据,所以需要设置 I/O 设备与主机交换数据的计数值,用来控制数据量;

③ 设置要传送的数据在主存缓冲区的首地址;

④ CPU 启动 I/O 设备;

⑤ 将 I/O 接口中的设备状态标志取至 CPU 并测试 I/O 设备是否准备就绪。如果未准备就绪,则等待,直到准备就绪为止;当准备就绪时,接着可实现传送。

对输入而言,准备就绪意味着接口电路中的数据缓冲寄存器已装满欲传送的数据,称为 输入缓冲满,CPU 可以取走数据;对输出而言,准备就绪意味着接口电路中的数据已被设备取走,称为 输出缓冲空,这样 CPU 可以再次将数据送到接口,设备则可以再次从接口接收数据。

⑥ CPU 执行 I/O 指令,从 I/O 接口的数据缓冲寄存器中读出一个数据(输入),或者把一个数据写入 I/O 接口中的数据缓冲寄存器内(输出),同时将接口中的状态标志复位;

⑦ 修改主存地址;

⑧ 修改计数值,若原设置计数值为原码,则依次减 1;若原设置计数值为负数的补码,则依次加 1;

⑨ 判断计数值。若计数值不为 0,表示一批数据尚未传送完,重新启动外设继续传送;若计数值为 0,则表示一批数据已传送完毕;

⑩ 结束 I/O 传送,继续执行原程序。


只要一启动 I/O 设备,CPU 就不断地查询 I/O 设备的准备情况,这就会暂停原程序的执行。当 I/O 设备准备就绪后,就将数据逐个传送;直到数据全部传送结束,CPU 才重新回到原程序继续执行。

所以在程序查询方式下,CPU 和 I/O 设备是串行工作的,效率不高。

2. 接口电路

程序查询方式对应的接口电路基本组成如下:

 

图中的数据缓冲寄存器用来存放要传送的数据;D 是完成触发器,B 是工作触发器。

3. 接口工作过程

以输入设备为例,数据应该从 I/O 设备传送至主机。I/O 接口的具体工作过程为:

① CPU 执行 I/O 指令启动输入设备;指令中的设备码字段,通过地址线(设备选择线)送至设备选择电路;

② 如果接口的设备码与地址线上信号相同,设备被选中,输出 SEL 有效;

③ I/O 指令中的启动命令输入,通过与非门将完成触发器 D 置 0(复位),将工作触发器 B 置1(置 位);

④ 工作触发器 B 输出启动信号,启动设备开始工作;

⑤ 输入设备将数据送至数据缓冲寄存器;

⑥ 输入缓冲满,由设备发出工作结束信号,将完成触发器 D 置 1,工作触发器 B 置 0;

⑦ 完成触发器 D 输出 1,通知 CPU “准备就绪”;

⑧ CPU 执行输入指令,将数据缓冲寄存器中的数据取出,送至通用寄存器,再存入主存。

6.5.2 程序中断方式

程序查询方式效率较低,主要原因就是 I/O 设备工作速度较慢,CPU 启动设备后,需要等待一段时间 I/O 设备才能准备就绪、开始信息交换;而 CPU 则耗费了大量时间进行状态查询。

如果 CPU 在启动 I/O 设备后,继续执行自身的原程序;等到 I/O 设备准备就绪后,主动向 CPU 发出请求再予以响应,就可以大大提升工作效率。这个请求就可以以 I/O 中断 的形式出现。

1. 程序中断基本流程

 

上图中,CPU 在执行 I/O 指令启动 I/O 设备之后,继续执行原程序;在执行第 M 条指令时,I/O 设备准备就绪,发来了中断请求,于是 CPU 在第 M 条指令执行结束后进入中断周期,转而执行中断服务程序;等到中断服务程序执行完毕,再返回到程序断点处,继续执行第 M + 1 条指令。

这种利用 I/O 中断,暂时中断 CPU 现行程序、转入 I/O 服务程序的方式,就称为 程序中断方式

采用程序中断方式,CPU 就可以不必等待 I/O 设备的准备过程,工作效率得到了明显的提升。不过 CPU 和 I/O 接口需要增加响应的硬件电路,还要编制对应的中断服务程序。

2. 接口电路

很明显,采用程序中断方式,必须在接口电路中增加中断处理相应的硬件,比如中断请求触发器 INTR、中断屏蔽触发器 MASK、排队器、中断向量地址形成部件等。

 

上图中,完成触发器 D 和 屏蔽触发器 MASK 的输出通过与非门连接,产生中断请求信号;并通过排队器判优,交给设备编码器(中断向量地址形成部件)产生向量地址。

3. I/O 中断处理过程

同样,CPU 响应 I/O 设备提出中断请求的条件,是允许中断触发器 EINT = 1;CPU 响应 I/O 中断请求的时间,是在某条指令执行阶段的结束时刻。

以输入设备为例,I/O 中断处理的过程为:

① CPU 发出 I/O 设备启动命令,将完成触发器 D 置 0,将完成触发器 B 置 1;

② 工作触发器 B 启动设备,开始工作;

③ 输入设备将数据送至数据缓冲寄存器;

④ 输入缓冲满,设备发出工作结束信号,将完成触发器 D 置 1,完成触发器 B 置 0;

⑤ 指令执行阶段的结束时刻,CPU 发出中断查询信号;

⑥ 当设备准备就绪(D = 1),且未被屏蔽(MASK = 0)时,中断请求触发器 INTR 被置 1,向 CPU 发出 中断请求;同时 INTR 信号送至排队器,进行 中断判优

⑦ 当 EINT = 1(允许中断),而设备又被排队器选中时,进入 中断响应 阶段;CPU 发来的中断响应信号 INTA 将排队器的输出送至编码器,形成向量地址;

⑧ 向量地址送至 PC,作为下一条指令的地址;随后跳转至中断服务程序入口地址,进入 中断服务 阶段;

⑨ 执行中断服务程序,通过输入指令将数据缓冲寄存器的数据取出,送至通用寄存器,再存入主存。

⑩ 中断服务程序最后一条指令是 中断返回 指令,执行结束后返回原程序的断点处。

这里也可以看出,一次中断处理的过程可以简单分为 中断请求中断判优中断响应中断服务中断返回 5 个阶段。

6.5.3 DMA 方式
1. DMA 的概念和特点

如果 I/O 设备能直接与主存交换信息而不占用 CPU,那么 CPU 的资源利用率就可以进一步提高,这种方式就被称为 直接存储器存取(Direct Memory Access,DMA)。

DMA 方式的特点是,I/O 设备与主存之间有一条直接数据通路。因此,I/O 设备可以与主存直接交换信息,而不需要调用中断服务程序。这样 ,CPU 就不必暂停现行程序、专门去为设备服务,省去了保护现场和恢复线程的过程;所以工作效率比程序中断方式更高。

 

对于 高速 I/O 设备 或者 辅存,如果采用程序中断方式,那么每次与主机进行数据交互时都要等待 CPU 做出中断响应,很可能会造成数据丢失;因此更适合采用 DMA 方式

2. DMA 与 CPU 的访存冲突

在 DMA 方式中,由于 DMA 接口与 CPU 都可以访问主存,这就有可能出现两者争用主存的冲突。

I/O 设备请求进行 DMA 传送时,会遇到三种情况:

  • CPU 此时不需要访问主存:这时 I/O 设备与 CPU 不发生冲突;

  • CPU 正在访问主存:这时必须等存取周期结束,CPU 才会将总线占有权让出;

  • CPU 也同时要求访问主存:这就出现了访存冲突。

发生访存冲突时,I/O 访存要优先于 CPU 的访存,因为 I/O 设备不立即访问主存的话就可能丢失数据。所以一般这时需要 CPU 进行 “让步”。

为了有效地分时使用主存,通常采用以下三种方法来解决访存冲突:

(1)停止 CPU 访问主存

这种方法就是 CPU 彻底停止访存、等 I/O 访存结束之后再继续进行。

当外设要求传送一批数据时,由 DMA 接口向 CPU 发一个停止信号,要求 CPU 放弃地址线、数据线和有关控制线的使用权。DMA 接口获得总线控制权后,开始进行数据传送;数据传送结束后,DMA 接口通知 CPU 可以使用主存,并把总线控制权交回给 CPU。

 

这种方式的优缺点如下:

  • 优点:控制简单,适用于数据传输率很高的 I/O 设备,进行成组数据的传送。

  • 缺点:DMA 接口在访问主存时,CPU 基本上处于不工作状态或保持原状态。

所以这种方式下,CPU 对主存的利用率不高。

(2)周期挪用(或周期窃取)

如果 I/O设备 发出 DMA 请求时,CPU 并不是完全放弃总线的使用权,而是允许 I/O 设备 “挪用” 或 “窃取” 总线使用权一个或几个主存周期;这种方式就被称为 周期挪用,或 周期窃取。当 DMA 不请求时,CPU 仍可以继续访问主存。

这就意味着,CPU 在执行访问主存指令过程中,插入了 DMA 请求,那么就会被窃取了若干个存取周期,使CPU 延缓若干存取周期后再访问主存。

 

与 CPU 暂停访存的方式相比,这种方式既实现了 I/O 传送,又较好地发挥了主存与 CPU 的效率,是一种广泛采用的方法。

(3)DMA 与 CPU 交替访问

这种方法就是 DMA 与 CPU 轮流进行访存,平均分配主存的使用时间。

交替访问的方法适合于 CPU 的工作周期比主存存取周期长的情况。

 

例如,CPU 的工作周期是主存存取周期的 2 倍以上,那么可以将一个 CPU 周期分为 C~1~ 和 C~2~ 两个子周期,

其中 C~1~ 专门进行 CPU 访存,而 C~2~ 专门进行 DMA 访存。

这种方式不需要总线使用权的申请、建立和归还过程,总线使用权是通过 C~1~ 和 C~2~ 分别控制的。CPU 与 DMA 接口各自有独立的访存地址寄存器、数据寄存器和读/写信号。

在这种工作方式下,CPU 既不停止主程序的运行,也不会进入等待状态。当然,其相应的硬件逻辑也会变得更为复杂。

3. DMA 接口的功能

采用 DMA 方式进行数据传送时,数据的传输过程完全由 DMA 接口电路控制;因此 DMA 接口也被称为 DMA 控制器

DMA 接口应该具有以下功能:

  • 向 CPU 申请 DMA 传送;

  • 在 CPU 允许 DMA 工作时,处理总线控制权的转交;

  • 在 DMA 期间管理系统总线,控制数据传输;

  • 确定数据传送的起始地址和数据长度,并在传送过程中进行修正;

  • 数据块传送结束时,向 CPU 提交 DMA 操作完成的信号。

4. DMA 接口的组成

最简单的 DMA 接口组成如下图所示:

 

接口中的主要部件有:

  • 主存地址寄存器(AR):用于存放主存中需要交换的数据的地址。DMA 传送数据前,应该将数据在主存中的首地址送至 AR;DMA 传送过程中,每交换一次数据,就将 AR 的内容加 1,直到一批数据传送完毕。

  • 字计数器(WC):用来记录传送数据的总字数。DMA 传送过程中,每传送一个数据字,WC 就减 1(若是补码则 加 1),直到计数器为 0(溢出),就表示这批数据传送完毕;DMA 接口就可以向 CPU 发出中断请求信号了。

  • 设备地址寄存器(DAR):存放 I/O 设备的设备码,或者辅存中的寻址信息。

  • 数据缓冲寄存器(BR):用来暂存每次要传送的数据。

  • DMA 控制逻辑:由控制电路、时序电路、命令状态控制寄存器等组成,负责管理 DMA 的传送过程。每当设备准备好一个数据字,就向 DMA 接口提出请求(DREQ),DMA 控制逻辑就向 CPU 请求 DMA 服务,发出总线使用权的请求信号 HRQ;CPU 发出响应信号 HLDA 后,DMA 控制逻辑就开始负责管理 DMA 传送的全过程,并通知设备已经被授权了一个 DMA 周期(DACK)。

  • 中断机构:用来提出中断请求。当字计数器 WC 溢出(全 0)时,一批数据传送完毕,这个 “溢出信号” 就通过中断机构向 CPU 提出中断请求,由 CPU 做 DMA 操作的后处理。

需要注意的是,这里和程序中断方式中的中断,都属于 I/O 中断;但中断的目的不同:程序中断方式的中断是为了传送数据,而 DMA 中的中断只是为了报告一批数据传送结束。

5. DMA 的工作过程

DMA 的数据传送过程可以分为 预处理数据传送后处理 三个阶段。

(1)预处理

在 DMA 接口开始工作之前,需要先做一些准备工作,由 CPU 给它预置一些信息;这就是 “预处理” 阶段。

  • 告诉 DMA 控制逻辑数据传送的方向,是输入(写主存)还是输出(读主存);

  • 将设备码写入 DMA 设备地址寄存器 DAR,并启动设备;

  • 将交换数据的主存起始地址,写入 DMA 主存地址寄存器 AR;

  • 将交换数据的个数,写入字计数器 WC

这些准备工作由 CPU 执行几条 I/O 指令完成,也就是程序的初始化阶段。这些工作完成后,CPU 可以继续执行原来的程序。

当 I/O 设备准备就绪时,就通过 DMA 接口向 CPU 提出占用总线的请求;若有多个 DMA 接口同时申请,就按优先级由 硬件排队判优逻辑 决定响应顺序。

I/O 设备得到主存总线的控制权后,数据的传送就全部由 DMA 接口进行管理。

 

(2)数据传送

DMA 方式是以 数据块 为单位进行数据传送的。

这里以周期挪用的 DMA 方式为例,对于数据输入的场景,数据传送的流程如下:

① 当设备准备好一个数据字时,发出选通信号,将该字读到数据缓冲寄存器(BR)中,表示数据缓冲寄存器 “满”;

② 与此同时,设备向 DMA 接口发出请求(DREQ);

③ DMA 接口向 CPU 申请总线控制权(HRQ);

④ CPU 发回应答信号 HLDA,表示允许将总线控制权交给 DMA 接口;

⑤ 将主存地址寄存器 AR 中的主存地址,送至地址总线,并向主存发出写命令;

⑥ 通知设备已被授予一个 DMA 周期(DACK),并为交换下一个字做准备;

⑦ 将数据缓冲寄存器 DAR 的内容,送至数据总线;

⑧ 主存将数据总线上的信息,写至地址总线指定的存储单元中;

⑨ 修改 AR 中的主存地址和字计数值 WC。

⑩ 判断数据块是否传送结束,如果没有结束则继续传送;如果已结束(字计数器溢出),则向 CPU 发出程序中断请求,标志数据块传送结束。

若为输出数据,整体步骤类似,主要的不同就是:

  • 第 ① 步需要将 BR 中的数据送至 I/O 设备,表示 BR 已 “空”;

  • 第 ⑤ 步需要向主存发出读命令;

  • 第 ⑦ ⑧ 步,是将主存相应存储单元的内容,通过数据总线写入 BR,并送至输出设备;

(3)后处理

当 DMA 的中断请求得到响应后,CPU 停止原程序的执行,转而去执行中断服务程序,做一些 DMA 的结束工作,这就是 “后处理” 阶段。

DMA 的结束阶段工作主要包括:

  • 校验送入主存的数据是否正确;

  • 决定是否继续用 DMA 传送其他数据块。若继续传送,则又要对 DMA 接口进行初始化;若不需要传送,则停止外设;

  • 测试在传送过程中是否发生错误;如果出错,则转错误诊断及处理程序。

6. DMA 方式和程序中断方式的对比

DMA 方式和程序中断方式是 I/O 设备最为常见的信息传送控制方式。两者都用到了中断,但中断的目的不同:程序中断方式的中断是为了传送数据,而 DMA 中的中断只是为了报告一批数据传送结束;而且中断的优先级上,DMA 中断的优先级更高。

6.6 章节练习

一、单项选择题
  1. 【2011真题】在系统总线的数据线上, 不可能传输的是 ( )

A.指令 B.操作数 C.握手(应答)信号 D.中断类型号

答案:C

要点:握手(应答)信号应该在通信总线上传输。

  1. 【2012真题】下列关于 USB 总线特性的描述中,错误的是 ( )

A. 可实现外设的即插即用和热拔插 B. 可通过级联方式连接多台外设

C. 是一种通信总线,连接不同外设 D. 同时可传输 2 位数据,数据传输率高

答案:D

要点:USB(通用串行总线)属于串行总线,不能同时传输 2 位数据。

  1. 【2016真题】下列关于总线设计的叙述中,错误的是 ( )

A.并行总线传输比串行总线传输速度快 B.采用信号线复用技术可减少信号线数量

C. 采用突发传输方式可提高总线数据传输率 D.采用分离事务通信方式可提高总线利用率

答案:A

要点:实际时钟频率比较低的情况下,并行总线因为可以同时传输多位数据,速率确实比串行总线快。但是,随着技术的发展,时钟频率越来越高,并行导线之间的相互干扰越来越严重,当时钟频率提高到一定程度时,传输的数据已经无法恢复。而串行总线因为导线少,线间干扰容易控制,反而可以通过不断提高时钟频率来提高传输速率。

  1. 【2009真题】假设某系统总线在一个总线周期中并行传输 4B 信息,一个总线周期占用 2 个时钟周期,总线时钟频率为 10MHz,则总线带宽是 ( )

A. 10MB/s B. 20MB/s C. 40MB/s D. 80MB/s

答案:B

要点:重点考虑每个时钟周期传输的数据量,总线带宽 = 总线时钟频率 × 每个总线时钟周期传输数据量

  1. 【2018真题】下列选项中,可提高同步总线数据传输率的是 ( )

Ⅰ .增加总线宽度 Ⅱ .提高总线工作频率 Ⅲ.支持突发传输 Ⅳ.采用地址/数据线复用

A. 仅Ⅰ 、Ⅱ B. 仅Ⅰ 、Ⅱ 、Ⅲ C. 仅Ⅲ、Ⅳ D. Ⅰ 、Ⅱ 、Ⅲ和Ⅳ

答案:B

要点:总线数据传输率 = 总线工作频率 × (总线宽度 / 8)。

采用地址/数据线复用只是减少了线的数量,节省了成本,并不能提高传输率。

  1. 【2020真题】QPI 总线是一种点对点全双工同步串行总线,总线上的设备可同时接收和发送信息,每个方向可同时传输 20 位信息(16 位数据 + 4 位校验位),每个 QPI 数据包有 80 位信息,分 2 个时钟周期传送,每个时钟周期传递 2 次。因此,QPI 总线带宽为:每秒传送次数 x 2B x 2。若 QPI 时钟频率为 2.4GHz,则总线带宽为( )

A. 4.8GB/s B. 9.6GB/s C. 19.2GB/s D. 38.4GB/s

答案:C

要点:公式中最后的 × 2,是指全双工模式下两个方向的数据传输;而 QPI 采用了双倍数据率技术(DDR),时钟信号上升沿和下降沿都可以进行数据传输(题目中提示 ”每个时钟周期传递 2 次“),因此带宽为:

$$
2.4GHz \; × \; 2B\; × \;2\; ×\; 2 =19.2GB/s
$$

  1. 【2014真题】一次总线事务中, 主设备只需给出一个首地址, 从设备就能从首地址开始的若干连续单元读出或写入多个数据。 这种总线事务方式称为 ( )

A. 并行传输 B. 串行传输 C. 突发传输 D. 同步传输

答案:C

  1. 【2012真题】某同步总线的时钟频率为 100MHz,宽度为 32 位,地址/数据线复用,每传输一个地址或数据占用一个时钟周期。若该总线支持突发(猝发)传输方式,则一次“主存写”总线事务传输 128 位数据所需要的时间至少是 ( )

A. 20ns B. 40ns C. 50ns D.80ns

答案:C

要点:对于宽度为 32 位的总线,传输 128 位数据需要 128 / 32 = 4 个时钟周期;如果数据地址连续,在突发方式下只需要传递一次地址,而地址/数据线复用,所以共需 5 个时钟周期。

  1. 【2015真题】下列有关总线定时的叙述中,错误的是 ( )

A.异步通信方式中,全互锁协议的速度最慢

B.异步通信方式中,非互锁协议的可靠性最差

C.同步通信方式中,同步时钟信号可由各设备提供

D.半同步通信方式中,握手信号的采样由同步时钟控制

答案:C

要点:在同步通信方式中,系统必须采用统一的时钟信号,否则无法实现统一的时钟。

  1. 【2021真题】下列关于总线的叙述中,错误的是 ( )

A. 总线是在两个或多个部件之间进行数据交换的传输介质

B. 同步总线由时钟信号定时,时钟频率不一定等于工作频率

C. 异步总线由握手信号定时,一次握手过程完成一位数据交换

D. 突发(Burst)传送总线事务可以在总线上连续传送多个数据

答案:C

要点:一次握手过程可能完成不止一位的数据交换。

  1. 【2014真题】下列有关 I/O 接口的叙述中, 错误的是 ( )

A. 状态端口和控制端口可以合用同一个寄存器

B. I/O 接口中 CPU 可访问的寄存器称为 I/O 端口

C. 采用独立编址方式时,I/O 端口地址和主存地址可能相同

D. 采用统一编址方式时,CPU 不能用访存指令访问 I/O 端口

答案:D

要点:采用统一编址时, CPU 访存和访问 I/O 端口用的是一样的指令,所以访存指令可以访问 I/O 端口。

  1. 【2017真题】 I/O 指令实现的数据传送通常发生在 ( )

A. I/O 设备和 I/O 端口之间 B.通用寄存器和 I/O 设备之间

C. I/O 端口和 I/O 端口之间 D.通用寄存器和 I/O 端口之间

答案:D

要点:在执行一条指令时, CPU 使用地址总线选择所请求的 I/O 端口,使用数据总线在 CPU 寄存器和端口之间传输数据。

  1. 【2021真题】下列选项中,不属于 I/O 接口的是 ( )

A. 磁盘驱动器 B. 打印机适配器 C. 网络控制器 D. 可编程中断控制器

答案:A

要点:I/O 接口即 I/O 控制器,其功能是接收主机发送的 I/O 控制信号,并实现主机和外部设备之间的信息交换。磁盘驱动器是由磁头、磁盘和读写电路等组成的,也就是我们平常所说的磁盘本身。

  1. 【2014真题】若某设备中断请求的响应和处理时间为 100ns, 每 400ns 发出一次中断请求,中断响应所允许的最长延迟时间为 50ns,则在该设备持续工作过程中,CPU 用于该设备的 I/O 时间占整个 CPU 时间的百分比至少是 ( )

A. 12.5% B. 25% C. 37.5% D. 50%

答案:B

要点:允许的延迟为干扰信息,无论延迟多久,每 400ns 都要花费 100ns 处理中断。

  1. 【2015真题】在采用中断 I/O 方式控制打印输出的情况下, CPU 和打印控制接口中的 I/O 端口之间交换的信息不可能是 ( )

A.打印字符 B. 主存地址 C.设备状态 D. 控制命令

答案: B

要点:在程序中断 I/O 方式中, CPU 和打印机直接交换数据,打印字符直接传输到打印机的 I/O端口,不会涉及到主存地址。

  1. 【2018真题】下列关于外部 I/O 中断的叙述中,正确的是 ( )

A. 中断控制器按所接收中断请求的先后次序进行中断优先级排队

B. CPU 响应中断时,通过执行中断隐指令完成通用寄存器的保护

C. CPU 只有在处于中断允许状态时,才能响应外部设备的中断请求

D. 有中断请求时,CPU 立即暂停当前指令执行,转去执行中断服务程序

答案:C

要点:中断优先级由屏蔽字决定,而不是根据请求的先后次序。

中断隐指令完成的工作有: 1)关中断; 2)保存断点; 3)引出中断服务程序,通用寄存器的保护由中断服务程序完成。

有中断请求时,先要由中断隐指令完成中断前程序的状态保存。

  1. 【2019真题】某设备以中断方式与 CPU 进行数据交换, CPU 主频为 1GHz,设备接口中的数据缓冲寄存器为 32 位,设备的数据传输率为 50kB/s。若每次中断开销(包括中断响应和中断处理)为 1000 个时钟周期,则 CPU 用于该设备输入/输出的时间占整个 CPU 时间的百分比最多是 ( )

A. 1.25% B. 2.5% C. 5% D. 12.5%

答案:A

要点:设备接口中的数据缓冲寄存器为 32 位,所以一次中断可以传输 4B 数据;设备数据传输率为 50kB/s,因此 1s 内共需要 50kB ÷ 4B = 12.5k 次中断。而每次中断开销为 1000 个时钟周期,所以 I/O 时间占比为:

$$
(12.5k \,×\, 1000) \, / \, 1G = 1.25\%
$$

  1. 【2019真题】下列关于 DMA 方式的叙述中,正确的是 ( )

Ⅰ.DMA 传送前由设备驱动程序设置传送参数

Ⅱ.数据传送前由 DMA 控制器请求总线使用权

Ⅲ.数据传送由 DMA 控制器直接控制总线完成

Ⅳ.DMA 传送结束后的处理由中断服务程序完成

A.仅 Ⅰ、 Ⅱ B.仅 I、Ⅲ、 Ⅳ C.仅 Ⅱ、Ⅲ、Ⅳ D. I、Ⅱ、Ⅲ、Ⅳ

答案:D

  1. 【2020真题】若设备采用周期挪用 DMA 方式进行输入和输出,每次 DMA 传送的数据块大小为 512 字节,相应的 I/O 接口中有一个 32 位数据缓冲寄存器。对于数据输入过程,下列叙述中,错误的是 ( )

A. 每准备好 32 位数据,DMA 控制器就发出一次总线请求

B. 相对于 CPU,DMA 控制器的总线使用权的优先级更高

C. 在整个数据块的传送过程中,CPU 不可以访问主存储器

D. 数据块传送结束时,会产生 “DMA 传送结束” 中断请求

答案:C

要点:周期挪用方式下,DMA 利用 CPU 不访问存储器的那些周期来实现 DMA 操作,此时 DMA 可以使用总线而不用通知 CPU,也不会妨碍 CPU 的工作。

  1. 【2021真题】异常事件在当前指令执行过程中进行检测,中断请求则在当前指令执行后进行检测。下列事件中,相应处理程序执行后,必须回到当前指令重新执行的是 ( )

A. 系统调用 B. 页缺失 C. DMA 传送结束 D. 打印机缺纸

答案:B

  1. 【2022真题】下列关于中断 I/O 方式的叙述中,不正确的是 ( )

A. 适用于键盘、针式打印机等字符型设备

B. 外设和主机之间的数据传送通过软件完成

C. 外设准备数据的时间应小于中断处理时间

D. 外设为某进程准备数据时 CPU 可运行其他进程

答案:C

要点:若外设准备数据的时间小于中断处理时间, 则可能导致数据丢失。

二、综合应用题
  1. 【2018真题】假定计算机的主频为 500MHz,CPI 为 4。现有设备 A 和 B,其数据传输率分别为 2MB/s 和 40MB/s,对应 I/O 接口中各有一个 32 位数据缓冲寄存器。请回答下列问题,要求给出计算过程。

(1)若设备 A 采用定时查询 I/O 方式,每次输入/输出都至少执行 10 条指令。设备 A 最多间隔多长时间查询一次才能不丢失数据?CPU 用于设备 A 输入/输出的时间占 CPU 总时间的百分比至少是多少?

(2)在中断 I/O 方式下,若每次中断响应和中断处理的总时钟周期数至少为 400,则设备 B 能否采用中断 I/O方式?为什么?

(3)若设备 B 采用 DMA 方式,每次 DMA 传送的数据块大小 1000B,CPU 用于 DMA 预处理和后处理的总时钟周期数为 500,则 CPU 用于设备 B 输入/输出的时间占 CPU 总时间的百分比最多是多少?

答案:

(1)程序定时向缓存端口查询数据,由于缓存端口大小有限,必须在传输完端口大小的数据时访问端口,以防止部分数据没有被及时读取而丢失。

设备 A 准备 32 位(4B)数据所用时间为

$$
4B \,÷\, 2MB/s = 2μs
$$

所以最多每隔 2µs 必须查询一次。

这样,每秒的查询次数至少是 1s / 2µs = 5×105,每秒 CPU 用于设备 A 输入/输出的时间至少为

$$
5 × 10^5 × 10 × 4 = 2×10^7
$$

个时钟周期,占整个 CPU 时间的百分比至少是

$$
2 × 10^7 \,÷\, 500M = 4\%
$$

(2)设备 B 准备 32 位(4B)数据所用时间为

$$
4B \,÷\, 40MB/s = 0.1μs
$$

而中断响应和中断处理的时间为

$$
400 \,×\, (1/500M) =0.8µs
$$

准备数据的时间小于中断响应和中断处理的时间,所以等待中断处理的过程中数据就会被刷新,从而造成丢失。因此,设备 B 不适合采用中断 I/O 方式。

(3)在 DMA 方式中,数据的传送过程由 DMA 控制,只有预处理和后处理需要 CPU 处理。

设备 B 每秒的 DMA 次数最多为

$$
40MB \,÷\, 1000B = 40000
$$

CPU 用于设备 B 输入/输出的时间占 CPU 总时间的百分比最多为

$$
40000 \,×\, 500 \,÷\, 500M = 4\%
$$

  1. 【2022真题】假设某磁盘驱动器中有 4 个双面盘片, 每个盘面有 20000 个磁道, 每个磁道有 500 个扇区,每个扇区可记录 512 字节的数据,盘片转速为 7200 RPM(转/分),平均寻道时间为 5ms。请回答下列问题。

(1)每个扇区包含数据及其地址信息, 地址信息分为 3 个字段。 这 3 个字段的名称各是什么?对于该磁盘, 各字段至少占多少位?

(2)一个扇区的平均访问时间约为多少?

(3)若采用周期挪用 DMA 方式进行磁盘与主机之间的数据传送, 磁盘控制器中的数据缓冲区大小为 64 位, 则在一个扇区读写过程中, DMA 控制器向 CPU 发送了多少次总线请求?若 CPU 检测到 DMA 控制器的总线请求信号时也需要访问主存,则 DMA 控制器是否可以获得总线使用权?为什么?

答案:

(1)3个字段的名称为:柱面号(或磁道号)、盘面号(或磁头号)、扇区号。

每个盘面有 20000 个磁道,因此该磁盘共有 20000个柱面,柱面号字段至少占 15 位;

磁盘共有 8 个盘面,因此盘面号字段至少占 3 位;

每个磁道有 500 个扇区,因此扇区号字段至少占 9 位。

(2)一个扇区的访问时间由 寻道时间等待时间传输时间 三部分组成。

平均寻道时间为 5ms;

平均等待时间,等于磁盘转半圈所需要的时间:

$$
60s \,÷\, 7200 \,÷\, 2 = \frac{1}{240} s ≈ 4.167 ms
$$

平均传输时间,等于一个扇区划过磁头下方所需要的时间:

$$
60s \,÷\, 7200 \,÷\, 500 = \frac{1}{60000} s ≈ 0.0167 ms
$$

因此一个扇区的平均访问时间约为:

$$
5ms \,+\, 4.167ms \,+\, 0.0167ms ≈ 9.18ms
$$

(3)磁盘控制器中的数据缓冲区每充满一次(64位,8B),DMA 控制器就需要发出一次总线请求。

因此在一个扇区读写过程中,DMA 控制器向 CPU 发送的总线请求次数为:

$$
512 B \,÷\, 8B = 64
$$

采用周期挪用 DMA 方式,当 CPU 和 DMA 控制器都需要访问主存时,DMA 控制器可以优先获得总线使用权。因为一旦磁盘开始读写,就必须按时完成数据传送,否则数据缓冲区中的数据会发生丢失。

  • 26
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

落樱听雨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值