labs - and - drills

Labs and Drills:四大件可操作练习

这份文件补足一个关键问题:只读文章不够,读者需要能自己观察现象。

这里的练习不绑定某个语言栈,也不要求复杂环境。每个练习都按同一结构写:

目标
观察点
操作
预期现象
反思问题
对应章节

0. 怎么做这些练习

建议每个练习都写一份简短记录:

练习名称:
我的输入:
观察到的现象:
我以为会怎样:
实际为什么这样:
对应的底层机制:

这会让学习从“看懂”变成“能解释”。

Lab 01:同一段 bit 的不同解释

项目内容
目标理解 bit 本身没有意义,意义来自解释规则
观察点同一段字节可以解释成整数、字符、指令或格式字段
操作选一段二进制模式,例如 01000001,分别按字符、无符号整数、有符号整数解释
预期现象同一段 bit 得到不同含义
对应章节00-cser-foundations.mdcomputer-organization/01-binary-data-cpu-memory.md

反思问题:

  1. 数据类型到底保存了什么信息?
  2. 文件格式和网络协议为什么必须定义字段解释规则?
  3. 为什么乱码、解析失败、协议错位本质上都是解释规则错了?

Lab 02:整数溢出的边界

项目内容
目标观察固定位宽整数的边界行为
观察点最大值再加 1 后,位模式如何变化
操作选择 8 位或 32 位整数模型,手算最大值附近的加法
预期现象超出范围后按固定位宽截断,解释成有符号数时可能变负
对应章节computer-organization/02-integer-complement-overflow.md

反思问题:

  1. 为什么机器整数不是数学整数?
  2. 为什么长度、容量、偏移量计算要警惕溢出?
  3. 为什么边界测试必须包含最大值和接近最大值的组合?

Lab 03:浮点误差观察

项目内容
目标理解二进制浮点近似
观察点十进制小数转成二进制后可能无法精确表示
操作0.10.20.3,比较十进制期望和二进制浮点近似结果
预期现象直接相等比较可能失败
对应章节computer-organization/03-floating-point.md

反思问题:

  1. 为什么不能直接用浮点做精确相等判断?
  2. 什么场景可以接受 epsilon?
  3. 什么场景应该使用整数化单位或定点表示?

Lab 04:顺序访问 vs 随机访问

项目内容
目标观察局部性对性能的影响
观察点顺序访问更容易利用缓存行和预取
操作准备一批连续数据,分别顺序访问和随机访问,记录耗时差异
预期现象随机访问更容易慢,即使操作数量相同
对应章节computer-organization/06-memory-hierarchy.mdcomputer-organization/07-cache.md

反思问题:

  1. 为什么复杂度一样,速度仍然不同?
  2. 数据结构的内存布局如何影响缓存命中?
  3. 为什么链式结构灵活,但可能不如连续结构缓存友好?

Lab 05:动态数组扩容抖动

项目内容
目标观察摊还复杂度和单次抖动的区别
观察点多数插入便宜,扩容那一次需要复制大量元素
操作模拟动态数组容量从 1、2、4、8 增长,每次扩容记录复制数量
预期现象平均成本低,但某些插入成本高
对应章节data-structures/02-array-dynamic-array.md

反思问题:

  1. 摊还 O(1) 为什么不等于每次 O(1)?
  2. 什么时候需要预估容量?
  3. 为什么尾延迟比平均值更敏感?

Lab 06:哈希冲突和扩容

项目内容
目标理解哈希表平均 O(1) 的条件
观察点冲突、负载因子、rehash 会影响性能
操作构造多个 key 映射到同一桶,观察查找链路;再模拟扩容后重新分布
预期现象冲突多时查找退化,扩容时发生集中成本
对应章节data-structures/05-hash-table.md

反思问题:

  1. 哈希函数质量为什么重要?
  2. 为什么负载因子不能无限增大?
  3. 为什么恶意 key 可能攻击哈希表性能?

Lab 07:队列堆积和反压

项目内容
目标理解队列只能吸收短期速度差
观察点生产速度大于消费速度时,队列长度持续增长
操作设定生产者每秒放入 100 个任务,消费者每秒处理 80 个任务,计算队列增长
预期现象队列越来越长,等待时间越来越高
对应章节data-structures/04-stack-queue.mdoperating-systems/11-performance-debugging.md

反思问题:

  1. 为什么队列不是性能瓶颈的最终解法?
  2. 什么时候需要限流、丢弃、降级或扩容消费者?
  3. 重试如何进一步放大队列压力?

Lab 08:进程状态和等待

项目内容
目标区分运行、就绪、阻塞和等待
观察点CPU 不高但程序慢,通常说明程序在等
操作画一个任务从运行到阻塞 IO,再被唤醒继续运行的状态图
预期现象任务生命周期不是连续占用 CPU
对应章节operating-systems/01-process-thread-cpu.md

反思问题:

  1. 为什么 CPU 低不代表系统没问题?
  2. 谁负责把阻塞任务唤醒?
  3. 为什么大量短任务会增加调度成本?

Lab 09:死锁等待图

项目内容
目标通过等待图识别死锁
观察点等待关系出现环
操作画两个任务、两把锁:任务 A 持有锁 1 等锁 2,任务 B 持有锁 2 等锁 1
预期现象等待图有环,两个任务都无法推进
对应章节operating-systems/04-concurrency-sync.mdfoundations/08-concurrency-time-order.md

反思问题:

  1. 死锁的四个必要条件是什么?
  2. 为什么统一加锁顺序能降低死锁风险?
  3. 超时和回滚能解决什么,又会带来什么复杂度?

Lab 10:write 成功和持久化完成的区别

项目内容
目标理解文件写入路径和崩溃窗口
观察点write 返回不等于数据一定落到持久介质
操作画出应用缓冲、内核页缓存、文件系统、块设备、存储介质的路径
预期现象每一层都可能暂存数据
对应章节operating-systems/05-file-system.mdcomputer-organization/08-bus-io.md

反思问题:

  1. 为什么崩溃一致性需要额外设计?
  2. 原子替换和日志分别解决什么?
  3. 什么时候必须关心刷盘?

Lab 11:网络请求阶段拆分

项目内容
目标把“请求慢”拆成阶段
观察点DNS、TCP、TLS、HTTP、下载可能分别变慢
操作对一次 HTTPS 请求画阶段时间线
预期现象慢点落在哪一段,排障方向不同
对应章节computer-networks/00-from-zero.mdcomputer-networks/12-network-troubleshooting.md

反思问题:

  1. connect timeout 和 read timeout 区别是什么?
  2. TLS 失败为什么不一定有应用日志?
  3. 502/504 为什么可能来自网关而不是应用?

Lab 12:TCP 字节流没有消息边界

项目内容
目标理解 TCP 是字节流
观察点多次 write 不等于多次 read
操作设计一个协议,如果没有长度字段或分隔符,接收方如何判断消息结束
预期现象应用必须自己定义消息边界
对应章节computer-networks/06-tcp-basics.md

反思问题:

  1. 粘包和拆包为什么不是 TCP bug?
  2. 固定长度、分隔符、长度字段各有什么问题?
  3. 为什么 HTTP 需要 Content-Length 或 chunked?

Lab 13:DNS TTL 变更延迟

项目内容
目标理解 DNS 缓存和 TTL
观察点权威记录变化不代表所有客户端立即变化
操作假设旧记录 TTL 为 24 小时,画出不同解析器缓存过期时间
预期现象一段时间内新旧结果共存
对应章节computer-networks/08-dns.md

反思问题:

  1. 为什么重大切换前要提前降低 TTL?
  2. 为什么程序环境和手动测试环境解析结果可能不同?
  3. DNS 负载均衡为什么不是实时健康调度?

Lab 14:证据链复盘

项目内容
目标训练从症状到证据的排障方法
观察点每个结论都必须有证据支持
操作practical-casebook.md 任选一个 case,写出失败边界、路径图、证据、假设、验证方式
预期现象模糊问题变成可推进的问题
对应章节foundations/09-debugging-observability.md

反思问题:

  1. 我有没有把猜测当结论?
  2. 哪个证据能推翻我的假设?
  3. 修复后怎样证明真的解决了原问题?

练习顺序建议

阶段练习
表示与计算Lab 01、02、03
性能直觉Lab 04、05、06、07
OS 与并发Lab 08、09、10
网络与排障Lab 11、12、13、14

真正学会四大件,不是能复述定义,而是能通过这些练习把现象、机制和证据连起来。

延伸阅读