跳转至

01-CPU架构基础

重要性:⭐⭐⭐⭐⭐ 实用度:⭐⭐⭐⭐⭐ 学习时间:2天 必须掌握:是


为什么学这一章?

CPU是计算机的核心,理解CPU架构能帮助你: - 理解程序是如何真正执行的 - 写出CPU友好的高效代码 - 理解性能瓶颈所在 - 为学习汇编语言和优化打下基础

学完这一章,你将能够: - ✅ 解释CPU的基本组成部分和工作原理 - ✅ 理解寄存器的作用和分类 - ✅ 掌握CPU执行指令的基本流程 - ✅ 了解现代CPU的核心技术


📖 核心概念

1. CPU的基本组成

Text Only
┌─────────────────────────────────────────────────────────────────────┐
│                         CPU架构全景图                                  │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │                      控制单元(CU)                            │ │
│  │  ┌─────────────────────────────────────────────────────┐     │ │
│  │  │  • 指令译码器(Instruction Decoder)                 │     │ │
│  │  │  • 控制信号生成器(Control Signal Generator)        │     │ │
│  │  │  • 程序计数器(PC)                                  │     │ │
│  │  │  • 时序发生器(Clock)                               │     │ │
│  │  └─────────────────────────────────────────────────────┘     │ │
│  │  功能:控制协调CPU各部件工作                                  │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                              ↓                                      │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │                    运算单元(ALU)                             │ │
│  │  ┌─────────────────────────────────────────────────────┐     │ │
│  │  │  • 算术运算:加、减、乘、除                          │     │ │
│  │  │  • 逻辑运算:与、或、非、异或                        │     │ │
│  │  │  • 比较运算:等于、大于、小于                        │     │ │
│  │  │  • 移位运算:左移、右移                              │     │ │
│  │  └─────────────────────────────────────────────────────┘     │ │
│  │  功能:执行算术和逻辑运算                                     │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                              ↓                                      │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │                    寄存器组(Registers)                       │ │
│  │  ┌─────────────────────────────────────────────────────┐     │ │
│  │  │  • 通用寄存器:RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP│     │ │
│  │  │  • 段寄存器:CS, DS, SS, ES, FS, GS                  │     │ │
│  │  │  • 特殊寄存器:PC(RIP), FLAGS(RFLAGS)            │     │ │
│  │  └─────────────────────────────────────────────────────┘     │ │
│  │  功能:高速数据存储                                           │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                              ↓                                      │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │                    缓存系统(Cache)                           │ │
│  │  ┌─────────────────────────────────────────────────────┐     │ │
│  │  │  • L1 Cache:32-64KB,分为指令缓存和数据缓存         │     │ │
│  │  │  • L2 Cache:256KB-512KB                            │     │ │
│  │  │  • L3 Cache:8-64MB(多核共享)                      │     │ │
│  │  └─────────────────────────────────────────────────────┘     │ │
│  │  功能:缓解CPU与内存的速度差异                                │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                              ↓                                      │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │                    内存接口(Memory Interface)                │ │
│  │  • 内存管理单元(MMU)                                        │ │
│  │  • 总线接口(Bus Interface)                                  │ │
│  │  • 预取单元(Prefetcher)                                     │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

2. x86-64寄存器详解

通用寄存器

Text Only
┌─────────────────────────────────────────────────────────────────────┐
│                    x86-64通用寄存器(64位)                            │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │  RAX(Accumulator)累加器                                      │ │
│  │  ├── 用途:函数返回值、算术运算                                │ │
│  │  ├── 32位:EAX                                               │ │
│  │  ├── 16位:AX                                                │ │
│  │  └── 8位:AH/AL                                              │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                                                                     │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │  RBX(Base)基址寄存器                                         │ │
│  │  └── 用途:数据指针(较少使用)                                │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                                                                     │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │  RCX(Counter)计数器                                          │ │
│  │  └── 用途:循环计数、字符串操作                                │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                                                                     │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │  RDX(Data)数据寄存器                                         │ │
│  │  └── 用途:I/O操作、乘除法辅助                                 │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                                                                     │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │  RSI(Source Index)源索引                                     │ │
│  │  └── 用途:字符串/数组操作的源地址                             │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                                                                     │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │  RDI(Destination Index)目的索引                              │ │
│  │  └── 用途:字符串/数组操作的目的地址                           │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                                                                     │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │  RBP(Base Pointer)基址指针                                   │ │
│  │  └── 用途:栈帧基址,访问局部变量和参数                        │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                                                                     │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │  RSP(Stack Pointer)栈指针                                    │ │
│  │  └── 用途:指向栈顶,push/pop操作                              │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                                                                     │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │  R8-R15(扩展寄存器)                                          │ │
│  │  └── 用途:通用,函数参数(RDI, RSI, RDX, RCX, R8, R9)        │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

特殊寄存器

Text Only
┌─────────────────────────────────────────────────────────────────────┐
│                    x86-64特殊寄存器                                   │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  RIP(Instruction Pointer)指令指针                                  │
│  ├── 存储下一条要执行的指令地址                                      │
│  ├── 自动递增(顺序执行)                                            │
│  └── 跳转/调用时改变                                                 │
│                                                                     │
│  RFLAGS(Flags Register)标志寄存器                                  │
│  ├── CF(Carry Flag):进位标志                                      │
│  ├── ZF(Zero Flag):零标志                                         │
│  ├── SF(Sign Flag):符号标志                                       │
│  ├── OF(Overflow Flag):溢出标志                                   │
│  ├── PF(Parity Flag):奇偶标志                                     │
│  └── AF(Auxiliary Carry):辅助进位                                 │
│                                                                     │
│  段寄存器(现代操作系统中作用有限)                                   │
│  ├── CS(Code Segment):代码段                                       │
│  ├── DS(Data Segment):数据段                                       │
│  ├── SS(Stack Segment):栈段                                        │
│  └── FS/GS:线程本地存储                                              │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

寄存器的历史演变

Text Only
8086(16位)        80386(32位)       x86-64(64位)
   ┌───┐              ┌─────┐            ┌───────┐
   │AX │─────────────→│ EAX │───────────→│  RAX  │
   └───┘              └─────┘            └───────┘
   ┌───┐              ┌─────┐            ┌───────┐
   │BX │─────────────→│ EBX │───────────→│  RBX  │
   └───┘              └─────┘            └───────┘
   ...                ...                ...

   新增:R8-R15(64位特有)

3. CPU执行指令的过程

指令周期(Instruction Cycle)

Text Only
┌─────────────────────────────────────────────────────────────────────┐
│                    CPU指令执行周期                                     │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  ┌─────────┐    ┌─────────┐    ┌─────────┐    ┌─────────┐          │
│  │  取指   │───→│  译码   │───→│  执行   │───→│  访存   │          │
│  │ Fetch   │    │ Decode  │    │Execute  │    │ Memory  │          │
│  └────┬────┘    └─────────┘    └─────────┘    └────┬────┘          │
│       │                                            │                │
│       │         ┌─────────┐                       │                │
│       └────────→│  写回   │←──────────────────────┘                │
│                 │Writeback│                                          │
│                 └────┬────┘                                          │
│                      │                                               │
│                      ↓                                               │
│              下一条指令                                              │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

各阶段详解

1. 取指(Fetch)

Text Only
操作:
1. PC(RIP)指向内存中的指令地址
2. 从内存读取指令到指令寄存器(IR)
3. PC = PC + 指令长度(指向下一条指令)

示例:
RIP = 0x400500
从地址0x400500读取指令 "mov eax, 42"
RIP = 0x400506(假设指令长度6字节)

2. 译码(Decode)

Text Only
操作:
1. 解析指令的操作码(Opcode)
2. 确定操作类型(mov, add, jmp等)
3. 确定操作数(寄存器、内存、立即数)
4. 从寄存器读取操作数

示例:
指令:mov eax, 42
译码结果:
- 操作:mov(数据传送)
- 目的:EAX寄存器
- 源:立即数42

3. 执行(Execute)

Text Only
操作:
1. ALU执行运算(如果是运算指令)
2. 计算内存地址(如果是访存指令)
3. 比较操作数,设置标志位(如果是比较指令)
4. 计算跳转地址(如果是跳转指令)

示例:
指令:add eax, ebx
执行:EAX = EAX + EBX
      设置ZF/SF/CF/OF标志位

4. 访存(Memory)

Text Only
操作:
1. 如果是load指令:从内存读取数据
2. 如果是store指令:准备写入内存的数据
3. 如果不是访存指令:跳过此阶段

示例:
指令:mov eax, [rbx]
访存:从RBX指向的内存地址读取4字节到EAX

5. 写回(Writeback)

Text Only
操作:
1. 将结果写回寄存器
2. 更新PC(如果是跳转指令)
3. 准备执行下一条指令

示例:
指令:add eax, 42
写回:EAX = 新值


4. 现代CPU的核心技术

流水线(Pipeline)

Text Only
┌─────────────────────────────────────────────────────────────────────┐
│                    5级流水线示例                                       │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  时钟周期 →  T1      T2      T3      T4      T5      T6      T7     │
│                                                                     │
│  指令1      [F]────→[D]────→[E]────→[M]────→[W]                     │
│  指令2              [F]────→[D]────→[E]────→[M]────→[W]             │
│  指令3                      [F]────→[D]────→[E]────→[M]────→[W]     │
│  指令4                              [F]────→[D]────→[E]────→[M]     │
│                                                                     │
│  F=Fetch, D=Decode, E=Execute, M=Memory, W=Writeback                │
│                                                                     │
│  理想情况:每个时钟周期完成一条指令(CPI = 1)                        │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

超标量(Superscalar)

Text Only
┌─────────────────────────────────────────────────────────────────────┐
│                    超标量执行(每周期多条指令)                         │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  单发射(Scalar)                      超标量(Superscalar 4-way)    │
│                                                                     │
│  周期1: 取指1 ──→ 执行1 ──→ 写回1                                    │
│  周期2: 取指2 ──→ 执行2 ──→ 写回2                                    │
│  周期3: 取指3 ──→ 执行3 ──→ 写回3                                    │
│                                                                     │
│  周期1: 取指1,2,3,4 ──→ 执行1,2,3,4 ──→ 写回1,2,3,4                │
│  周期2: 取指5,6,7,8 ──→ 执行5,6,7,8 ──→ 写回5,6,7,8                │
│                                                                     │
│  现代CPU:每周期可执行4-8条指令                                       │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

乱序执行(Out-of-Order Execution)

Text Only
┌─────────────────────────────────────────────────────────────────────┐
│                    乱序执行示例                                        │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  原始程序顺序:                                                       │
│  1. load  r1, [a]      ; 从内存加载(耗时100周期)                    │
│  2. add   r2, r3, r4   ; 寄存器加法(耗时1周期)                      │
│  3. mul   r5, r6, r7   ; 寄存器乘法(耗时3周期)                      │
│  4. add   r8, r1, r2   ; 依赖r1的结果                                │
│                                                                     │
│  顺序执行:                                                           │
│  周期1-100: load r1(等待内存)                                       │
│  周期101:   add r2(r1完成后才能执行)                                │
│  周期102:   mul r5                                                    │
│  周期105:   add r8                                                    │
│  总时间:105周期                                                      │
│                                                                     │
│  乱序执行:                                                           │
│  周期1:     load r1(开始执行,但结果未就绪)                         │
│  周期2:     add r2(不依赖r1,提前执行)                              │
│  周期3:     mul r5(不依赖r1,提前执行)                              │
│  周期100:   load r1完成                                               │
│  周期101:   add r8(现在可以执行了)                                  │
│  总时间:101周期                                                      │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

🧪 动手实验

实验1:观察寄存器

目的:使用GDB观察寄存器的值

步骤

  1. 创建测试程序

    C++
    // register_test.cpp
    #include <iostream>
    
    int add(int a, int b) {
        return a + b;
    }
    
    int main() {
        int x = 10;
        int y = 20;
        int result = add(x, y);
        std::cout << "Result: " << result << std::endl;
        return 0;
    }
    

  2. 编译并调试

    Bash
    g++ -g -O0 -o register_test register_test.cpp
    gdb ./register_test
    

  3. 在GDB中观察寄存器

    Text Only
    (gdb) break add
    (gdb) run
    (gdb) info registers        # 查看所有寄存器
    (gdb) info registers rax    # 查看RAX寄存器
    (gdb) info registers rsp rbp # 查看栈指针和基址指针
    (gdb) stepi                 # 单步执行汇编指令
    (gdb) info registers        # 观察寄存器变化
    

实验2:查看汇编代码

目的:理解C代码如何对应到汇编

步骤

  1. 生成汇编代码

    Bash
    g++ -S -O0 register_test.cpp -o register_test.s
    cat register_test.s
    

  2. 分析关键部分

  3. 找到函数prologue(保存rbp,设置栈帧)
  4. 找到参数传递(rdi, rsi寄存器)
  5. 找到函数epilogue(恢复rbp,返回)

实验3:使用perf观察CPU性能

目的:学习使用性能分析工具

步骤

  1. 安装perf(Linux)

    Bash
    sudo apt-get install linux-tools-generic
    

  2. 创建计算密集型程序

    C++
    // cpu_intensive.cpp
    #include <iostream>  // 引入头文件
    
    long long factorial(int n) {
        if (n <= 1) return 1;
        return n * factorial(n - 1);
    }
    
    int main() {
        long long result = 0;
        for (int i = 0; i < 1000000; i++) {
            result += factorial(20);
        }
        std::cout << "Result: " << result << std::endl;
        return 0;
    }
    

  3. 使用perf分析

    Bash
    g++ -O2 -o cpu_intensive cpu_intensive.cpp
    perf stat ./cpu_intensive
    

  4. 观察输出

  5. 指令数(instructions)
  6. 时钟周期(cycles)
  7. CPI(Cycles Per Instruction)

💡 核心要点总结

CPU核心组件

组件 功能 关键概念
控制单元(CU) 控制协调 PC、指令译码、控制信号
运算单元(ALU) 执行运算 算术、逻辑、比较、移位
寄存器 高速存储 通用寄存器、特殊寄存器
缓存 缓解速度差异 L1/L2/L3 Cache

x86-64寄存器速查

寄存器 用途 说明
RAX 累加器 返回值、算术运算
RBX 基址 数据指针
RCX 计数器 循环计数
RDX 数据 I/O、乘除法
RSI 源索引 字符串操作源
RDI 目的索引 字符串操作目的
RBP 基址指针 栈帧基址
RSP 栈指针 栈顶
RIP 指令指针 下条指令地址
RFLAGS 标志寄存器 状态标志

指令执行周期

Text Only
取指(Fetch) → 译码(Decode) → 执行(Execute) → 访存(Memory) → 写回(Writeback)

❓ 常见问题

Q1:为什么64位CPU的寄存器是64位,但int还是32位?

A:为了保持向后兼容性。C/C++的int类型大小由ABI(应用程序二进制接口)定义,在x86-64 Linux上int是32位,long和指针是64位。

Q2:RAX和EAX是什么关系?

A:EAX是RAX的低32位。写入EAX会清零RAX的高32位,这是x86-64架构的设计。

Q3:为什么需要这么多寄存器?

A:寄存器比内存快得多(快100倍以上)。更多的寄存器可以减少内存访问,提高程序性能。

Q4:现代CPU的CPI(每指令周期数)是多少?

A:理想情况下CPI可以小于1(超标量)。但由于缓存未命中、分支预测失败等原因,实际CPI通常在1-4之间。


📚 扩展阅读

  1. 《深入理解计算机系统》 - 第4章:处理器体系结构
  2. 《计算机体系结构:量化研究方法》 - 第1-3章
  3. Intel手册:Intel 64 and IA-32 Architectures Software Developer's Manual

🎯 下一步

学习 02-指令集与汇编语言,掌握x86-64汇编编程。