第二章 CPU与内存
学习目标
- 理解冯诺依曼架构的基本原理和计算机五大组成部分
- 掌握CPU的基本结构:算术逻辑单元(ALU)、控制单元(CU)和寄存器
- 理解CPU指令执行的基本流程:取指、解码、执行
- 掌握内存层次结构:寄存器、高速缓存(Cache)、主存、虚拟内存
- 理解缓存的工作原理和局部性原理
- 掌握CPU与内存交互的基本方式:总线和DMA
2.1 冯诺依曼架构
2.1.1 计算机架构的诞生
1945年,美籍匈牙利数学家约翰·冯·诺依曼(John von Neumann)在一份关于EDVAC(Electronic Discrete Variable Automatic Computer,电子离散变量自动计算机)的草案中,提出了一种全新的计算机架构设计。这份草案影响深远,以至于今天几乎所有的通用计算机都采用这种架构,人们称之为冯诺依曼架构(Von Neumann Architecture)。
冯诺依曼架构的核心思想是存储程序(Stored-Program)概念:程序和数据都存储在计算机的内存中,计算机可以像处理数据一样修改程序本身。这一思想奠定了现代软件的基础——程序不再是一成不变的硬件接线,而是可以动态改变的可修改内容。
2.1.2 冯诺依曼架构的五大组成部分
冯诺依曼架构将计算机分为五个核心部分:
1. 运算器(Arithmetic Logic Unit, ALU) 负责执行所有的算术运算(如加、减、乘、除)和逻辑运算(如与、或、非、比较)。它是计算机的执行部件,是计算的真正发生地。
2. 控制器(Control Unit, CU) 负责协调和控制计算机各部件的工作。它的核心功能是从内存中取指令、分析指令(解码),然后向其他部件发出控制信号来完成指令的执行。没有控制器,计算机就像没有指挥家的交响乐团,各部件各行其是。
3. 存储器(Memory) 存储数据和指令。在现代计算机中,这通常指主存(Main Memory),即我们常说的内存(RAM,Random Access Memory)。存储器是计算机的"记忆",没有它,计算机就无法保持任何信息。
4. 输入设备(Input Device) 将外部信息输入到计算机中。常见的输入设备包括键盘、鼠标、触摸屏、扫描仪、传感器等。输入设备是计算机感知世界的"感官"。
5. 输出设备(Output Device) 将计算机处理后的信息输出。常见的输出设备包括显示器、打印机、扬声器、执行器等。输出设备是计算机向世界"表达"的方式。
2.1.3 冯诺依曼瓶颈
冯诺依曼架构虽然简单高效,但存在一个固有的问题,称为冯诺依曼瓶颈(Von Neumann Bottleneck)。
问题在于:运算器和存储器是分开的两个部件,数据需要在它们之间传输。CPU的处理速度增长非常快(遵循摩尔定律),但内存访问速度的增长相对缓慢。这就造成了CPU经常需要等待内存读写完成才能继续工作,形成了一个速度不匹配的瓶颈。
打个比方:这就像一个天才的厨师(CPU),但需要等待食材从远处的仓库(内存)送来。厨师做得再快,如果食材供应跟不上,整体效率仍然受限。
现代计算机通过多种技术来缓解这个问题: - 在CPU和内存之间加入高速缓存(Cache) - 使用更宽的数据总线 - 采用指令流水线(Pipeline)技术 - 发展专用AI芯片,采用不同的架构(如存算一体)
2.1.4 哈佛架构与改进
与冯诺依曼架构相对的是哈佛架构(Harvard Architecture)。哈佛架构将指令存储器和数据存储器分开,指令总线和数据总线也各自独立。这样,CPU可以同时获取指令和访问数据,避免了冯诺依曼瓶颈。
然而,哈佛架构的电路更加复杂,通用性也不如冯诺依曼架构。因此,现代计算机通常采用修正的哈佛架构(Modified Harvard Architecture),在内部保留哈佛架构的特点(独立的指令Cache和数据Cache),但对外呈现统一的内存视图。
2.1.5 计算机的基本工作流程
冯诺依曼架构计算机的基本工作流程是一个不断循环的过程,通常称为执行周期(Instruction Cycle):
- 取指(Fetch):控制器从内存中读取下一条指令到指令寄存器
- 解码(Decode):控制器分析指令,确定需要执行什么操作
- 执行(Execute):运算器执行指令规定的操作
- 访存(Memory Access):如果需要,访问内存读写数据
- 写回(Write Back):将执行结果写回寄存器
这个过程不断循环,直到遇到停机指令或发生错误。
本节小结
- 冯诺依曼架构是现代计算机的基础,由运算器、控制器、存储器、输入/输出设备五部分组成
- "存储程序"概念是冯诺依曼架构的核心创新,程序和数据都存储在内存中
- 冯诺依曼瓶颈是指CPU和内存之间数据传输速度不匹配的问题
- 哈佛架构将指令存储器和数据存储器分开,可以缓解冯诺依曼瓶颈
- 计算机的基本工作流程是"取指-解码-执行-访存-写回"的循环
2.2 CPU的基本结构
2.2.1 CPU是什么
CPU(Central Processing Unit),即中央处理器,是计算机的大脑。它负责执行指令、处理数据,是计算机性能的核心决定因素。
现代CPU通常是一个集成电路(Integrated Circuit, IC)芯片,封装在金属或陶瓷外壳中,通过引脚与外部电路连接。一个典型的桌面CPU可能有1000多个引脚,这些引脚负责电源、地址信号、数据信号和控制信号等。
CPU的性能通常用以下几个指标来衡量: - 时钟频率(Clock Frequency):CPU的工作节拍,单位是GHz(吉赫兹)。1 GHz表示每秒可以执行10亿个时钟周期。频率越高,理论上CPU执行速度越快。 - 每周期指令数(Instructions Per Cycle, IPC):每个时钟周期能完成的指令数。CPU架构的改进可以提高IPC。 - 核心数(Number of Cores):CPU中独立的处理单元数量。多核CPU可以并行执行多个任务。
2.2.2 算术逻辑单元(ALU)
ALU(Arithmetic Logic Unit)是CPU中负责执行计算的部件。它的名字已经说明了它的两大类功能:算术运算和逻辑运算。
算术运算包括: - 基本运算:加法、减法、乘法、除法 - 在现代CPU中,乘法和除法通常由专用的乘法器和除法器实现,比用加法器反复相加要快得多
逻辑运算包括: - 按位与(AND)、或(OR)、异或(XOR)、取反(NOT) - 比较运算:如大于、小于、等于 - 移位运算:左移、右移、算术移位、逻辑移位
ALU的基本结构:
┌─────────────┐
A ──→│ │
│ ALU │──→ 结果
B ──→│ │
└─────────────┘
│
↓
操作码(Operation Code)
ALU接受两个输入A和B,以及一个操作码(Opcode),输出运算结果。操作码指示ALU执行哪种操作。
标志位(Flag):
ALU还会产生一些额外的信息,称为标志位或状态标志: - 零标志(Zero Flag, ZF):运算结果是否为0 - 符号标志(Sign Flag, SF):运算结果是否为负数 - 进位标志(Carry Flag, CF):无符号运算是否产生进位或借位 - 溢出标志(Overflow Flag, OF):有符号运算是否发生溢出
这些标志位会被送到控制单元,用于决定后续的指令执行流程。
2.2.3 控制单元(CU)
控制单元(Control Unit, CU)是CPU的"指挥官",负责协调和控制CPU内部各部件的工作,以及CPU与外部的交互。
控制单元的主要功能包括:
指令获取(Instruction Fetch): - 程序计数器(Program Counter, PC)保存下一条要执行的指令地址 - 控制单元将PC的值送到地址总线,从内存读取指令 - 读取的指令存入指令寄存器(Instruction Register, IR)
指令解码(Instruction Decode): - 控制单元分析指令的操作码(Opcode),确定这是什么指令 - 分析操作数(Operand),确定需要访问哪些寄存器或内存位置 - 向相关部件发出控制信号,准备执行
指令执行(Instruction Execution): - 根据解码结果,向ALU发出运算控制信号 - 控制数据在寄存器组、ALU、内存之间流动 - 记录执行结果,更新程序计数器
现代CPU的控制单元实现方式:
早期CPU使用硬连线控制(Hardwired Control):控制单元是一组固定的逻辑电路,每个指令有对应的硬件线路。优点是速度快,缺点是不灵活,增加新指令需要改变电路。
现代CPU更多使用微程序控制(Microprogrammed Control):将每条指令分解为更基本的微操作(Micro-operation),存储在控制存储器(Control Store)中。微程序可以看作是指令的解释器。优点是设计灵活,易于修改;缺点是因为多了一层解释,速度稍慢。
RISC架构(如ARM、MIPS)倾向于使用硬连线控制,因为指令集简单规整;CISC架构(如x86)则常用微程序控制,因为指令复杂多样。
2.2.4 寄存器
寄存器(Register)是CPU内部的高速存储单元,用于临时保存操作数和运算结果。寄存器是CPU和内存之间数据传输的桥梁——CPU要处理的数据,先从内存加载到寄存器,运算完成后可能再写回内存。
寄存器的速度非常快,因为它们就在CPU内部,与CPU以相同的时钟频率工作。一个时钟周期内可以完成多次寄存器读写。
寄存器的分类:
1. 通用寄存器(General Purpose Register, GPR) 用于存储操作数、运算结果、地址等,编程人员可以自由使用。
以x86-64架构为例,有16个64位通用寄存器: - rax, rbx, rcx, rdx:常用于特定用途(rax用于返回值,rcx用于计数等) - rsi, rdi:用于字符串操作(源索引、目的索引) - rbp, rsp:栈帧指针(基址指针、栈指针) - r8-r15:通用用途
2. 程序计数器(Program Counter, PC) 也称为指令指针(Instruction Pointer, IP),保存下一条要执行的指令地址。执行完一条指令后,PC自动更新为下一条指令的地址(除非是跳转指令)。
3. 指令寄存器(Instruction Register, IR) 存储当前正在执行的指令。指令从内存取出后,首先存入IR,然后由控制单元解码。
4. 状态寄存器(Status Register) 也称为标志寄存器(Flag Register)或程序状态字(Program Status Word, PSW)。存储ALU产生各种状态标志,如零标志、进位标志、溢出标志等,以及中断使能等控制位。
5. 栈指针(Stack Pointer, SP) 指向当前栈的顶部。栈是一种重要的数据结构,用于函数调用、局部变量存储等。
6. 基址指针(Base Pointer, BP) 也称为帧指针(Frame Pointer),用于访问函数的参数和局部变量。
2.2.5 指令的基本格式
指令是CPU执行任务的基本单位。每条指令都告诉CPU要做什么操作,以及操作的数据在哪里。
典型的指令格式包括:
┌─────────┬─────────┬─────────┐
│ 操作码 │ 操作数1 │ 操作数2 │
│ Opcode │ Operand1│ Operand2│
└─────────┴─────────┴─────────┘
操作码(Opcode)指明要执行的操作类型,如加法、减法、跳转等。
操作数(Operand)指明操作的数据在哪里。操作数可以是: - 立即数(Immediate):指令中直接给出的常量值 - 寄存器:操作数在某个寄存器中 - 内存地址:操作数在内存中某个位置
指令长度: - 在RISC架构(如ARM)中,所有指令长度相同(如32位) - 在CISC架构(如x86)中,指令长度可变(1到15字节不等)
x86指令示例:
- mov eax, 5:将立即数5传送到寄存器eax
- add ebx, ecx:将ebx和ecx相加,结果存入ebx
- cmp eax, 10:比较eax和10,设置标志位
- jne loop:如果不相等(Zero Flag为0),跳转到loop标签
2.2.6 指令执行详解
让我们通过一个具体的例子,详细了解指令执行的完整过程。
假设我们要执行:add eax, ebx(将eax和ebx的值相加,结果存入eax)
这条指令的功能是:\(eax = eax + ebx\)
第一步:取指(Fetch) 1. 控制单元将程序计数器(PC)的值送到地址总线 2. 内存根据地址返回指令数据到数据总线 3. 指令数据存入指令寄存器(IR) 4. PC更新,指向下一条指令的地址
第二步:解码(Decode) 1. 控制单元分析IR中的操作码,确定这是一条加法指令 2. 分析操作数:源操作数是ebx和eax(隐含),目的操作数是eax 3. 控制单元发出控制信号,准备从寄存器读取ebx和eax的值
第三步:读取操作数(Read Operands) 1. 从寄存器文件(Register File)读取ebx的值 2. 从寄存器文件读取eax的值 3. 将这两个值送到ALU的输入端
第四步:执行(Execute) 1. 控制单元向ALU发出"加法"控制信号 2. ALU执行加法运算 3. ALU产生结果:eax + ebx 4. ALU同时产生各种标志位(零标志、进位标志等)
第五步:写回(Write Back) 1. 控制单元将ALU的结果写回eax寄存器 2. 更新状态寄存器中的标志位
第六步:更新PC 1. 如果不是跳转指令,PC已经指向下一条指令 2. 如果是跳转指令,且跳转条件满足,PC被设置为目标地址
这个过程看起来很复杂,但现代CPU可以在一个时钟周期内完成多个步骤,或者通过流水线技术并行处理不同指令的不同阶段。
本节小结
- CPU是计算机的大脑,由ALU、控制单元和寄存器组成
- ALU负责算术运算和逻辑运算,是CPU的"计算器"
- 控制单元负责指令的获取、解码和执行控制
- 寄存器是CPU内部的高速存储单元,用于临时保存数据
- 指令执行的基本流程是:取指→解码→读操作数→执行→写回
- 通用寄存器可用于存储任意数据,专用寄存器有特定用途
2.3 内存层次结构
2.3.1 为什么要分层
让我们先思考一个问题:既然CPU需要从内存读写数据,为什么不把内存做得又快又大,满足所有需求呢?
答案在于一个基本的物理和经济规律:速度、容量和成本是一个不可能三角(Impossible Triangle)。
- 速度更快的存储技术,通常制造成本更高,消耗更多能量,体积也更大
- 容量更大的存储技术,通常速度更慢
举例来说: - CPU内部的寄存器,访问延迟只有不到1纳秒(ns),但容量只有几十到几百个字节 - SRAM(静态随机存取存储器,用作CPU缓存),延迟约2-5纳秒,每MB成本很高 - DRAM(动态随机存取存储器,用作内存),延迟约50-100纳秒,成本适中 - SSD(固态硬盘),延迟约100微秒,容量可以很大,成本较低 - 机械硬盘,延迟约10毫秒,容量最大,成本最低
如果我们只用最快的存储技术,1TB容量的"寄存器"将贵得离谱。如果我们只用最便宜的存储技术,计算机将慢得无法使用。
因此,现代计算机采用内存层次结构(Memory Hierarchy),将不同类型的存储设备组合起来,兼顾速度、容量和成本。
2.3.2 内存层次结构图
内存层次结构从上到下,大致分为以下几个层次:
┌─────────────────────────────────────┐
│ CPU核心 │
│ ┌───────────────────────────────┐ │
│ │ 寄存器 (Registers) │ │ ← 极快,几十字节,CPU内部
│ └───────────────────────────────┘ │
│ ↑↓ │
│ ┌───────────────────────────────┐ │
│ │ L1 Cache (一级缓存) │ │ ← 很快,几十KB,与CPU同封装
│ └───────────────────────────────┘ │
│ ↑↓ │
│ ┌───────────────────────────────┐ │
│ │ L2 Cache (二级缓存) │ │ ← 较快,几百KB
│ └───────────────────────────────┘ │
│ ↑↓ │
│ ┌───────────────────────────────┐ │
│ │ L3 Cache (三级缓存) │ │ ← 较快,几MB,共享缓存
│ └───────────────────────────────┘ │
│ ↑↓ │
│ ┌───────────────────────────────┐ │
│ │ 主存 (Main Memory) │ │ ← 中等,几GB到几十GB
│ └───────────────────────────────┘ │
│ ↑↓ │
│ ┌───────────────────────────────┐ │
│ │ 本地存储 (Local Storage) │ │ ← 慢,SSD/HDD,GB到TB
│ └───────────────────────────────┘ │
│ ↑↓ │
│ ┌───────────────────────────────┐ │
│ │ 远程存储 (Remote Storage) │ │ ← 最慢,网络存储
│ └───────────────────────────────┘ │
└─────────────────────────────────────┘
每一层都服务于其上一层:当上一层需要数据时,下一层负责提供;当上一层有数据要保存时,可能需要写到下一层。
2.3.3 寄存器(Registers)
寄存器是内存层次结构中最顶层的存储设备,完全位于CPU内部。
寄存器的特点: - 极快的访问速度:访问延迟小于1纳秒,通常一个时钟周期就可以完成读写 - 极小的容量:桌面处理器通常有十几个到几十个通用寄存器,每个64位(8字节),总共也就几百字节 - 直接参与运算:ALU的输入直接来自寄存器,运算结果也直接写回寄存器
寄存器的类型: - 通用寄存器:存储任意数据 - 专用寄存器:如程序计数器(PC)、栈指针(SP)、基址指针(BP) - 状态寄存器:存储各种标志位
寄存器是CPU与内存之间数据传输的最终点。CPU要处理的数据,必须加载到寄存器中;计算结果,也最终保存在寄存器中(或者写回内存)。
2.3.4 高速缓存(Cache)
高速缓存是介于CPU寄存器和主存之间的存储层次,用于减少CPU访问主存的次数,从而缓解冯诺依曼瓶颈。
为什么需要Cache:
程序运行具有局部性(Locality): - 时间局部性(Temporal Locality):如果某个数据被访问过,那么近期它很可能再次被访问。例如,循环变量在循环执行期间会被反复访问。 - 空间局部性(Spatial Locality):如果某个数据被访问过,那么与它相邻的数据很可能也会被访问。例如,遍历数组时,会依次访问相邻的数组元素。
由于局部性的存在,我们可以把最近访问的数据(以及相邻的数据)复制到Cache中。如果CPU要访问的数据已经在Cache中(缓存命中,Cache Hit),就可以快速获取;如果不在(缓存未命中,Cache Miss),则需要从主存读取,同时把该数据及其相邻数据块一起加载到Cache中。
Cache的结构:
Cache的容量比主存小得多,但速度比主存快很多。以L1 Cache为例: - 容量:通常32KB到128KB - 延迟:约1-5纳秒 - 访问周期:约4-10个时钟周期
Cache的基本单元是缓存行(Cache Line),也称为块(Block)。每条缓存行包含: - 数据块(Data Block):存储从主存复制过来的数据,通常是几十个字节(如64字节) - 标签(Tag):主存地址的一部分,用于标识这个缓存行对应哪个主存地址 - 有效位(Valid Bit):标识这个缓存行是否包含有效数据
缓存映射方式:
由于Cache容量远小于主存,需要一种机制来确定主存中的某个地址的数据现在位于Cache的哪个位置。常见的映射方式有:
- 直接映射(Direct-Mapped):每个主存地址只能映射到Cache中的一个特定位置
- 优点:查找简单,速度快
-
缺点:容易发生冲突
-
组相联(Set-Associative):每个主存地址可以映射到Cache中的一组位置,查找时在这一组中遍历
- 例如,4路组相联:每个地址映射到4个可能的Cache位置
- 优点:减少冲突,提高命中率
-
缺点:查找稍微复杂
-
全相联(Fully-Associative):每个主存地址可以放在Cache中的任意位置
- 优点:最灵活,冲突最少
- 缺点:需要并行比较所有Cache行的标签,成本高,只适合小容量Cache(如TLB)
缓存的读写策略:
读操作: 1. CPU发出要读取的内存地址 2. Cache控制器检查该地址是否在Cache中(通过标签比较) 3. 如果命中,直接从Cache返回数据给CPU 4. 如果未命中,从主存读取数据,同时: - 将数据返回给CPU - 将数据存入某个Cache行(可能需要替换已有的数据)
写操作: 1. 写直达(Write-Through):同时写Cache和主存 - 优点:主存数据始终有效,一致性好 - 缺点:每次写操作都要访问主存,速度慢
- 写回(Write-Back):只写Cache,只有当该Cache行被替换时,才写回主存
- 优点:减少对主存的写操作,提高性能
- 缺点:需要额外的"脏位"(Dirty Bit)来标识哪些行被修改过
2.3.5 多级缓存
现代CPU通常有多个级别的缓存:
L1 Cache(一级缓存): - 最接近CPU核心,速度最快 - 容量最小,通常32KB到128KB - 通常分为指令缓存(I-Cache)和数据缓存(D-Cache) - 延迟:约1-2纳秒
L2 Cache(二级缓存): - 比L1慢一些,容量更大 - 可以是每个核心私有,也可以是共享的 - 容量通常256KB到几MB - 延迟:约3-10纳秒
L3 Cache(三级缓存): - 比L2更慢一些,容量更大 - 通常是所有核心共享的 - 容量可以达几十MB - 延迟:约10-20纳秒
缓存层级的工作方式: - 当CPU要访问内存时,首先检查L1 Cache - 如果L1未命中,检查L2 Cache - 如果L2未命中,检查L3 Cache - 如果L3也未命中,则访问主存
这种逐级查找的方式,对于具有良好局部性的程序,可以大大减少对主存的访问次数。
2.3.6 主存(Main Memory)
主存通常指DRAM(Dynamic Random Access Memory,动态随机存取存储器),是我们通常所说的"内存"。
DRAM的工作原理:
DRAM使用电容来存储每一位数据。电容充电时表示1,放电时表示0。但电容会随着时间慢慢放电(漏电),所以需要周期性刷新(Refresh)来保持数据,这也是"动态"一词的由来。
DRAM的基本单元(一个bit)由一个电容和一个晶体管组成,结构简单,可以做得非常密集。
DRAM的访问:
CPU不能直接访问DRAM。需要通过内存控制器(Memory Controller)来访问。
内存控制器是CPU和DRAM之间的桥梁,负责: - 接收CPU的内存访问请求 - 转换地址格式 - 刷新DRAM - 处理数据传输
在早期,内存控制器集成在北桥芯片中。现在,为了降低延迟,内存控制器通常集成在CPU内部。
DRAM的时序:
DRAM的访问涉及多个步骤,需要精确的时序控制。主要参数包括: - CAS延迟(CL):列地址选通延迟,即从发出读命令到数据可用的时间 - tRCD:行地址到列地址延迟 - tRP:行预充电时间
DDR SDRAM:
DDR(Double Data Rate)是一种内存技术,在时钟的上升沿和下降沿都传输数据,所以数据传输率是时钟频率的两倍。
DDR技术经历了多代发展: - DDR1:2000年,传输频率200-400MHz - DDR2:2003年,传输频率400-800MHz - DDR3:2007年,传输频率800-2133MHz - DDR4:2014年,传输频率2133-3200MHz - DDR5:2020年,传输频率4800-6400MHz
2.3.7 虚拟内存
虚拟内存(Virtual Memory)是操作系统提供的一种内存管理技术,它让程序认为它有一大片连续的地址空间(虚拟地址空间),而实际的物理内存可能远小于这个地址空间。
为什么需要虚拟内存:
-
程序可以使用连续的地址:没有虚拟内存时,程序需要自己管理物理内存的分配和地址,容易出现碎片化和地址冲突。有了虚拟内存,操作系统为每个程序提供连续的虚拟地址空间。
-
程序间相互隔离:有了虚拟内存,不同程序的虚拟地址可以映射到不同的物理地址,实现程序间的内存隔离,提高安全性和稳定性。
-
物理内存可以小于虚拟地址空间:通过swapping(换页)技术,不常用的内存页可以暂时写入磁盘,等需要时再换回来。这让程序可以使用比物理内存更大的地址空间。
-
内存利用率更高:操作系统可以根据需要动态分配物理内存,减少浪费。
虚拟地址到物理地址的转换:
虚拟地址到物理地址的转换由内存管理单元(Memory Management Unit, MMU)完成。
MMU通过页表(Page Table)来存储虚拟地址到物理地址的映射关系。每个程序有自己的页表。
分页(Paging)是常见的虚拟内存管理方式: - 虚拟地址空间被划分为固定大小的页(Page) - 物理内存被划分为相同大小的页框(Page Frame) - 页表记录每个虚拟页对应的物理页框
虚拟地址结构:
┌──────────────┬─────────────┐
│ 页号 │ 页内偏移 │
│ (VPN) │ (Offset) │
└──────────────┴─────────────┘
↑ ↑
└──── 虚拟页号 ──┘
物理地址结构:
┌──────────────┬─────────────┐
│ 页框号 │ 页内偏移 │
│ (PFN) │ (Offset) │
└──────────────┴─────────────┘
转换时,MMU根据虚拟地址的页号查页表,得到对应的物理页框号,再加上页内偏移,就得到了物理地址。
TLB(Translation Lookaside Buffer):
每次地址转换都要查页表,而页表可能很大,导致多次内存访问。为了加速地址转换,CPU引入了TLB,这是一个专门缓存最近使用的虚拟页号到物理页框号的映射的高速缓存。
TLB的工作方式: - CPU给出虚拟地址 - MMU首先查TLB,看是否有该虚拟地址的映射 - 如果命中(TLB Hit),直接获得物理地址 - 如果未命中(TLB Miss),查页表,获得映射,同时更新TLB
2.3.8 局部性原理详解
局部性原理(Principle of Locality)是缓存技术和虚拟内存技术的理论基础。它表明程序在执行过程中,对内存的访问呈现出高度局部化的特征。
时间局部性(Temporal Locality):
同一个内存位置,在短期内可能被多次访问。
典型例子: - 循环中的循环变量,每次迭代都要检查和更新 - 函数的返回地址,调用后很快就会用到 - 常用的配置数据
// 时间局部性示例
int sum = 0;
for (int i = 0; i < N; i++) {
sum += arr[i]; // i在每次迭代都被访问
}
空间局部性(Spatial Locality):
当某个内存位置被访问时,相邻的内存位置很可能也会被访问。
典型例子: - 数组元素依次访问 - 顺序执行的代码(指令在内存中是连续存放的) - 结构体的成员(通常连续存放在内存中)
// 空间局部性示例
for (int i = 0; i < N; i++) {
sum += arr[i]; // arr[i+1] 在 arr[i] 之后被访问
}
评估局部性:
了解局部性原理对于编写高效代码很重要: - 数组应该尽量顺序访问,避免跳跃式访问 - 嵌套循环应该尽量让内层循环访问的数据连续 - 频繁访问的数据应该尽量保持在缓存中
本节小结
- 内存层次结构是为了解决速度、容量和成本之间的矛盾
- 寄存器是速度最快、容量最小的存储,位于CPU内部
- Cache利用局部性原理,减少对主存的访问,是CPU和主存之间的缓冲区
- 多级缓存(L1/L2/L3)提供不同速度和容量的缓存层次
- 主存(DRAM)容量较大但速度较慢,需要内存控制器访问
- 虚拟内存让程序使用连续的虚拟地址空间,由MMU通过页表转换
- 局部性原理包括时间局部性和空间局部性,是缓存技术的理论基础
2.4 CPU与内存交互
2.4.1 总线概述
总线(Bus)是计算机中各部件传输数据的共享通道。就像城市里的主干道,总线是连接CPU、内存、I/O设备的"高速公路"。
总线的特点: - 共享性:多个设备可以连接到同一条总线,但同一时刻只能有一个设备使用 - 方向性:数据总线是双向的(可以发送也可以接收),地址总线通常是单向的(由CPU发出) - 速度匹配:总线速度必须满足连接设备中速度最慢的那个
总线的分类:
- 系统总线(System Bus):连接CPU和内存,包括:
- 数据总线(Data Bus):传输数据,宽度(如64位)影响一次传输的数据量
- 地址总线(Address Bus):传输内存地址,宽度(如32位、64位)决定可寻址空间大小
-
控制总线(Control Bus):传输控制信号,如读/写信号、时钟信号、中断信号
-
I/O总线:连接CPU和外部设备,如PCIe、USB、SATA等
-
外部总线:连接不同设备,如以太网、串口等
2.4.2 数据传输过程
当CPU需要从内存读取数据时,数据传输过程如下:
读操作: 1. CPU将内存地址放到地址总线上 2. CPU发出读控制信号(RD) 3. 内存控制器检测到地址和控制信号,从指定地址读取数据 4. 内存将数据放到数据总线上 5. CPU从数据总线读取数据并存入寄存器
写操作: 1. CPU将内存地址放到地址总线上 2. CPU将数据放到数据总线上 3. CPU发出写控制信号(WR) 4. 内存控制器检测到地址、控制信号和数据,将数据写入指定地址
总线的带宽:
总线带宽是指单位时间内可以传输的数据量,计算公式是:
例如,一个64位(8字节)宽度的总线,工作频率为100MHz,则理论带宽是:
2.4.3 直接内存访问(DMA)
DMA(Direct Memory Access,直接内存访问)是一种允许外部设备直接与内存交换数据的技术,无需CPU介入。
为什么需要DMA:
在没有DMA的时代,外设与内存交换数据需要CPU"居中协调": - 外设数据先读到CPU寄存器 - 再从CPU寄存器写到内存
这称为程序控制I/O(Programmed I/O)。问题是,如果传输大量数据(如从硬盘读取文件),CPU就会浪费大量时间在简单的数据搬运上,无法执行其他任务。
DMA的工作原理:
- CPU配置DMA控制器:
- 设置源地址(数据从哪里来)
- 设置目标地址(数据到哪里去)
- 设置传输的数据量
-
启动DMA传输
-
DMA控制器接管总线,开始传输数据
- 数据从外设直接传输到内存(或反之)
-
CPU此时可以执行其他任务
-
传输完成后,DMA控制器向CPU发出中断信号
- CPU知道数据传输已完成
- CPU可以处理这些数据
DMA的优势: - CPU可以在数据传输期间执行其他计算任务 - 提高系统整体效率 - 减少CPU负载
DMA的应用场景: - 磁盘I/O:硬盘读写 - 网络数据传输:网卡收发的数据包 - 音频/视频播放:音频/视频数据流 - 显卡显存更新:GPU与显存之间的数据交换
2.4.4 缓存一致性
在具有缓存的系统中,一个重要的问题是缓存一致性(Cache Coherence)。
问题场景: - CPU和DMA控制器都访问同一块内存 - CPU可能已经把某个内存位置的数据加载到缓存 - 如果DMA直接修改了主存中的数据,而CPU还在使用旧缓存中的数据,就会产生不一致
缓存一致性的解决方案:
- 缓存直写(Write-Through):每次写操作同时更新缓存和主存
- 主存数据始终是最新的
-
但会降低写性能
-
缓存失效(Cache Invalidation):当DMA要访问某块内存时,通知CPU使相关缓存行失效
- CPU下次访问时必须从主存重新读取
-
需要硬件支持(如嗅探协议)
-
缓存同调(Cache Coherent)协议:如MESI协议,确保缓存和主存的数据一致
- MESI:Modified(修改)、Exclusive(独占)、Shared(共享)、Invalid(无效)
- 当某个CPU修改数据时,其他CPU的相关缓存行会被无效或更新
2.4.5 内存保护
操作系统需要保护内存,防止恶意或错误的程序访问不该访问的内存区域。
内存保护机制:
- 特权级别(Privilege Level):
- CPU支持多个特权级别(如Ring 0到Ring 3)
- 操作系统运行在最高特权级(Ring 0)
- 用户程序运行在较低特权级(Ring 3)
-
特权指令只能在高特权级执行
-
段保护(Segment Protection):
- 每个内存段有访问权限(如只读、读写、执行)
-
尝试违反权限的操作会触发异常
-
页保护(Page Protection):
- 每个内存页有访问权限
- 页表项中包含读/写/执行权限位
- 尝试违反权限的操作会触发页错误(Page Fault)
2.4.6 中断机制
中断(Interrupt)是计算机中一种重要的异步通信机制,允许外部设备通知CPU需要处理的事件。
中断的类型:
- 硬件中断(Hardware Interrupt):
- 由外部设备发出,如键盘按键、鼠标移动、定时器到期、网络数据包到达
-
通过中断控制器(如Intel的8259或现代的APIC)路由到CPU
-
软件中断(Software Interrupt):
- 由程序执行特定指令触发
-
如x86的
int 0x80(系统调用) -
异常(Exception):
- 由CPU在执行指令时检测到错误情况产生
- 如除零、非法指令、页错误
中断的处理过程:
- 外设发出中断请求(IRQ)
- 中断控制器收集并优先级排序所有IRQ
- 中断控制器向CPU发送中断信号
- CPU完成当前指令(取决于中断类型)
- CPU保存当前执行上下文(寄存器、PC等)到栈
- CPU根据中断号查找中断向量表,获得中断处理程序地址
- CPU跳转到中断处理程序执行
- 中断处理完成后,恢复保存的上下文,继续执行被中断的代码
2.4.7 时钟和同步
时钟(Clock)是计算机系统的时间基准,用于同步各部件的工作。
时钟的作用: - 为CPU提供基本的时序参考 - 决定CPU的执行频率 - 同步总线和数据传输时序 - 为定时器提供基准
同步与异步: - 同步系统:所有部件使用统一的时钟信号协同工作 - 异步系统:部件之间通过握手信号协调,不需要统一时钟
现代计算机通常是同步的(有全局时钟),但某些总线和数据传输可能是异步的(通过握手协议协调)。
本节小结
- 总线是连接各部件的数据通道,分为系统总线、I/O总线等
- 数据传输涉及地址、数据和控制信号的交互
- DMA允许外设直接与内存交换数据,减轻CPU负担
- 缓存一致性确保多级缓存和主存的数据一致
- 内存保护通过特权级别、段保护和页保护实现
- 中断是外设通知CPU的异步机制,用于处理紧急事件
- 时钟为计算机系统提供统一的时间基准
本章小结
本章深入介绍了计算机的核心组成——CPU和内存的工作原理。
冯诺依曼架构是现代计算机的基础,由运算器、控制器、存储器、输入设备和输出设备五部分组成。"存储程序"概念允许程序像数据一样被修改和执行。
CPU的结构包括: - ALU负责算术和逻辑运算 - 控制单元负责指令的取指、解码和执行控制 - 寄存器用于快速存储数据,是CPU内部最快速的存储
指令执行流程是"取指-解码-读取操作数-执行-写回"的循环,CPU通过程序计数器来跟踪下一条要执行的指令。
内存层次结构解决了速度、容量和成本的矛盾: - 寄存器速度最快但容量最小 - 多级缓存利用局部性原理加速数据访问 - 主存(DRAM)提供大容量存储 - 虚拟内存通过页表和MMU实现地址转换和内存保护
CPU与内存交互通过总线进行,DMA允许外设直接访问内存而不需要CPU介入。缓存一致性、内存保护、中断机制等都是保证系统正确运行的重要机制。
理解CPU和内存的工作原理,对于理解计算机系统的行为和编写高效程序都至关重要。
思考与练习
- 冯诺依曼架构
- 画出冯诺依曼架构的框图,标出五大组成部分及其关系。
-
解释什么是"冯诺依曼瓶颈",并列举两种缓解该瓶颈的技术。
-
CPU结构
- 描述CPU中ALU和控制单元的功能,并说明它们如何协同工作。
-
为什么说寄存器是内存层次结构中最快的一层?
-
指令执行
- 详细描述执行
mov eax, [0x1000]这条指令(从内存读取数据到寄存器)的完整过程。 -
程序计数器(PC)的作用是什么?什么情况下PC的值会改变?
-
内存层次
- 解释时间局部性和空间局部性的含义,并各举一个程序中的例子。
- 为什么L1 Cache通常分为指令缓存和数据缓存两个部分?
-
如果L1 Cache未命中,CPU接下来会检查哪里?
-
虚拟内存
- 虚拟内存解决了哪些问题?请列出至少三个。
-
假设一个系统使用32位虚拟地址,页大小为4KB。请计算虚拟地址空间的大小,以及虚拟页号占多少位。
-
总线与DMA
- 解释总线带宽的概念。如果总线宽度为64位,频率为100MHz,理论带宽是多少?
-
DMA相对于程序控制I/O有什么优势?请解释DMA的工作流程。
-
缓存一致性
-
考虑这个场景:CPU A和CPU B共享一块内存,CPU A修改了该内存中的数据,但CPU B不知道修改发生,仍然使用旧数据。这种问题应该怎么解决?
-
中断机制
- 什么是中断?中断在计算机系统中有什么作用?
- 描述CPU处理一个硬件中断的完整过程。