《深入理解计算机系统》读书笔记01: 从入门到实践
《深入理解计算机系统》学习指南:从入门到实践
作者: andylin02
学习章节: 全书概览
关键词: CSAPP; 计算机系统漫游; 编译系统; 存储层次结构; 进程; 虚拟内存; 抽象
《深入理解计算机系统》(Computer Systems: A Programmer’s Perspective,简称 CSAPP)被誉为计算机领域的圣经,是每个程序员深入理解底层系统的必读之作。它不仅涵盖了计算机系统的核心概念,还通过大量实验帮助读者在实践中巩固知识。
一、全书章节概览
本书共 12 章,大致可分为三大部分:
| 部分 | 章节 | 核心内容 |
|---|---|---|
| 第一部分 | 第1-6章 | 程序结构和执行:数据表示、汇编、处理器架构、性能优化、存储器层次结构 |
| 第二部分 | 第7-9章 | 在系统上运行程序:链接、异常控制流、虚拟内存 |
| 第三部分 | 第10-12章 | 程序间的交互和通信:系统级 I/O、网络编程、并发编程 |
第一章:计算机系统漫游— 通过跟踪hello.c程序的生命周期,宏观了解计算机系统全貌。
第二章:信息的表示和处理— 深入讲解整数、浮点数的二进制表示与运算,是理解计算机底层运算的基础。
第三章:程序的机器级表示— 学习汇编语言与机器代码,理解 C 语言程序在底层的执行方式。
第四章:处理器体系结构— 设计一个简单的处理器,理解指令执行的流水线机制。
第五章:优化程序性能— 学习代码优化技巧,理解编译器优化能力与局限。
第六章:存储器层次结构— 深入理解缓存(Cache)的工作机制与程序局部性原理。
第七章:链接— 学习静态链接、动态链接、符号解析、重定位等核心概念。
第八章:异常控制流— 理解进程、异常、信号、非本地跳转等机制。
第九章:虚拟内存— 深入剖析虚拟地址到物理地址的映射机制,理解分页、缺页处理等。
第十章:系统级 I/O— 学习 Unix I/O 模型与文件操作。
第十一章:网络编程— 了解网络编程基础、客户端-服务器模型。
第十二章:并发编程— 学习线程、同步、并发 bug 等并发编程核心概念。
二、关键代码示例
1. 程序编译的四个阶段示例
以下是完整的hello.c程序以及各个阶段的输出示例:
// hello.c#include<stdio.h>intmain(){printf("hello, world\n");return0;}预处理阶段(.i 文件):
// hello.i (部分示意)#1"hello.c"#1"<built-in>"#1"hello.c"externintprintf(constchar*__restrict __format,...);intmain(){printf("hello, world\n");return0;}汇编阶段(.s 文件):
; hello.s main: subq $8, %rsp movl $.LCO, %edi call puts movl $0, %eax addq $8, %rsp ret .LCO: .string "hello, world"编译命令:
# 完整编译gcc-ohello hello.c# 分步执行四个阶段gcc-Ehello.c-ohello.i# 预处理gcc-Shello.i-ohello.s# 编译gcc-chello.s-ohello.o# 汇编gcc hello.o-ohello# 链接编译系统由预处理器(cpp)、编译器(ccl)、汇编器(as)、链接器(ld)四个程序构成。
2. 大端/小端存储示例
// 字节序检测程序#include<stdio.h>typedefunsignedchar*byte_pointer;voidshow_bytes(byte_pointer start,size_tlen){size_ti;for(i=0;i<len;i++)printf("%.2x ",start[i]);printf("\n");}voidshow_int(intx){show_bytes((byte_pointer)&x,sizeof(int));}intmain(){intval=0x01234567;printf("int 0x01234567 在内存中的字节序: ");show_int(val);return0;}- 小端存储(如 Intel x86):最低有效字节在前,输出为
67 45 23 01 - 大端存储(如某些 IBM 系统):最高有效字节在前,输出为
01 23 45 67
三、架构图与流程图
1. 编译系统流程图
┌─────────────────────────────────────────────────────────────────────┐ │ 编译系统流程图 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ hello.c ──→ 预处理器 ──→ hello.i ──→ 编译器 ──→ hello.s │ │ (文本) (cpp) (文本) (ccl) (汇编) │ │ │ │ │ ↓ │ │ hello ──←─────── 链接器 ──←─────── hello.o ──←─── 汇编器 │ │ (可执行) (ld) (目标文件) (as) │ │ │ │ 阶段说明: │ │ ▸ 预处理:处理 #include、#define 等预处理指令 │ │ ▸ 编译:C 语言 → 汇编语言 │ │ ▸ 汇编:汇编语言 → 机器指令(二进制) │ │ ▸ 链接:合并目标文件及库 → 可执行文件 │ │ │ └─────────────────────────────────────────────────────────────────────┘2. 典型系统硬件组成
┌─────────────────────────────────────────────────────────────────────┐ │ 典型系统硬件组成架构图 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ CPU ┌──────────────────────────────────────────────────┐ │ │ │ │ 寄存器文件 │ │ │ │ └──────────────────────────────────────────────────┘ │ │ │ ▲ │ │ │ │ │ ↓ │ │ │ ┌──────────────────────────────────────────────────┐ │ │ │ │ 算术/逻辑单元 (ALU) │ │ │ │ └──────────────────────────────────────────────────┘ │ │ │ ▲ │ │ │ │ │ ↓ │ │ └─────────┼──────────────────────┼───────────────────────────────┘ │ │ │ │ ↓ ↓ │ ┌─────────────────┐ ┌─────────────────┐ │ │ 高速缓存 │◄────►│ 主存 │ │ │ (Cache) │ │ (DRAM) │ │ └─────────────────┘ └─────────────────┘ │ ▲ ▲ │ │ │ │ └──────────┬───────────┘ │ │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ 系统总线 │ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ↓ ↓ ↓ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ I/O桥 │ │ 磁盘 │ │ 显示器 │ │ │ 控制器 │ │ 控制器 │ │ 适配器 │ │ └─────────┘ └─────────┘ └─────────┘ │ │ │ │ │ ↓ ↓ ↓ │ [键盘/鼠标] [磁盘] [显示器] │ └─────────────────────────────────────────────────────────────────────┘系统硬件包括总线、I/O 设备、主存(DRAM)和处理器。总线是贯穿整个系统的电子管道,通常设计为传送定长的字节块(字)。
3. 存储器层次结构
┌─────────────────────────────────────────────────────────────────────┐ │ 存储器层次结构图 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ L0: 寄存器 │ │ ↑ (1个周期) │ │ L1: L1 高速缓存 (SRAM) │ │ ↑ (2-4个周期) │ │ L2: L2 高速缓存 (SRAM) │ │ ↑ (10-20个周期) │ │ L3: L3 高速缓存 (SRAM) │ │ ↑ (50-100个周期) │ │ L4: 主存 (DRAM) │ │ ↑ (数百个周期) │ │ L5: 本地磁盘 │ │ ↑ (数千万个周期) │ │ L6: 远程存储(网络) │ │ │ │ 核心思想:每一层作为下一层的"高速缓存",速度递减、容量递增 │ │ │ └─────────────────────────────────────────────────────────────────────┘存储器层次结构的设计核心是局部性原理(时间局部性和空间局部性),将更小更快的设备作为下一层的缓存。
4. 计算机系统抽象层次
┌─────────────────────────────────────────────────────────────────────┐ │ 计算机系统抽象层次 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ 应用程序 │ │ ↑ │ │ │ 软件 │ │ ↓ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ 操作系统 │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ │ │ │ │ 进程 │ │ 虚拟内存 │ │ 文件 │ │ │ │ │ │ (处理器抽象) │ │ (内存抽象) │ │ (I/O设备抽象) │ │ │ │ │ └─────────────┘ └─────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ ↑ │ │ │ 硬件 │ │ ↓ │ │ 计算机硬件(处理器、内存、I/O设备) │ │ │ │ 抽象对应关系: │ │ ▸ 文件 → I/O 设备的抽象表示 │ │ ▸ 虚拟内存 → 主存和磁盘 I/O 设备的抽象表示 │ │ ▸ 进程 → 处理器、主存和 I/O 设备的抽象表示 │ │ ▸ 指令集架构 → 实际处理器的抽象 │ │ │ └─────────────────────────────────────────────────────────────────────┘操作系统通过进程、虚拟内存和文件等抽象概念实现两大功能:防止硬件被失控应用程序滥用,并为应用提供统一的硬件控制接口。
四、第一章预告:计算机系统漫游
1.1 信息就是位 + 上下文
核心思想:计算机系统中的所有信息——包括磁盘文件、内存中的程序、用户数据和网络数据——都是由一串比特(bit)表示的。区分不同数据对象的唯一方法是读取时的上下文。
例如,相同的二进制序列1101001:
- 作为字符读取:ASCII 码 105 → 字符
i - 作为整数读取:十进制的 105
┌─────────────────────────────────────────────────────────────────────┐ │ 信息 = 位 + 上下文 示意图 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ 二进制比特序列: 1101001 │ │ │ │ │ ┌─────────────┼─────────────┐ │ │ ↓ ↓ ↓ │ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │ │ ASCII 解码 │ │ 整数解码 │ │ 指令解码 │ │ │ │ 上下文 │ │ 上下文 │ │ 上下文 │ │ │ └────────────┘ └────────────┘ └────────────┘ │ │ ↓ ↓ ↓ │ │ 字符 'i' 整数 105 机器指令 │ │ │ │ → 同样的比特流,在不同的上下文中呈现出完全不同的含义! │ │ │ └─────────────────────────────────────────────────────────────────────┘1.2 程序被其他程序翻译成不同格式
hello.c从源文件到可执行文件的转换过程:
# 编译命令unix>gcc-ohello hello.c┌─────────────────────────────────────────────────────────────────────┐ │ hello.c (文本) │ │ │ │ │ ↓ 预处理器 (cpp) - 处理 #include 等预处理指令 │ │ hello.i (文本) │ │ │ │ │ ↓ 编译器 (ccl) - 翻译成汇编语言 │ │ hello.s (汇编文本) │ │ │ │ │ ↓ 汇编器 (as) - 翻译成机器指令 │ │ hello.o (二进制目标文件) │ │ │ │ │ ↓ 链接器 (ld) - 合并 printf.o 等库文件 │ │ hello (可执行二进制文件) │ └─────────────────────────────────────────────────────────────────────┘1.3 处理器读并解释存储在主存中的指令
典型硬件组成
| 组件 | 功能描述 |
|---|---|
| 总线 | 贯穿系统的电子管道,在各部件间传递信息字节 |
| I/O 设备 | 系统与外界的通道(键盘、鼠标、显示器、磁盘) |
| 主存 | 临时存储设备,存放程序和程序处理的数据,逻辑上是线性的字节数组 |
| 处理器(CPU) | 解释/执行主存指令的引擎,核心是程序计数器(PC) |
| 高速缓存 | 更小更快的存储设备,作为处理器与主存之间的缓存层 |
程序执行流程
┌─────────────────────────────────────────────────────────────────────┐ │ hello 程序执行流程 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ 1. shell 读取命令行 "hello" │ │ ↓ │ │ 2. shell 调用加载器,将 hello 文件中的代码和数据复制到主存 │ │ ↓ │ │ 3. 处理器开始执行 main 程序中的机器语言指令 │ │ ↓ │ │ 4. 指令将 "hello, world\n" 字符串从主存复制到寄存器 │ │ ↓ │ │ 5. 寄存器将字符串复制到显示设备(经过存储器映射 I/O) │ │ ↓ │ │ 6. 屏幕上显示 "hello, world" │ │ │ │ 💡 关键技术:DMA(直接存储器存取),数据可直接从磁盘到达主存, │ │ 无需经过处理器 │ │ │ └─────────────────────────────────────────────────────────────────────┘1.4 操作系统管理硬件
操作系统实现两个基本功能:
- 防止硬件被失控的应用程序滥用
- 向应用程序提供简单一致的硬件控制机制
操作系统通过三个基本抽象实现这些功能:
| 抽象概念 | 抽象的对象 | 说明 |
|---|---|---|
| 文件 | I/O 设备 | 将磁盘、键盘等设备统一抽象为文件 |
| 虚拟内存 | 主存 + 磁盘 I/O | 为每个进程提供独占主存的假象 |
| 进程 | 处理器 + 主存 + I/O 设备 | 操作系统对正在运行的程序的一种抽象 |
1.5 Amdahl 定律与并发并行
Amdahl 定律:当我们对系统的某个部分进行加速时,其对系统整体性能的影响取决于该部分的重要性与加速程度。这提醒我们优化应聚焦于关键瓶颈。
并发与并行的区别:
- 并发:逻辑上的同时,强调一段时间内处理多个任务
- 并行:物理上的同时,强调多核处理器真正同时执行
1.6 本章学习收获
第一章的核心价值在于建立计算机系统的宏观认知,包括:
- ✅ 程序的整个生命周期(编写 → 编译 → 链接 → 加载 → 执行)
- ✅ 编译系统的四个阶段及各阶段作用
- ✅ 系统硬件的核心组成(总线、I/O 设备、主存、处理器、高速缓存)
- ✅ 存储层次结构与局部性原理
- ✅ 操作系统提供的抽象层(进程、虚拟内存、文件)
- ✅ Amdahl 定律与并发并行基本概念
这些概念将在后续章节中深入展开。本书从上层程序出发,由上至下剖析和串联了每一个环节,帮助读者构建完整的计算机系统知识体系。
五、学习建议
1. 学习资源汇总
| 资源类型 | 推荐内容 |
|---|---|
| 官方教材 | 《深入理解计算机系统》第 3 版 |
| 在线课程 | CMU 15-213(本书作者授课的原版课程) |
| 配套实验 | Data Lab、Bomb Lab、Attack Lab、Cache Lab、Shell Lab、Malloc Lab、Proxy Lab 等 7 个经典实验 |
| 开源代码 | GitHub 上的csapp实验代码库(包含简易 shell 程序、动态内存分配器等) |
2. 学习路线
第一阶段:打好基础 ├── 第1章:计算机系统漫游(建立宏观概念) ├── 第2章:信息的表示和处理(二进制基础) └── 第3章:程序的机器级表示(汇编基础) 第二阶段:深入底层 ├── 第4章:处理器体系结构 ├── 第5章:优化程序性能 └── 第6章:存储器层次结构 + Cache Lab 第三阶段:系统核心 ├── 第7章:链接 ├── 第8章:异常控制流 + Shell Lab ├── 第9章:虚拟内存 + Malloc Lab 第四阶段:应用拓展 ├── 第10章:系统级 I/O ├── 第11章:网络编程 + Proxy Lab └── 第12章:并发编程3. 实验推荐顺序
- Data Lab— 熟悉整数和浮点数的位级表示(入门级)
- Bomb Lab— 学习汇编语言和 GDB 调试
- Attack Lab— 理解代码注入和缓冲区溢出攻击
- Cache Lab— 深入理解缓存机制
- Shell Lab— 实现简易 shell 程序,学习进程管理和信号处理
- Malloc Lab— 实现动态内存分配器
- Proxy Lab— 实现 HTTP 代理服务器
阅读建议:本书每章前言都会从程序员角度分析"为什么要学这些知识",然后正文清晰地阐述答案,建议认真阅读每章前言。
六、总结
《深入理解计算机系统》是一本将理论与实践完美结合的计算机系统经典著作。通过阅读本书和完成配套实验,你将能够:
- 🎯 构建完整的计算机系统知识体系
- 🎯 编写更高效、更安全、更可移植的程序
- 🎯 快速定位和解决程序中的性能问题
- 🎯 理解编译、链接、加载等底层机制
- 🎯 掌握网络编程和并发编程的核心技术
后续章节将逐步深入,敬请期待!
本文为个人学习笔记,仅用于知识分享。如有错误,欢迎指正。
👍🏻 点赞 + 收藏 + 分享,让更多开发者看到这篇深度解析!❤️ 如果觉得有用,请给个赞支持一下作者!
