前置知识: 入门指南

计算机体系结构

9 minBeginner2026/6/14

冯·诺依曼体系结构、CPU工作原理、指令周期、存储层次与总线系统。

1. 冯·诺依曼体系结构

现代计算机的理论基础来自1945年约翰·冯·诺依曼提出的”存储程序”思想。其核心观点是:程序和数据以同等地位存储在同一存储器中,由控制器按顺序从存储器中取出指令并执行

1.1 五大组成部分

冯·诺依曼体系结构将计算机划分为五个功能部件:

部件功能
控制器从存储器取指令、译码、控制各部件协调工作乐队指挥
运算器执行算术运算和逻辑运算计算器
存储器存放程序和数据书架
输入设备将外部信息转换为计算机能识别的数据眼睛/耳朵
输出设备将计算结果转换为人可感知的形式嘴巴/手

控制器和运算器合称为中央处理器(CPU)

1.2 存储程序原理

存储程序是冯·诺依曼体系结构最核心的思想:

  1. 程序和数据以二进制形式存放在存储器中
  2. 计算机运行时,从存储器中依次取出指令并执行
  3. 指令的执行顺序可以通过跳转指令改变
  4. 存储器中的内容既可以被读取也可以被修改
┌─────────────────────────────────────────────┐
│                 存储器                       │
│  ┌──────┬──────┬──────┬──────┬──────┐       │
│  │指令1 │指令2 │指令3 │数据1 │数据2 │  ...  │
│  └──────┴──────┴──────┴──────┴──────┘       │
└──────────┬──────────────────┬───────────────┘
           │ 取指令           │ 读/写数据
           ▼                  ▼
┌─────────────────┐  ┌─────────────────┐
│     控制器       │  │     运算器       │
│  ┌───────────┐  │  │  ┌───────────┐  │
│  │ 指令寄存器 │  │  │  │  累加器   │  │
│  │ 程序计数器│  │  │  │  ALU      │  │
│  └───────────┘  │  │  └───────────┘  │
└─────────────────┘  └─────────────────┘

1.3 冯·诺依曼瓶颈

由于指令和数据共享同一条总线,CPU与存储器之间的数据传输速率成为系统性能的瓶颈,这被称为冯·诺依曼瓶颈。现代计算机通过缓存、流水线等技术缓解此问题。

2. CPU 工作原理

CPU是计算机的”大脑”,由控制器和运算器两部分组成。

2.1 控制器

控制器负责协调计算机各部件的工作,主要包含以下寄存器:

寄存器作用
PC(程序计数器)存放下一条要执行的指令地址
IR(指令寄存器)存放当前正在执行的指令
MAR(地址寄存器)存放要访问的存储器地址
MDR(数据寄存器)存放从存储器读出或要写入的数据

2.2 运算器

运算器(ALU,算术逻辑单元)执行具体的计算:

  • 算术运算:加、减、乘、除
  • 逻辑运算:与、或、非、异或
  • 移位操作:左移、右移
  • 比较操作:等于、大于、小于
// ALU 的简化模型
int ALU(int operand1, int operand2, int opcode) {
    switch (opcode) {
        case ADD: return operand1 + operand2;
        case SUB: return operand1 - operand2;
        case AND: return operand1 & operand2;
        case OR:  return operand1 | operand2;
        case XOR: return operand1 ^ operand2;
        case SHL: return operand1 << operand2;
        case SHR: return operand1 >> operand2;
        default:  return 0;
    }
}

2.3 CPU 内部结构示意

          ┌──────────────────────────────────┐
          │              CPU                  │
          │                                   │
          │  ┌──────────┐   ┌──────────┐     │
          │  │  控制器   │   │  运算器   │     │
          │  │          │   │          │     │
          │  │ PC  IR   │◄──│ ALU      │     │
          │  │ MAR MDR  │──►│ 累加器   │     │
          │  │ 时序发生器│   │ 标志寄存器│     │
          │  └──────────┘   └──────────┘     │
          │        │              │           │
          │        └──────┬───────┘           │
          │               │ 内部总线          │
          └───────────────┼──────────────────┘

                    外部总线

                  ┌───────┴───────┐
                  │    存储器      │
                  └───────────────┘

3. 指令周期

CPU执行一条指令的过程称为指令周期,通常分为以下几个阶段:

3.1 取指(Fetch)

  1. 将 PC 中的地址送入 MAR
  2. 通过地址总线将 MAR 中的地址发送给存储器
  3. 存储器将对应地址的数据通过数据总线送入 MDR
  4. 将 MDR 中的指令送入 IR
  5. PC 自增,指向下一条指令

3.2 译码(Decode)

  1. 分析 IR 中指令的操作码
  2. 确定要执行的操作
  3. 识别操作数地址

3.3 执行(Execute)

  1. 根据译码结果执行相应操作
  2. 可能需要从存储器读取操作数
  3. ALU 执行计算
  4. 将结果写回寄存器或存储器

3.4 指令周期流程

取指 ──► 译码 ──► 执行 ──► 写回
  │                              │
  └──────────────────────────────┘
           重复执行

3.5 用代码理解指令周期

// 模拟简化的指令周期
void cpu_run() {
    while (running) {
        // 取指:从存储器取出PC指向的指令
        int instruction = memory[PC];

        // 译码:分离操作码和操作数
        int opcode  = (instruction >> 12) & 0xF;  // 高4位为操作码
        int operand = instruction & 0xFFF;         // 低12位为操作数

        // 执行:根据操作码执行对应操作
        switch (opcode) {
            case LOAD:  // 加载数据到累加器
                accumulator = memory[operand];
                break;
            case STORE: // 将累加器数据存入存储器
                memory[operand] = accumulator;
                break;
            case ADD:   // 加法
                accumulator += memory[operand];
                break;
            case SUB:   // 减法
                accumulator -= memory[operand];
                break;
            case JUMP:  // 无条件跳转
                PC = operand;
                continue;  // 跳过PC自增
            case HALT:  // 停机
                running = 0;
                break;
        }

        PC++;  // 程序计数器自增
    }
}

3.6 时钟周期与指令周期

概念定义关系
时钟周期CPU 时钟脉冲的一个周期,最小时间单位基本单位
机器周期完成一个基本操作所需的时间通常 = 若干时钟周期
指令周期执行一条指令所需的时间= 若干机器周期

现代CPU通过流水线技术让多条指令的不同阶段重叠执行,大幅提升吞吐量。例如五级流水线:取指→译码→执行→访存→写回,五条指令可以同时在不同阶段运行。

4. 存储层次

计算机的存储系统按照速度、容量和价格形成层次结构,从快到慢、从小到大:

4.1 存储层次结构

  速度 ↑    ┌──────────────┐
           │   CPU寄存器   │  ~1ns    | ~几百B
           ├──────────────┤
           │   高速缓存    │  ~几ns   | ~几MB
           │  (L1/L2/L3)  │
           ├──────────────┤
           │    主存(内存)  │  ~100ns  | ~几GB~几TB
           ├──────────────┤
           │   辅存(外存)   │  ~ms级   | ~几TB~几PB
           │  SSD / HDD   │
           └──────────────┘
  容量 ↑

4.2 各级存储详细对比

层次典型容量访问时间是否易失典型用途
寄存器几十~几百B< 1ns指令执行中的临时数据
L1 缓存32~64 KB1~2 ns最频繁使用的数据
L2 缓存256 KB~1 MB3~10 ns较频繁使用的数据
L3 缓存4~64 MB10~30 ns多核共享数据
主存(DRAM)8~128 GB50~100 ns运行中的程序和数据
SSD256 GB~4 TB0.1~0.5 ms持久化存储
HDD1~20 TB5~15 ms大容量归档存储

4.3 缓存工作原理

缓存利用了局部性原理

  • 时间局部性:最近被访问的数据,很可能在不久后再次被访问
  • 空间局部性:被访问数据附近的数据,很可能也会被访问
// 缓存命中与未命中的概念
int data[1000];

// 时间局部性:sum 被反复读写
int sum = 0;
for (int i = 0; i < 1000; i++) {
    sum += data[i];  // sum 会被缓存
}

// 空间局部性:顺序访问数组
// data[0] 被访问后,data[1], data[2]... 也会被预取到缓存
for (int i = 0; i < 1000; i++) {
    process(data[i]);
}

4.4 缓存命中率

缓存命中率是衡量缓存效率的关键指标:

命中率 = 缓存命中次数 / 总访问次数 × 100%
场景典型命中率说明
L1 缓存90%~95%大多数指令和数据在L1命中
L2 缓存80%~90%L1未命中的大部分在L2命中
L3 缓存70%~85%L2未命中的大部分在L3命中

缓存每提升1%的命中率,程序性能可能有数个百分点的提升。编写缓存友好的代码是性能优化的重要手段。

5. 总线系统

总线是计算机各部件之间传送信息的公共通道。

5.1 总线分

按功能划分,总线分为三

总线传输内容方向宽度
地址总线内存地址/IO端口地址CPU→存储器/IO决定寻址空间
数据总线数据双向决定一次传输量
控制总线控制信号双向读写/中断等

5.2 总线工作示例:读内存

CPU                          存储器
 │                              │
 │  ── 地址(0x1000) ──────────► │  1. CPU通过地址总线发送地址
 │                              │
 │  ── 读信号 ────────────────► │  2. CPU通过控制总线发送读信号
 │                              │
 │  ◄── 数据(0x5A) ─────────── │  3. 存储器通过数据总线返回数据
 │                              │

5.3 地址总线与寻址空间

地址总线的宽度决定了CPU能直接寻址的内存空间大小:

地址总线宽度寻址空间计算
16 位64 KB2^16 = 65,536 B
20 位1 MB2^20 = 1,048,576 B
32 位4 GB2^32 ≈ 4.3×10^9 B
64 位16 EB2^64 ≈ 1.8×10^19 B
// 32位系统的寻址空间计算
// 地址总线32根,每根线0或1
// 可表示地址数 = 2^32 = 4,294,967,296
// 每个地址对应1字节
// 总寻址空间 = 4,294,967,296 字节 = 4 GB

#include <stdio.h>
int main() {
    // 32位指针的大小
    printf("指针大小: %zu 字节\n", sizeof(void*));  // 4 (32位) 或 8 (64位)

    // 理论寻址空间
    unsigned long long addr_space = 1ULL << 32;
    printf("32位寻址空间: %llu 字节 = %llu GB\n",
           addr_space, addr_space / (1024*1024*1024));
    return 0;
}

5.4 数据总线与传输效率

数据总线的宽度决定了CPU一次能传输的数据量:

数据总线宽度一次传输说明
8 位1 字节早期8位机
16 位2 字节8086等16位机
32 位4 字节80386等32位机
64 位8 字节现代64位处理器

5.5 总线仲裁

当多个设备同时请求使用总线时,需要通过总线仲裁决定优先级:

  • 链式查询:设备串行连接,离仲裁器越近优先级越高
  • 计数器定时:从某个起始地址开始计数,被计数的设备获得总线
  • 独立请求:每个设备有独立的请求线和授权线,响应最快

6. 哈佛架构与冯·诺依曼架构对比

特性冯·诺依曼架构哈佛架构
指令与数据共用存储器和总线分开存储,独立总线
总线数量1套2套
取指与取数据不能同时进行可以同时进行
实现复杂度较低较高
典型应用通用计算机DSP、嵌入式、ARM

现代CPU通常采用改进型哈佛架构:在CPU内部(L1缓存层)使用哈佛架构(指令缓存和数据缓存分离),在外部使用冯·诺依曼架构(统一主存),兼顾两者优势。

7. 小结

概念要点
冯·诺依曼架构五大部件、存储程序原理、指令数据共享总线
CPU控制器+运算器,通过寄存器暂存中间结果
指令周期取指→译码→执行→写回,流水线提升吞吐量
存储层次寄存器→缓存→内存→外存,速度递减容量递增
总线系统地址总线定寻址空间,数据总线定传输宽度

理解计算机体系结构是学习操作系统、编译原理和性能优化的基础。后续章节将在此基础上深入探讨数据表示和程序设计。