01-计算机是如何工作的¶
重要性:⭐⭐⭐⭐⭐ 实用度:⭐⭐⭐⭐⭐ 学习时间:2-3天 必须掌握:是
为什么学这一章?¶
在深入代码如何变成可执行程序之前,你需要先理解计算机的基本工作原理。这一章将为你建立计算机系统的全局认知,为后续学习打下坚实基础。
学完这一章,你将能够: - ✅ 解释计算机的五大基本组成部分 - ✅ 理解冯·诺依曼架构的核心思想 - ✅ 明白硬件和软件如何协同工作 - ✅ 建立计算机系统的层次化思维
📖 核心概念¶
1. 计算机的五大组成部分¶
计算机本质上是一个信息处理系统,由五个基本部分组成:
┌─────────────────────────────────────────────────────────────┐
│ 计算机系统架构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 输入设备 输出设备 │
│ (键盘/鼠标) (显示器/打印机) │
│ ↓ ↑ │
│ └────────┬─────┘ │
│ ↓ │
│ ┌─────────────────────────┐ │
│ │ 运算器 (ALU) │ ← 执行算术和逻辑运算 │
│ ├─────────────────────────┤ │
│ │ 控制器 │ ← 指挥协调各部件工作 │
│ ├─────────────────────────┤ │
│ │ 存储器 │ ← 存储程序和数据 │
│ │ (内存 + 外存) │ │
│ └─────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
运算器(ALU - Arithmetic Logic Unit)¶
是什么:计算机的"计算器"
功能: - 算术运算:加、减、乘、除 - 逻辑运算:与、或、非、异或 - 比较运算:等于、大于、小于
生活化类比:
就像你做题时的草稿纸和笔,ALU负责所有的计算工作。
代码示例:
int a = 5, b = 3;
int c = a + b; // ALU执行加法
int d = a & b; // ALU执行按位与
bool e = a > b; // ALU执行比较
控制器(Control Unit)¶
是什么:计算机的"指挥官"
功能: - 从内存中取指令 - 解码指令 - 控制其他部件执行指令 - 协调各部件的工作节奏
生活化类比:
就像乐队的指挥,不直接演奏乐器,但指挥着整个乐队的节奏和配合。
存储器(Memory)¶
是什么:计算机的"记忆系统"
分类:
存储器层次结构(速度 vs 容量)
速度 ↑ ┌─────────────┐
快 │ │ 寄存器 │ ← CPU内部,最快,容量最小(几十字节)
│ ├─────────────┤
│ │ 缓存 │ ← L1/L2/L3,快,容量小(KB到MB)
│ ├─────────────┤
│ │ 内存(RAM) │ ← 主存,较快,容量中等(GB级)
│ ├─────────────┤
│ │ 硬盘/SSD │ ← 外存,慢,容量大(TB级)
慢 │ ├─────────────┤
│ │ 网络存储 │ ← 最慢,容量最大
└ └─────────────┘
容量 →
小 大
为什么需要层次结构? - 速度快的存储器贵且容量小 - 速度慢的存储器便宜且容量大 - 通过层次结构平衡性能和成本
输入设备¶
是什么:向计算机输入数据和指令的设备
常见设备: - 键盘、鼠标 - 麦克风、摄像头 - 触摸屏 - 传感器(温度、光线等)
输出设备¶
是什么:将计算机处理结果呈现给用户的设备
常见设备: - 显示器 - 打印机 - 扬声器 - 投影仪
2. 冯·诺依曼架构¶
什么是冯·诺依曼架构?¶
1945年,数学家冯·诺依曼提出了一种计算机设计思想,成为现代计算机的基础。
核心思想(三大原则):
┌─────────────────────────────────────────────────────────────┐
│ 冯·诺依曼架构三大原则 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1️⃣ 存储程序原则 │
│ 程序和数据以同等地位存储在存储器中 │
│ 计算机可以从存储器中读取指令并执行 │
│ │
│ 2️⃣ 二进制原则 │
│ 指令和数据都用二进制表示 │
│ 0和1可以表示所有信息 │
│ │
│ 3️⃣ 五大部件原则 │
│ 计算机由运算器、控制器、存储器、输入设备、输出设备组成 │
│ │
└─────────────────────────────────────────────────────────────┘
存储程序原则的意义¶
革命性变化:
在冯·诺依曼架构之前: - 计算机程序通过物理线路(插线板)设置 - 改变程序需要重新接线 - 每台计算机只能做特定任务
在冯·诺依曼架构之后: - 程序存储在内存中 - 改变程序只需要改变内存中的指令 - 同一台计算机可以执行不同任务
生活化类比:
就像乐谱和乐器的关系。以前每个乐器只能演奏固定的曲子(硬连线),现在乐器可以演奏任何乐谱(存储程序)。
二进制原则¶
为什么用二进制?
┌─────────────────────────────────────────────────────────────┐
│ 为什么计算机用二进制? │
├─────────────────────────────────────────────────────────────┤
│ │
│ 物理实现简单 │
│ ├── 电路:通电=1,断电=0 │
│ ├── 磁盘:磁化=1,未磁化=0 │
│ ├── 光盘:凹坑=1,平面=0 │
│ └── 光纤:有光=1,无光=0 │
│ │
│ 可靠性高 │
│ ├── 只有两种状态,容易区分 │
│ └── 抗干扰能力强 │
│ │
│ 运算简单 │
│ ├── 加法:0+0=0, 0+1=1, 1+0=1, 1+1=10 │
│ └── 逻辑运算:与、或、非都可以用电路实现 │
│ │
└─────────────────────────────────────────────────────────────┘
二进制与十进制的转换:
十进制 13 = 二进制 1101
计算过程:
13 ÷ 2 = 6 余 1
6 ÷ 2 = 3 余 0
3 ÷ 2 = 1 余 1
1 ÷ 2 = 0 余 1
从下往上读:1101
验证:1×2³ + 1×2² + 0×2¹ + 1×2⁰ = 8 + 4 + 0 + 1 = 13 ✓
3. 计算机系统的层次结构¶
计算机系统是一个复杂的层次结构,从硬件到软件,每一层都建立在下一层之上:
┌─────────────────────────────────────────────────────────────┐
│ 第6层:应用程序层 │
│ ├── 浏览器、Word、游戏、AI训练程序 │
│ └── 用户直接使用的软件 │
├─────────────────────────────────────────────────────────────┤
│ 第5层:高级语言层 │
│ ├── C/C++、Python、Java、JavaScript │
│ └── 程序员编写的源代码 │
├─────────────────────────────────────────────────────────────┤
│ 第4层:汇编语言层 │
│ ├── x86-64汇编、ARM汇编 │
│ └── 人类可读的机器指令 │
├─────────────────────────────────────────────────────────────┤
│ 第3层:操作系统层 │
│ ├── Windows、Linux、macOS │
│ └── 管理硬件资源,提供服务 │
├─────────────────────────────────────────────────────────────┤
│ 第2层:指令集架构层(ISA) │
│ ├── x86-64、ARM、RISC-V │
│ └── 硬件和软件的接口 │
├─────────────────────────────────────────────────────────────┤
│ 第1层:硬件层 │
│ ├── CPU、内存、硬盘、显卡 │
│ └── 物理硬件设备 │
└─────────────────────────────────────────────────────────────┘
各层之间的关系¶
为什么需要分层?¶
好处: 1. 抽象复杂:上层不需要了解下层的细节 2. 易于开发:程序员专注于当前层 3. 便于维护:修改一层不影响其他层 4. 促进标准化:层与层之间定义清晰的接口
生活化类比:
就像快递系统: - 你(用户)只需要填写快递单 - 快递员(应用程序)负责取件和派送 - 分拣中心(操作系统)负责路由规划 - 货车(硬件)负责实际运输
你不需要知道货车走哪条路,快递员不需要知道货车怎么开。
4. 指令执行的基本周期¶
CPU执行程序的基本过程:
┌─────────────────────────────────────────────────────────────┐
│ 指令执行周期(取指-执行循环) │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ │
│ │ 开始 │ │
│ └────┬─────┘ │
│ ↓ │
│ ┌──────────┐ ┌─────────────────────────────┐ │
│ │ 取指 │────→│ 从内存中读取下一条指令 │ │
│ │ (Fetch) │ │ PC(程序计数器)指向下一条 │ │
│ └────┬─────┘ └─────────────────────────────┘ │
│ ↓ │
│ ┌──────────┐ ┌─────────────────────────────┐ │
│ │ 译码 │────→│ 解析指令的操作类型和操作数 │ │
│ │ (Decode) │ │ 确定要执行什么操作 │ │
│ └────┬─────┘ └─────────────────────────────┘ │
│ ↓ │
│ ┌──────────┐ ┌─────────────────────────────┐ │
│ │ 执行 │────→│ ALU执行运算,或访问内存 │ │
│ │(Execute) │ │ 或进行IO操作 │ │
│ └────┬─────┘ └─────────────────────────────┘ │
│ ↓ │
│ ┌──────────┐ ┌─────────────────────────────┐ │
│ │ 访存 │────→│ 如有需要,读写内存数据 │ │
│ │(Memory) │ │ (非所有指令都需要) │ │
│ └────┬─────┘ └─────────────────────────────┘ │
│ ↓ │
│ ┌──────────┐ ┌─────────────────────────────┐ │
│ │ 写回 │────→│ 将结果写回寄存器或内存 │ │
│ │(Writeback)│ │ 更新程序状态 │ │
│ └────┬─────┘ └─────────────────────────────┘ │
│ ↓ │
│ ┌──────────┐ │
│ │ 重复 │──────────────────────────────────────── │
│ └──────────┘ (除非遇到停机指令) │
│ │
└─────────────────────────────────────────────────────────────┘
具体例子:执行 a = b + c¶
假设: - 变量a在内存地址1000 - 变量b在内存地址1004 - 变量c在内存地址1008
执行过程:
1. 取指:从内存读取 "LOAD b" 指令
2. 译码:解析为"从地址1004加载数据到寄存器R1"
3. 执行:从内存地址1004读取b的值到R1
4. 取指:从内存读取 "LOAD c" 指令
5. 译码:解析为"从地址1008加载数据到寄存器R2"
6. 执行:从内存地址1008读取c的值到R2
7. 取指:从内存读取 "ADD R1, R2" 指令
8. 译码:解析为"将R1和R2相加,结果存到R3"
9. 执行:ALU执行加法,R3 = R1 + R2
10. 取指:从内存读取 "STORE a" 指令
11. 译码:解析为"将R3的值存储到地址1000"
12. 执行:将R3的值写入内存地址1000
需要多少时钟周期? - 现代CPU可以在1个周期内执行多条指令(流水线、超标量) - 但基本思想仍然是取指-译码-执行
🧪 动手实验¶
实验1:观察程序的执行¶
目的:理解程序是如何被CPU执行的
步骤:
-
创建一个简单的C程序
-
编译并运行
-
使用GDB单步执行
思考问题: - 每执行一行C代码,实际执行了多少条机器指令? - 变量a、b、c存储在哪里(寄存器还是内存)?
实验2:观察汇编代码¶
目的:理解C代码如何转换成机器指令
步骤:
-
生成汇编代码
-
查看汇编代码
-
对比C代码和汇编代码
- 找到变量a、b、c的初始化
- 找到加法运算对应的汇编指令
- 找到printf调用
思考问题: - 一条C语句对应多少条汇编指令? - 哪些C语句生成的汇编代码最多?
实验3:使用Compiler Explorer¶
目的:可视化编译过程
步骤:
-
左侧输入代码:
-
右侧查看生成的汇编代码
-
尝试不同的编译器优化级别:
- 在编译器选项中添加
-O0(无优化) - 改为
-O3(最高优化) - 观察汇编代码的变化
思考问题: - 优化后的代码有什么变化? - 为什么简单的加法函数在优化后可能完全不同?
💡 核心要点总结¶
必须记住的概念¶
- 计算机五大部件:运算器、控制器、存储器、输入设备、输出设备
- 冯·诺依曼三大原则:存储程序、二进制、五大部件
- 存储程序的意义:程序和数据同等存储,计算机变得通用
- 系统层次结构:从硬件到应用,层层抽象
- 指令执行周期:取指→译码→执行→访存→写回
关键理解¶
程序执行的本质:
源代码
↓ 编译/解释
机器指令序列
↓ CPU执行
┌─────────────────────────────────────┐
│ 取指 → 译码 → 执行 → 访存 → 写回 │
│ ↑________________________________│
│ 循环执行 │
└─────────────────────────────────────┘
↓
程序运行结果
❓ 常见问题¶
Q1:为什么计算机只能理解二进制?
A:因为计算机硬件基于电子电路,只有两种稳定状态(通电/断电)。用二进制表示正好对应这两种状态,物理实现简单可靠。
Q2:高级语言(如Python)需要编译吗?
A:Python是解释型语言,不需要显式编译,但执行时仍然需要转换成机器码。Python解释器会逐行读取代码,转换成字节码,然后在虚拟机上执行。
Q3:CPU的时钟频率(如3.5GHz)是什么意思?
A:表示CPU每秒可以执行35亿个时钟周期。每个时钟周期可以执行一条或多条指令。频率越高,理论上CPU执行速度越快。
Q4:为什么需要操作系统?程序不能直接运行在硬件上吗?
A:理论上可以(裸机编程),但操作系统提供了: - 硬件抽象:程序不需要知道硬件细节 - 资源管理:多个程序共享CPU、内存 - 安全保护:防止程序破坏系统或其他程序 - 便捷服务:文件系统、网络、图形界面等
📚 扩展阅读¶
- 《编码:隐匿在计算机软硬件背后的语言》 - Charles Petzold
-
从摩斯码到计算机,循序渐进理解计算机原理
-
《计算机系统要素》 - Noam Nisan等
-
从逻辑门到操作系统,动手构建计算机系统
-
在线资源:
- Nand2Tetris(从与非门到俄罗斯方块)
- Ben Eater的计算机原理视频(YouTube/B站)
🎯 下一步¶
完成本章后,你已经建立了计算机系统的全局认知。接下来进入 02-从代码到执行的旅程,了解代码从编写到运行的完整过程。