前置知识: 计算机基础

计算机体系结构基础

7 min入门

从零理解计算机体系结构:冯·诺依曼模型、CPU 工作原理、指令周期、存储层次与总线系统(入门学习笔记)。

1. 冯·诺依曼体系结构

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

1.1 五大组成部分

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

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

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

1.2 存储程序原理

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

  1. 程序和数据以二进制形式存放在存储器中
  2. 计算机运行时,从存储器中依次取出指令并执行
  3. 指令的执行顺序可以通过跳转指令改变
  4. 存储器中的内容既可以被读取也可以被修改
flowchart TD
    subgraph Mem[存储器]<br/>指令1 指令2 指令3 数据1 数据2
    end
    Mem -->|取指令| C[控制器<br/>指令寄存器/程序计数器]
    Mem -->|读/写数据| A[运算器<br/>累加器/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 内部结构示意

flowchart TD
    subgraph CPU[CPU]
        C[控制器<br/>PC IR MAR MDR 时序发生器]
        A[运算器<br/>ALU 累加器 标志寄存器]
        C <-->|内部总线| A
    end
    CPU -->|外部总线| M[存储器]

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 指令周期流程

flowchart LR
    F[取指] --> D[译码] --> E[执行] --> W[写回]
    W -->|重复执行| F

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 存储层次结构

flowchart TD
    R[CPU 寄存器 ~1ns ~几百B 速度↑] --> C[高速缓存 L1/L2/L3 ~几ns ~几MB]
    C --> M[主存 内存 ~100ns ~几GB-几TB]
    M --> S[辅存 外存 SSD/HDD ~ms级 ~几TB-几PB 容量↑]

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 总线工作示例:读内存

sequenceDiagram
    participant CPU as CPU
    participant M as 存储器
    CPU->>M: 地址 0x1000(地址总线)
    CPU->>M: 读信号(控制总线)
    M-->>CPU: 数据 0x5A(数据总线)

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控制器+运算器,通过寄存器暂存中间结果
指令周期取指→译码→执行→写回,流水线提升吞吐量
存储层次寄存器→缓存→内存→外存,速度递减容量递增
总线系统地址总线定寻址空间,数据总线定传输宽度

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