冯诺依曼结构
“冯诺依曼结构”这个词出自美籍犹太数学家约翰·冯诺依曼(John von Neumann)的论文:《EDVAC报告书的第一份草案》(First Draft of a Report on the EDVAC),于1945年6月30日。冯诺依曼由于在曼哈顿工程中需要大量的运算,从而使用了当时最先进的两台计算机Mark I和ENIAC,在使用Mark I和ENIAC的过程中,他意识到了存储程序的重要性,从而提出了存储程序逻辑架构。
“存储程序”的思想奠定了现代计算机的基本结构,以此概念为基础的各类计算机通称为冯•诺依曼计算机,其特点如下:
- 采用“存储程序”的工作方式。
- 计算机硬件系统由运算器、存储器、控制器、输入设备和输出设备5大部件组成。
- 指令和数据以同等地位存储在存储器中,形式上没有区别,但计算机应能区分它们。
- 指令和数据均用二进制代码表示。
- 指令由操作码和地址码组成,操作码指出操作的类型,地址码指出操作数的地址。
典型的冯诺依曼结构如下图所示,以运算器为中心,这使得存储器与I/O设备之间的数据交换也需要经过运算器
现代的计算机已转化为以存储器为中心,I/O设备与存储器能够直接进行数据交换,无需再经过CPU
由于运算器和控制器在逻辑关系和电路结构上联系十分紧密,尤其在大规模集成电路制作工艺出现后,这两大部件往往制作在同一芯片上,因此,通常将它们合起来统称为中央处理器(Central Processing Unit) , 简称CPU。把输入设备与输出设备简称为I/O设备(Input/Output equipment) 。
这样, 现代计算机可认为由三大部分组成:CPU、IO设备及主存储器(Main Memory,MM) 。CPU与主存储器合起来又可称为主机, I/O设备叫作外部设备。
多级层次结构
从用户的角度看,人们在操作系统提供的运行环境下,首先用高级语言编写程序(称为源程序),然后将源程序翻译成汇编语言程序,再将其翻译成机器能识别的机器语言程序(称为目标程序),最后用微程序解释每条机器指令。这样,就构成一个常见的计算机系统的5级层次结构,如下图所示:
相邻层级之间的关系,下层是上层的基础,上层是下层的扩展。随着超大规模集成电路技术的不断发展,部分软件功能可以由硬件来实现,所以软/硬件交界面的划分也不是绝对的。
计算机系统工作原理
从源代码到可执行文件
用高级语言编写好一段程序之后,需要经过一系列“翻译“过程,才能得到计算机能够执行的机器代码。这里以最简单的 hello 程序为例,跟踪整个程序的生命周期
#include <stdio.h>
int main()
{
printf("hello, world\n");
return 0;
}
程序员通过编辑器创建并保存的文件 hello.c 本质上只是一段由 0 和 1 组成的二进制位序列,将 8 个位组织成 1 组,称之为字节。
现代计算机系统大多使用 ASCII 编码格式来表示文本字符,实际上就是用一个唯一的单字节大小的整数值来表示一个文本字符。使用 ASCII 编码来表示 hello.c 文件结果如下:
为了在系统上运行 hello.c 程序,需要将每条 C 语句都转化成有一系列低级的机器指令。在 Unix 系统上,从源文件到目标文件的转化可以由 GCC 编译器来完成
linux> gcc -o hello hello.c
这里 GCC 编译器将程序从文本文件 hello.c 翻译成二进制可执行文件 hello,需要经过四个阶段完成。
- 预处理阶段:预处理器(cpp)根据源代码中以
#
开头的命令直接展开,输出一个 hello.i 文件。(这个阶段不会进行任何的语法检查) - 编译阶段:编译器(ccl)对预处理后的文件进行编译,生成一个汇编语言源程序 hello.s。如下所示:
main:
subq $8, %rsp
movl $.LCO, $edi
call puts
movl $0, %eax
addq $8, %rsp
ret
- 汇编阶段:汇编器(as)会将 hello.s 文件翻译成机器语言指令,打包成一种叫做可重定位目标程序(relocatable object program)的格式,结果保存到目标文件 hello.o 中
- 链接阶段:这里 hello 程序调用了 printf 函数,该标准库函数存在于一个名为 printf.o 的单独预编译目标文件中。链接器(ld)就负责将 hello.o 与 printf.o 合并到一起,输出一个二进制可执行目标文件 hello,该文件可以被加载到内存中由系统执行
linux> ./hello
hello, world
linux>
存储程序的基本思想
“存储程序”的基本思想,就是将程序和数据一样,存放在主存中;运行时通过地址访问到程序的内容,解析出对应的指令进行执行。
- 程序执行前,先将第一条指令的地址存放在程序计数器(PC)中;
- 将PC的内容作为地址,访问主存,取出指令;
- 在每条指令执行过程中,都需要计算下一条将执行指令的地址,并送至PC
- 如果当前指令是顺序执行的,则下一条指令地址是PC的内容加上当前指令的长度
- 如果是跳转指令,则下一条指令的地址是指定的目标地址;
- 当前指令执行完毕后,再根据PC的值作为地址访问主存,取出的是下一条将要执行的指令。
计算机的功能部件
存储器
存储器可分为主存储器(也称内存储器或主存)和辅助存储器(也称外存储器或辅存)
- 主存储器可以被CPU直接访问
- 辅助存储器中的信息必须调入主存储器之后,才能够被CPU访问
主存中的每个存储单元有一个唯一的编号,叫做存储单元的”地址“(Address)。主存储器的工作方式是按存储单元的地址进行存取,这种存取方式称为按地址存取方式
- MAR(Memory Address Register,存储器地址寄存器):用来存放想要访问的存储单元的地址,它的位数决定了能访问的存储单元的最大个数。
- MDR(Memory Data Register,存储器数据寄存器):用来存放从存储体单元中取出,或者准备向存储体单元存入的数据,它的位数和存储字相等。
MAR 与 MDR 虽然是存储器中的一部分,但在现代计算机中却是集成在 CPU 中;另外,高速缓存(Cache)也集成在 CPU 当中
运算器
运算器是计算机的执行部件,用于进行算术运算和逻辑运算。运算器的核心是算术逻辑单元(Arithmetic and Logic Unit,ALU),还包括了最少三个通用寄存器
- ACC(Accumulator,累加器)
- MQ(Multiplier-Quotient Register,乘商寄存器)
- X: 操作数寄存器
这三个寄存器在完成不同的算术运算时,所存放的操作数也各不相同。具体的情况如下表所示:
加法 | 减法 | 乘法 | 除法 | |
---|---|---|---|---|
ACC | 被加数及和 | 被减数及差 | 乘积高位 | 被除数及余数 |
MQ | —— | —— | 乘数及乘积低位 | 商 |
X | 加数 | 减数 | 被乘数 | 除数 |
控制器
控制器是计算机的指挥中心,由其“指挥”各部件自动协调地进行工作,完成一条指令操作,需要取指、分析和执行3个阶段。控制器由程序计数器(PC)、指令寄存器(IR)和控制单元(CU)组成
- 控制单元(CU)是控制器的核心组件,用来分析当前指令所需完成的操作,并发出各种微操作命令序列,从而控制所有被控对象
- 程序计数器(PC)用来存放当前欲执行执行的地址,具有自增功能(完成指令后,自动指向下一条指令的地址),与MAR之间有一条直接通路
- 指令寄存器(IR)用来存放当前的指令,其内容来自主存储器的MDR
- 指令中的操作码 OP(IR) 送至 CU,用以分析指令并发出各种微指令序列
- 地址码 Ad(IR) 送往 MAR,用以取操作数
I/O设备
I/O 系统包括各种 I/O 设备及其相应的接口。每一种 I/O 设备都由 I/O 接口与主机联系,它接收 CU 发出的各种控制命令,并完成相应的操作。
计算机性能指标
机器字长
通常所说的“16位或32位机器”,其中的16、32指的就是机器字长,简称字长。字长是指计算机进行一次整数运算(即定点整数运算)所能处理的二进制数据的位数,通常与CPU的寄存器位数、ALU有关。因此,字长一般等于通用寄存器的位数和ALU的宽度,字长越长,数的表示范围越大,计算精度越高。
- 指令字长:一个指令字中包含的二进制代码的位数
- 存储字长:一个存储单元存储的二进制代码的位数
指令字长一般取存储字长的整数倍,若指令字长等于存储字长的2倍,则需要2个访存周期来取出一条指令;若指令字长等于存储字长,则取指令周期等于机器周期。
早期的存储字长一般与指令字长、字长相等,因此访问一次主存储器便可取出一条指令或一个数据。随着计算机的发展,指令字长、字长都可变,但必须都是字节(Byte)的整数倍。
数据通路宽度
数据通路带宽,是指数据总线一次所能并行传送信息的位数,它关系到数据的传送能力。这里所说的数据通路带宽是指外部数据总线的宽度,它与 CPU 内部的数据总线宽度(机器字长)可能不同。
主存容量
主存容量是指主存储器所能存储信息的最大容量,通常以字节来衡量,也可用字数$\times$字长(如 $512K\times16$ 位)来表示存储容量。
- MAR 的位数反映了存储单元的个数。例如,MAR 为 16 位,即该存储体内有 $2^{16}=65536$ 个存储单元(可称为 $64K$ 内存,$1K = 1024$)
- MDR 的位数反映了存储单元的字长。若 MDR 为 32位,则表示存储容量为 $64K\times 32$ 位
运算速度
- 吞吐量:指系统在单位时间内处理请求的数量。它取决于信息能多快地输入内存,CPU能多快的取指令,数据能多快地从内存取出或存入,以及所得结果能多快地从内存送给一台外部设备。几乎每步都关系到主存储器,因此系统吞吐量主要取决于主存储器的存取周期。
- 响应时间:指从用户向计算机发送一个请求,到系统对该请求做出响应并获得所需结果的等待时间。通常包括CPU 时间(运行一个程序所花费的时间)与等待时间(用于磁盘访问、存储器访问、I/O操作、操作系统开销等时间)
- CPU 时钟周期:指机器内部主时钟脉冲信号的宽度,它是 CPU 工作的最小时间单位
时钟脉冲信号是有机器脉冲源发出的脉冲信号经整形和分频后形成
时钟周期以相邻状态单元间组合逻辑电路的最大延迟为基准确定
时钟周期也以指令流水线的每个流水段的最大延迟间确定
- 主频:指机器内部主时钟的频率,即时钟周期的倒数,它是衡量机器速度的重要参数
CPU 时钟周期 = 1/主频
主频通常以 Hz(赫兹)为单位,10Hz 表示每秒 10 次
- CPI(Cycle Per Instruction):即执行一条指令所需的时钟周期数
- IPS(Instructions Per Second):即每秒执行多少条指令,IPS = 主频 / 平均 CPI
不同指令的时钟周期数可能不同,因此对于一个程序或一台机器来说,其 CPI 指该程序或该机器指令集中的所有指令执行所需的平均时钟周期数,此时 CPI 是一个平均值
- CPU 执行时间:指运行一个程序所花费的时间
CPU 执行时间 = CPU 时钟周期数 / 主频 = (指令条数×CPI) / 主频
对于同一个程序,CPU 的执行时间就代表了 CPU 的性能,它主要取决于三个要素:主频、CPI 和 指令数。这三者是相互制约的。不同的机器可以有不同的指令集,更改指令集可以让程序的指令数更少,但 CPI 可能就会增大;同时可能引起 CPU 结构的调整,从而造成主频的降低。
- MIPS(Million Instructions Per Second):即每秒执行多少百万条指令
- FLOPS(Floating-point Operations Per Second):即每秒执行多少次浮点运算
- MFLOPS(Million FLOPS):即每秒执行多少百万次浮点运算
MIPS = 指令条数 / (执行时间 $\times 10^6$) = 主频 / ($CPI \times 10^6$)
MIPS 对不同机器进行性能比较是有缺陷的,因为不同机器的指令集不同,指令的功能也就不同,CPI 和时钟周期也不同,因而同一条指令在不同机器上所用的时间也不同
在描述存储容量、文件大小等时,K、M、G、T 通常用 2 的幂次表示,如 $1Kb = 2^{10}b$
在描述速率、频率等时,通常用 10 的幂次表示,如 $1kb/s = 10^3 b/s$
通常前者用大写的 K,后者用小写的 k,但其他前缀均为大写,表示的含义取决于所用的场景
参考资料
- 冯诺依曼结构
- 《深入理解计算机系统》
- 王道计算机教育 《2025年计算机组成原理考研复习指导》