"饿了么么"外卖App刚上线三天,服务器就卡成了PPT。老王一拍桌子:"再买台机器!堆就完了!"小明翻了个白眼:"你连CPU是怎么取指令执行都不知道,堆一万台也是烧钱。"老王不服:"我又不是修电脑的,架构师干嘛要懂硬件?"
大牛哥端着咖啡路过:"架构师不懂底层原理,就像厨师不知道火是怎么着的——菜炒糊了只能怪锅。这章就带你把电脑拆开,看看里面到底装了啥、怎么跑的、为啥会卡。"
🧱 1.1 计算机系统组成:硬件全家桶
计算机系统是硬件+软件的综合体,按功能分成多级层次。原始的冯·诺依曼计算机以运算器为中心,现代计算机已经转向以存储器为中心——因为现在干活最慢的、最值钱的、最拖后腿的都是存数据。
1.1.1 硬件六大件
| 部件 | 干啥的 | 关键零件 |
|---|---|---|
| 控制器 | 分析执行指令的总指挥 | PC(下条指令地址) / IR(暂存指令) / ID(译码) / 时序部件 |
| 运算器 ALU | 算术+逻辑运算 | ALU / AC累加器 / DR数据缓冲 / PSW状态条件 |
| 主存储器 | 存现场指令和数据(内存) | CPU可直接读写 |
| 辅助存储器 | 长期保存信息(外存) | 硬盘、U盘 |
| 输入设备 | 把人的信息塞进电脑 | 键盘、鼠标、扫描仪 |
| 输出设备 | 把结果送出电脑 | 显示器、打印机 |
🏗️ 1.1.2 系统结构分类:从冯·诺依曼到Flynn
存储程序概念是冯·诺依曼1946年提出的,三句话:①五大部件组成;②内部用二进制;③程序和数据先存进存储器再启动。它最大的贡献是"存储程序控制",最大弱点是存储器访问成为瓶颈。突破这个瓶颈的就叫"非冯·诺依曼机"(数据流计算机、归约计算机等)。
1966年Flynn按指令流和数据流的多倍性把计算机分成四类:
| 类型 | 指令流 | 数据流 | 举例/人话 |
|---|---|---|---|
| SISD | 单 | 单 | 传统单处理器,一条指令一条数据地干 |
| SIMD | 单 | 多 | 并行处理机/矩阵处理机,一条指令对一批数据同时算 |
| MISD | 多 | 单 | 实际很少见,有文献把流水线算这类 |
| MIMD | 多 | 多 | 多核处理器、多处理机,全面并行 |
⚔️ 1.1.3 CISC vs RISC:复杂派 vs 精简派
| 对比项 | CISC(复杂指令集) | RISC(精简指令集) |
|---|---|---|
| 指令数量 | 100~250条 | 少,只用高频简单指令 |
| 寻址方式 | 5~20种 | 少,寄存器/立即数/相对寻址 |
| 指令长度 | 变长(译码复杂) | 固定,格式少(译码容易) |
| 主存访问 | 有指令直接操作主存 | 只有LOAD/STORE访存,其余在寄存器间 |
| 控制方式 | 微程序控制为主 | 硬布线逻辑为主 |
| 执行 | 多周期 | 单周期+流水线 |
| 通用寄存器 | 较少 | 多,一般32个以上,有的上千 |
🛣️ 1.1.4 总线:数据的高速公路
总线是一组能为多个部件分时共享的公共信息传送线路。"共享"是多个部件都能挂上去,"分时"是同一时刻只允许一个部件发信息(否则撞车),但可以多个部件同时接收。
- 按位置分:内部总线(CPU内寄存器/ALU之间)、外部总线(CPU与内存/外设之间)
- 按功能分:地址总线(传地址)、数据总线(传数据)、控制总线(传控制信号)
💾 1.2 存储器系统:金字塔式的记忆术
存储器分三级:Cache → 主存 → 辅存。越往上越快越小越贵,越往下越慢越大越便宜。CPU直接访问主存,但不能直接访问辅存(要靠I/O搬进主存)。当CPU太快主存跟不上时,就在CPU和主存之间塞一层Cache。
这套能用低投入换高速度,靠的是局部性原理:程序执行时短期内只局限在某片区域。分两种——
- ⏱️ 时间局部性:某条指令刚执行过,不久后可能再执行(典型:循环)
- 🗺️ 空间局部性:访问了某单元,附近单元也很快被访问(典型:顺序执行)
存取方式有四种:顺序存取(磁带,按线性顺序)、直接存取(磁盘,直接定位到数据块)、随机存取(主存,任意单元等时访问)、相联存取(Cache,按内容而不是地址访问)。
🧩 1.2.1 主存储器:RAM、ROM和编址
主存分RAM(随机存取,可读写但断电丢数据)和ROM(只读,断电不丢,常存BIOS)。
- DRAM(动态RAM):信息会随时间消失,需定时刷新;密度大、便宜、但慢
- SRAM(静态RAM):不断电信息一直在;速度快、电路简单、但容量小、贵
💿 1.2.2 辅助存储器:磁带与硬盘
磁带是顺序存取,容量大、便宜、好携带,但慢,现在主要用于归档。硬盘信息分布层次:记录面 → 圆柱面(柱面)→ 磁道 → 扇区。
- 每个盘片两个记录面,每个面对应一个磁头,所有磁头一起径向移动
- 各记录面上相同编号的磁道构成一个柱面——引入柱面是为提高速度:大文件先塞满同一柱面,避免来回寻道
- 一条磁道划分为若干扇区,每扇区存定长块(如512字节),扇区编号从1开始(磁头/柱面从0开始)
磁盘访问时间 = 寻道时间 + 旋转延迟时间(定位磁道+旋转到目标扇区,平均转半圈)。
⚡ 1.2.3 Cache:突破冯·诺依曼瓶颈的便签纸
Cache的功能是提高CPU数据输入输出速率,突破"冯·诺依曼瓶颈"(CPU与存储系统间数据传送带宽限制)。它在CPU和内存之间设小容量高速存储,靠相联存储器CAM(按内容访问)实现。命中率高,系统性能就大幅提升。
设命中率 h,Cache周期 t1,主存周期 t2,则平均周期 t3 = t1·h + t2·(1−h)。命中率哪怕提高一点,性能也明显改善。
三种映射方式
主存和Cache都分成同样大小的块。CPU访存时先查Cache有没有,这叫地址映射。
| 方式 | 规则 | 优缺点 |
|---|---|---|
| 直接映像 | 主存每页只能进Cache固定页(区号+页号+页内地址) | 硬件简单,但冲突率高、不灵活 |
| 全相联映像 | 主存任意页可映像到Cache任意页 | 灵活、冲突低,但比较器贵、速度慢,仅小容量Cache用 |
| 组相联映像 | 组间直接映像、组内全相联(折中) | 兼顾灵活与代价,主流选择 |
替换算法:Cache满了踢谁走?
- 🎲 随机算法:瞎选一个换掉,简单粗暴
- 📤 FIFO(先进先出):按进Cache的先后淘汰最早的。简单,但可能踢掉正在用的循环块
- 📉 LRU(近期最少使用):淘汰最近用得最少的,需"年龄计数器"记录。较合理但开销大
写操作:怎么保证Cache和主存一致?
- ✍️ 写直达(write through):写Cache同时写回内存。简单、数据始终正确,但慢(多写主存)
- 🔁 写回(write back):改了Cache先不写内存,等这块被淘汰时才写回。快,但主存可能短暂不一致(用标志位记录是否改过)
- 🏷️ 标记法:每数据设有效位,进Cache置1,CPU改数据只写内存并把有效位置0。读时先查有效位,为1才从Cache取
🌊 1.3 流水线:像工厂流水线一样干活
流水线把一个任务分解成若干子任务,不同子任务由不同机构并行执行——任一时刻每个机构只处理一个任务的不同阶段,实现重叠执行提高效率。
⏱️ 1.3.1 流水线周期:木桶效应
把工作分N个阶段,最耗时的那段的时间就是流水线周期。比如取指2ms、分析4ms、执行1ms,流水线周期就是4ms——最慢的那段卡住所有人。
🧮 1.3.2 执行时间:理论 vs 实际
流水线执行时间 = 第1条指令的完整时间 + (n−1) × 流水线周期。但考试有两种算法:
🚀 1.3.3 吞吐率:单位时间出多少活
吞吐率 TP = 单位时间内完成的任务数 = 任务数 / 流水线总时间。流水线周期越短、段数越合理,吞吐率越高。
🏁 1.3.4 加速比:用了流水线快多少
加速比 = 不使用流水线的时间 / 使用流水线的时间 = T0 / Tk。如果各段时间相等(设为Dt),一条k段流水线完成n个连续任务:流水线时间=(k+n−1)Dt,顺序时间=nkDt,加速比=nk / (k+n−1)。任务数n越大,加速比越接近k(段数)——加再多段,收益也是有上限的。