1000万字以内最好的操作系统详解2:CPU执行程序与存储层次
后端硬核:CPU 到底是怎么执行程序的?磁盘真的比内存慢几万倍吗?
作为后端开发者,你每天写代码、调接口、查数据库,但你有没有想过——你敲下的那一行
a = 1 + 2,CPU 到底是怎么把它跑起来的?这篇文章从图灵机讲到冯·诺依曼,从寄存器讲到机械硬盘,把计算机最底层的运行逻辑一次讲透。

2.1 CPU 是如何执行程序的?
一切从图灵机开始
要理解 CPU 怎么执行程序,得先回到 1936 年。
那一年,阿兰·图灵提出了一个假想的机器——图灵机(Turing Machine)。它不是一台真实存在的机器,而是一个思想实验:
┌─────────────────────────────────────────────┐
│ 无限长纸带 → 分成一个个格子,每个格子写一个符号 │
│ 读写头 → 在纸带上左右移动,读格子、写格子 │
│ 状态寄存器 → 记录当前机器状态 │
│ 规则表 → 告诉机器:当前状态+读到什么符号时, │
│ 该写什么、往哪走、切换到什么状态 │
└─────────────────────────────────────────────┘
图灵的结论惊人地简洁:任何可计算的问题,都可以通过这台简单的机器一步步完成。 这就是后来所有计算机的理论原型。
你现在用的这台电脑,本质上就是一台极其复杂、速度极快的图灵机——只不过纸带换成了内存,读写头换成了 CPU,规则表换成了程序指令。
冯·诺依曼模型:现代计算机的蓝图
1945 年,冯·诺依曼在 EDVAC 报告中提出了一个核心设计思想:
数据和指令,都存放在同一个存储器里。
这就是著名的 冯·诺依曼架构(Von Neumann Architecture)。在此之前,早期计算机要么靠接线板编程,要么指令和数据分开存放,灵活性极差。
冯·诺依曼模型由五大部件组成:

| 部件 | 作用 | 类比 |
|---|---|---|
| 输入设备 | 把外部信息送进计算机 | 键盘、鼠标、网卡 |
| 输出设备 | 把计算机处理结果送出来 | 显示器、打印机、硬盘 |
| 存储器(Memory) | 存放指令和数据 | 既放"菜谱",也放"食材" |
| 运算器(ALU) | 执行算术和逻辑运算 | 厨房的灶台和菜刀 |
| 控制器(CU) | 指挥协调所有部件按节拍工作 | 厨房里的大厨 |
其中,运算器 + 控制器 = 中央处理器(CPU)。
内存:既放指令,也放数据
在冯·诺依曼模型里,内存是一个巨大的"房间阵列":
地址 内容
─────────────────────
0x1000 ┌─────────┐
0x1001 │ 指令 │ ← 程序代码放在这里
0x1002 │ 指令 │
0x1003 │ 指令 │
0x1004 ├─────────┤
0x1005 │ 数据 │ ← 变量、常量放在这里
0x1006 │ 数据 │
0x1007 └─────────┘
每个内存单元有一个唯一编号(地址),每个单元存放一个固定位数的二进制数据(通常是 8 位 = 1 字节)。CPU 要读某个数据,就把地址扔到地址总线上,内存就把对应单元的数据送到数据总线上。
关键洞察:CPU 根本分不清内存里存的是指令还是数据——它只是根据当前位置(程序计数器 PC)去取,然后机械地执行。 这就是为什么缓冲区溢出等漏洞能得逞:你输入的数据,被 CPU 当成了指令来执行。
CPU:大脑的内部构造
拆开一颗现代 CPU,你会看到里面有这些关键部件:
┌──────────────────── CPU 内部 ────────────────────┐
│ │
│ ┌──────────┐ ┌──────────┐ ┌────────────────┐ │
│ │ 程序计数器 │ │ 指令寄存器 │ │ 通用寄存器组 │ │
│ │ (PC) │ │ (IR) │ │ R0 R1 R2 ... │ │
│ └──────────┘ └──────────┘ └────────────────┘ │
│ │
│ ┌──────────────────────────────────────────┐ │
│ │ 控制单元 (CU) │ │
│ │ 译码 → 产生控制信号 → 指挥全机 │ │
│ └──────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────┐ │
│ │ 算术逻辑单元 (ALU) │ │
│ │ 加法 / 减法 / 与 / 或 / 异或 / 比较 │ │
│ └──────────────────────────────────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────┐ │
│ │ L1 Cache │ │ L2 Cache │ │ 内存管理 │ │
│ │ (数据/指令) │ │ │ │ 单元MMU │ │
│ └─────────────┘ └─────────────┘ └─────────┘ │
└──────────────────────────────────────────────────┘
几个核心寄存器的职责:
- 程序计数器(PC):存着"下一条指令的内存地址"。CPU 每取完一条指令,PC 自动加 1(或跳到目标地址),就像书签一样告诉你读到哪了。
- 指令寄存器(IR):当前正在执行的指令,临时存在这里。
- 通用寄存器:CPU 内部最快的"小内存",用来临时存放运算数据。x86-64 有 16 个通用寄存器,ARM64 有 31 个。
总线:连接一切的信息高速公路
CPU、内存、外设之间靠 总线(Bus) 通信。总线分三组:
地址总线 (Address Bus)
←———————————————————————
CPU 告诉内存:"我要读哪个地址"
数据总线 (Data Bus)
⇅⇅⇅⇅⇅⇅⇅⇅⇅⇅⇅⇅⇅⇅⇅
CPU 和内存之间搬运数据
控制总线 (Control Bus)
———————————————————————→
CPU 发出读/写信号
- 地址总线:宽度决定了 CPU 能访问多大的内存空间。32 位地址总线 = 2³² = 4GB 寻址空间;64 位地址总线理论上能寻址 16EB(尽管实际用不到这么多)。
- 数据总线:宽度决定了 CPU 一次能搬多少数据。32 位数据总线 = 一次传 4 字节。
- 控制总线:传读/写、中断、时钟等控制信号。
线路位宽与 CPU 位宽
我们常说的"32 位 CPU"“64 位 CPU”,到底指什么?
CPU 的"位",指的是 ALU 一次能处理的二进制数据位数。
| 对比项 | 32 位 CPU | 64 位 CPU |
|---|---|---|
| 通用寄存器宽度 | 32 位 | 64 位 |
| 一次算术运算最大 | 2³² ≈ 42 亿 | 2⁶⁴ ≈ 1844 亿亿 |
| 虚拟地址空间 | 最多 4GB | 理论 16EB,实际常用 48 位 = 256TB |
| 数据总线(典型) | 32 位 | 64 位 |
注意:位宽 ≠ 总线宽度。 比如 64 位 CPU 可能用 256 位甚至 512 位的数据总线(SIMD 指令),也可能地址总线只有 48 位(实际寻址)。位宽描述的是"计算能力",总线宽度描述的是"搬运能力"。
程序执行的基本过程:取指 → 译码 → 执行
不管多复杂的程序,CPU 周而复始地做这三件事(取指周期):
┌──────────┐
│ 取指 │ PC → 内存地址总线 → 指令经数据总线 → IR
│ Fetch │ PC = PC + 1
└────┬─────┘
↓
┌──────────┐
│ 译码 │ CU 分析 IR 中的 opcode,确定要做什么
│ Decode │ 并准备好操作数来源
└────┬─────┘
↓
┌──────────┐
│ 执行 │ ALU 执行运算,或读写内存,或跳转到新地址
│ Execute │ 结果写回寄存器或内存
└────┬─────┘
↓
回到取指,无限循环
这个循环叫做 指令周期(Instruction Cycle),也叫 Fetch-Decode-Execute 循环。现代 CPU 每秒要跑几十亿次这个循环。
实战拆解:a = 1 + 2 到底经历了什么?
假设我们写了一行代码:
int a = 1 + 2;
这行代码编译成汇编后,大概长这样(简化版):
MOV R1, #1 ; 把立即数 1 存入寄存器 R1
MOV R2, #2 ; 把立即数 2 存入寄存器 R2
ADD R3, R1, R2 ; R1 + R2,结果存入 R3
MOV [0x2000], R3 ; 把 R3 的值写入内存地址 0x2000(变量 a)
我们逐条追踪 CPU 的执行过程:
第一条指令 MOV R1, #1
| 阶段 | 发生了什么 |
|---|---|
| 取指 | PC=0x1000,CPU 把 0x1000 送到地址总线,内存返回指令 MOV R1, #1,存入 IR。PC 变为 0x1004 |
| 译码 | CU 解读 opcode 为"MOV",知道要把立即数存入寄存器 |
| 执行 | 立即数 1 被送入 R1 |
第二条指令 MOV R2, #2
同理,PC=0x1004,取指 → 译码 → 执行,R2 = 2。PC 变为 0x1008。
第三条指令 ADD R3, R1, R2
| 阶段 | 发生了什么 |
|---|---|
| 取指 | PC=0x1008,取回 ADD 指令 |
| 译码 | CU 识别出这是加法指令,准备 ALU |
| 执行 | ALU 接收 R1(1) 和 R2(2),执行加法,得到 3。结果写入 R3 |
第四条指令 MOV [0x2000], R3
| 阶段 | 发生了什么 |
|---|---|
| 取指 | PC=0x100C,取回指令 |
| 译码 | CU 识别出这是"写内存"操作 |
| 执行 | 把地址 0x2000 送到地址总线,把 R3 的值 3 送到数据总线,发出写信号。内存把 3 写入 0x2000 单元 |
至此,a = 1 + 2 执行完毕。你写代码时感觉瞬间完成的事,CPU 实际上经历了 4 次取指、4 次译码、4 次执行,中间还穿插了无数次总线传输和寄存器读写。

指令:CPU 听得懂的"外语"
一条指令在内存中就是一串二进制数,通常由两部分组成:
┌──────────────┬──────────────────────┐
│ Opcode │ Operand(s) │
│ 操作码 │ 操作数 │
│ (做什么) │ (对谁做) │
└──────────────┴──────────────────────┘
比如 ADD 比如 R1, R2
比如 ADD R1, R2 翻译成二进制(假设):
00000001 00000001 00000010
ADD R1 R2
指令的类型
CPU 的指令集(Instruction Set)虽多,但大致可以归为五大类:
| 类型 | 作用 | 典型指令 | 生活类比 |
|---|---|---|---|
| 数据传送 | 在寄存器和内存之间搬数据 | MOV, PUSH, POP, LOAD, STORE | 搬箱子 |
| 算术逻辑 | 做计算 | ADD, SUB, MUL, DIV, AND, OR, XOR, CMP | 算账 |
| 控制流 | 改变执行顺序 | JMP, BEQ, BNE, CALL, RET | 转弯/跳站 |
| 移位 | 移位操作 | SHL, SHR, ROL, ROR | 左移右移 |
| 特殊 | 中断、特权指令 | INT, IRET, HLT, IN, OUT | 按门铃/睡觉 |
你写的所有高级语言代码,最终都被编译器翻译成这五类指令的组合。
指令的执行速度:为什么 CPU 越来越快?
一条指令需要多少时间?这取决于几个关键指标:
- 时钟频率(Clock Rate):CPU 每秒振荡多少次。比如 3 GHz = 每秒 30 亿次时钟周期,一个周期 = 0.33 纳秒。
- CPI(Cycles Per Instruction):平均每条指令需要几个时钟周期。
- IPC(Instructions Per Cycle):每个周期能执行几条指令,是 CPI 的倒数。
理论执行时间 = 指令数 × CPI / 时钟频率
那为什么 CPU 频率从 1980 年代的 1 MHz 飙到了今天的 5 GHz+,却越来越难往上提?
- 物理极限:频率越高,发热越严重(Intel 曾经因为功耗墙,"Tick-Tock"模式被迫放缓)。
- 架构优化:现代 CPU 不靠堆频率,而是靠 流水线(Pipeline)、超标量(Superscalar)、乱序执行(Out-of-Order)、分支预测 等技术,让一个周期内能完成更多指令。
流水线的思想很简单:就像工厂流水线,取指、译码、执行不是等一条指令完全做完才开始下一条,而是像流水线一样重叠起来——当第 1 条在执行时,第 2 条在译码,第 3 条在取指。这样吞吐率就大大提高了。
非流水线:
指令1: [取指][译码][执行]
指令2: [取指][译码][执行]
指令3: [取指][译码][执行]
流水线:
周期: 1 2 3 4 5 6
指令1: 取指 译码 执行
指令2: 取指 译码 执行
指令3: 取指 译码 执行
理想情况下,流水线让每 1 个周期就能完成一条指令(IPC=1),超标量设计更进一步,能做到 IPC > 1。
2.1 小结
- 图灵机定义了"可计算"的理论边界,是所有计算机的思想原型。
- 冯·诺依曼模型把指令和数据统一存放在内存中,CPU 通过 取指→译码→执行 的循环来运行程序。
- CPU 内部有 PC、IR、ALU、寄存器组 等关键部件,通过 地址总线、数据总线、控制总线 与外界通信。
- 你写的每一行代码,最终都被拆解成 数据传送、算术逻辑、控制流 等基本指令,在 CPU 上一条一条地执行。
- 现代 CPU 靠 流水线、超标量、缓存 等技术提升性能,而不是单纯堆频率。
2.2 磁盘比内存慢几万倍?
存储器的层次结构:为什么不能全都用最快的?
如果内存速度跟寄存器一样快、容量跟硬盘一样大、价格像白菜一样便宜,那该多好?
但物理世界不允许——速度越快的存储,必然容量越小、价格越贵。于是计算机科学家设计了一套 金字塔式的存储层次结构:

从上到下,速度越来越慢、容量越来越大、价格越来越便宜。
第一层:寄存器(Register)
- 位置:CPU 内部,是 ALU 旁边的小存储单元
- 容量:极小,x86-64 约 16 个通用寄存器(×8 字节 = 128 字节)
- 速度:1 个时钟周期,约 0.3 ns
- 价格:极贵(和 CPU 晶圆一起制造,面积成本极高)
- 作用:CPU 运算时的临时"草稿纸",没有寄存器,ALU 根本没法工作
打个比方:寄存器就是你手里的笔和眼前的草稿纸,随手就能写。
第二层:CPU Cache(高速缓存)
CPU Cache 是为了缓解 CPU 和内存之间的速度差距而设计的。它分为多级:
| 缓存 | 容量(典型) | 延迟 | 位置 |
|---|---|---|---|
| L1 Cache | 32 KB(数据)+ 32 KB(指令) | ~1 ns(4 周期) | CPU 核心内部 |
| L2 Cache | 256 KB ~ 1 MB | ~3 ns(12 周期) | CPU 核心内部 |
| L3 Cache | 8 MB ~ 64 MB(所有核心共享) | ~10 ns(40 周期) | CPU 内部 |
为什么需要缓存?因为程序有 局部性原理:
- 时间局部性:刚访问过的数据,很快可能再访问
- 空间局部性:刚访问过的数据旁边的数据,很快可能被访问
Cache 就是利用这个原理,把 CPU 常用的数据提前从内存"搬"到更快的地方。命中率越高,CPU 等待内存的时间越少。
第三层:内存(RAM / Main Memory)
- 位置:主板上的内存条,DDR4/DDR5
- 容量:8 GB ~ 64 GB(消费级),服务器可达 TB 级
- 速度:~60 ~ 100 ns(约 200 ~ 300 个 CPU 周期)
- 价格:约 ¥ 30 ~ 60 / GB
- 作用:存放正在运行的程序和数据
内存就像你的书桌:你正在用的东西放在桌上,方便随时拿,但桌面空间有限。
这里有个关键的对比:
CPU 寄存器访问一次:~0.3 ns
内存访问一次: ~80 ns
差距 ≈ 250 倍!
也就是说,CPU 每访问一次内存,相当于等了 250 个寄存器周期。这就是为什么 Cache 如此重要——它在寄存器和内存之间充当了"中间缓冲"。
第四层:SSD(固态硬盘)
- 位置:主板上的 M.2 插槽或 SATA 接口
- 容量:256 GB ~ 4 TB
- 速度:~100 μs(微秒)= 100,000 ns(随机读取)
- 顺序读取:~3000 ~ 7000 MB/s(NVMe PCIe 4.0)
- 价格:约 ¥ 0.3 ~ 0.6 / GB
SSD 没有机械结构,用闪存芯片存储数据,速度比 HDD 快一个数量级以上。
第五层:HDD(机械硬盘)
- 位置:SATA 或 SAS 接口
- 容量:1 TB ~ 20 TB
- 速度:~10 ms(毫秒)= 10,000,000 ns(随机寻道)
- 顺序读取:~100 ~ 200 MB/s
- 价格:约 ¥ 0.05 ~ 0.1 / GB
HDD 靠磁盘旋转和磁头摆动来读写,寻道时间(磁头移动到目标磁道) 是最大的开销。
存储器层次关系:一张表看懂
| 层级 | 类型 | 典型容量 | 访问延迟 | 相对速度(对比内存) | 价格(每 GB) |
|---|---|---|---|---|---|
| 1 | 寄存器 | < 1 KB | 0.3 ns | ~250 倍快 | 极其昂贵 |
| 2 | L1 Cache | 32 KB | 1 ns | ~80 倍快 | 极贵 |
| 3 | L2 Cache | 256 KB ~ 1 MB | 3 ns | ~25 倍快 | 很贵 |
| 4 | L3 Cache | 8 ~ 64 MB | 10 ns | ~8 倍快 | 贵 |
| 5 | 内存 RAM | 8 ~ 64 GB | 80 ns | 基准(1 倍) | ~¥ 40 |
| 6 | SSD | 256 GB ~ 4 TB | 100,000 ns | ~1,000 ~ 1,500 倍慢 | ~¥ 0.5 |
| 7 | HDD | 1 ~ 20 TB | 10,000,000 ns | ~100,000 ~ 150,000 倍慢 | ~¥ 0.07 |
磁盘比内存慢几万倍?——这个说法对不对?
我们来算一笔账:
内存随机访问:~80 ns
SSD 随机访问:~100,000 ns → 是内存的 ~1,250 倍
HDD 随机访问:~10,000,000 ns → 是内存的 ~125,000 倍
所以:
- SSD 比内存慢约 1,000 ~ 1,500 倍——不是"几万倍"
- HDD 比内存慢约 10 万 ~ 15 万倍——这才是真正的"几万倍甚至几十万倍"
这个说法在 机械硬盘(HDD) 的语境下基本成立,但在 固态硬盘(SSD) 的语境下,差距只有约 1000 倍,远没有"几万倍"那么夸张。
不过这里有个陷阱:以上是随机访问的延迟对比。如果看顺序读写带宽,差距会小很多:
| 存储类型 | 顺序读取带宽 |
|---|---|
| DDR5-4800 内存 | ~38,400 MB/s |
| NVMe SSD (PCIe 4.0) | ~7,000 MB/s |
| SATA SSD | ~550 MB/s |
| HDD | ~200 MB/s |
顺序读写时,内存只比最快的 SSD 快约 5 倍,比 HDD 快约 200 倍。真正的鸿沟在随机访问,而不是顺序读写。
这也是为什么数据库、操作系统、编程语言都在拼命做 预取(Prefetch) 和 顺序 I/O 优化——能顺序读就不随机读,能一次多读就不一次次读。
价格和性能的博弈:为什么不把缓存做得更大?
每 GB 成本大致对比(数量级):
寄存器: 几百万美元 / GB
L1 Cache: 几十万美元 / GB
L2 Cache: 几万美元 / GB
L3 Cache: 几千美元 / GB
DDR5 内存: ~40 美元 / GB
NVMe SSD: ~0.10 美元 / GB
HDD: ~0.01 美元 / GB
从 L3 Cache 到内存,单位容量成本下降了约 100 倍;从内存到 SSD,又下降了约 400 倍;从 SSD 到 HDD,再下降约 10 倍。
这就是为什么 Cache 只能做几 MB 到几十 MB,而内存能做到几十 GB,硬盘能做到几十 TB——用容量换价格,用速度换成本,这就是存储层次的核心逻辑。
对后端开发的启示
理解了存储层次,你就能理解很多后端工程实践背后的道理:
- 为什么 Redis 快? 因为它把数据放在内存里,比 MySQL(磁盘)快上万倍。
- 为什么要做数据库索引? 因为索引减少了磁盘随机 I/O,把"全表扫描"变成了"定位查找"。
- 为什么 CPU 缓存行(Cache Line)是 64 字节? 因为空间局部性——每次取 64 字节进 L1,相邻数据大概率马上会用到。
- 为什么锁竞争会导致性能暴跌? 因为 Cache 一致性协议(MESI)会让多核之间反复同步缓存行,比普通访问慢得多。
- 为什么 Rust 强调"零拷贝"? 因为每多一次内存拷贝,就是一次 80 ns 的延迟——在每秒处理百万级请求时,这些加起来就是巨大的开销。
2.2 小结
- 计算机存储器是一个 金字塔层次结构:寄存器 → Cache → 内存 → SSD → HDD,越往上越快、越小、越贵。
- 内存随机访问约 80 ns,SSD 约 100 μs(10 万 ns),HDD 约 10 ms(1000 万 ns)。
- "磁盘比内存慢几万倍"这个说法,对 HDD 成立(约 10 万倍),对 SSD 不成立(约 1000 倍)。
- 真正的性能鸿沟在 随机访问延迟,顺序带宽的差距小得多。
- 理解存储层次,是理解数据库、缓存、操作系统、并发编程等后端核心问题的基石。
写在最后
从图灵的一条纸带,到冯·诺依曼的五大部件,从 CPU 内部的寄存器到机房里的机械硬盘——计算机的世界看似复杂,但拆开来看,不过是 “取指→译码→执行” 的无限循环,和 “快的小、慢的大” 的存储阶梯。
你写的每一行代码、发的每一个请求、查的每一条数据库记录,都在这个层次结构上上下下穿梭。理解了这些,你就不只是在"调 API",而是真正理解了机器是怎么干活的。
关注我,下期我们聊聊 操作系统如何调度进程、虚拟内存到底是怎么回事、中断和系统调用的底层原理——这些才是后端工程师真正的"内功心法"。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐
所有评论(0)