计算机体系结构 05:从逻辑到状态
从逻辑到状态:一块 ALU 为什么还需要时钟 中心问题:只靠一张真值表能不能得到一台会执行程序的机器? 答案是否定的。真值表和组合逻辑能说明“给定输入会产生什么输出”,但程序执行还需要“什么时候保存这个输出”。组合逻辑负责计算,寄存器负责记住,时钟把“旧状态参与计算”和“新状态进入机器”分成两个时刻。没有这个分界,CPU 里的寄存器、PC、内存写回都会变成一团互相追赶的信号。 本篇只讨论一条最短的桥:布尔函数怎样变成小型 ALU,ALU 输出怎样进入寄存器,为什么最长组合路径决定时钟周期。证据等级是“手算”和“功能执行”。功能执行指 Python 脚本对 4 位 ALU 穷举断言并保存输出;时序部分只是静态延迟手算,不是周期仿真。这里没有 RTL、综合报告、FPGA 上板或真实 CPU 测量。 边界与依据 RISC-V 官方 ISA Specifications 20260120 的 RV32I 章节说明:RV32I 有 32 个 32 位 x 寄存器,x0 恒为 0,另有 pc 保存当前指令地址;R 型指令从 rs1、rs2 读数并向 rd 写结果,ADD/SUB/AND/OR...
分布式系统 05:时钟与因果偏序
服务 P 修改一条记录,再发消息通知服务 Q;Q 收到通知后更新索引。两份日志的墙上时间却显示,索引更新比记录修改早了五毫秒。这个排序能证明系统违反了业务顺序吗?不能。机器时钟可能有偏差,但消息接收必须晚于相应发送,这条约束来自通信过程。 上一篇的网络 KV 与持久化恢复处理单机服务的请求顺序和故障恢复。跨越进程之后,一条本地日志不再包含全部事件,恢复出来的记录也未必足以比较另一台机器的操作。本篇从事件和消息定义因果偏序,用 Lamport 时钟保持这个偏序,再用向量时钟识别模型内的并发。物理时间仍有用途,只是它需要另一组假设。 顺序从哪些事实产生 假定系统有固定的三个进程 P、Q、R。每个进程内部的事件按一个顺序执行,跨进程的影响全部通过显式消息传递;发送和接收是不同事件。消息可以延迟和乱序,讨论到的接收事件都有对应发送。进程不重启回退计数器,不复用身份,整数不溢出。若应用通过共享数据库等其他途径通信,也必须把那条路径纳入模型。 在这个模型中,a → b 表示 a happens-before b,定义来自三条规则:同一进程上先发生的事件在后发生事件之前;消息发送在相应接收之前...
分布式系统 04:从网络 KV 到持久化恢复
服务端已经把 x=two 写入文件,并完成同步。返回客户端的连接随即关闭,客户端收到错误。服务进程被终止以后,新的进程应该恢复出什么值?客户端用原来的请求 ID 重试,应该重新写一遍,还是取回上次结果? 第 01 篇的内存去重在重启后会丢失。本篇把业务修改、请求身份和返回结果放进同一条追加日志,再用真实 HTTP 连接和子进程终止验证恢复行为。第 03 篇 讨论 GFS 写入路径中的多个副本;这里暂时收窄到一台机器上的一个服务进程,把单副本自身必须兑现的承诺讲清楚。 网络接口需要先定义可观察语义 这个 KV 服务提供两类请求。GET /kv?key=x 查询当前值;POST /kv 接收包含 ID、Key、Value 的 JSON,将一个键设为指定字符串。写入返回原请求与全局递增的 Revision。首次成功修改获得一个新 revision,同 ID 同参数重试返回第一次的记录,不推进 revision。 revision 在这里仅是单个日志中的序号。它既不是物理时间,也不是跨节点一致性协议里的任期,更不能当作全局分布式事务版本。GET 返回当前值与存储的全局 revision。...
分布式系统 03:GFS 写入路径与副本边界
多个工作者可以重做同一份计算,保存计算结果的文件系统却必须回答另一组问题:数据放在哪些机器上,谁决定并发写入的先后,一次写入只有部分副本完成时能否读取,以及客户端收到成功究竟意味着什么。 上一篇:MapReduce 任务重试与输出提交区分了执行与发布。GFS 把这种区分推进到存储层:网络已经传完数据、某个副本已经改动文件、整个请求已经得到成功响应,是三个不同的时刻。混淆它们,会把失败后的残留内容当成已提交结果。 讨论对象是 Ghemawat、Gobioff 和 Leung 在 SOSP 2003 发表的 GFS。MIT 6.5840 Spring 2026 把它放在 MapReduce、RPC 之后,用它连接分片、复制与故障恢复。本篇的 Go 程序只执行有限内存时间线,不提供 GFS API,也没有真实的磁盘复制、租约时钟或网络传输。 从工作负载确定系统模型 2003 年 GFS 面向大型数据处理。文件较大,追加和顺序读取常见,系统需要聚合带宽,并把机器故障视为日常条件。这与任意程序都能直接使用的通用文件系统有距离。应用可以配合存储接口,接受校验记录、过滤重复等额外责任。原论文 ...
分布式系统 02:MapReduce 任务重试与输出提交
一批输入有十八条记录。某个工作者写出半个结果文件后退出,调度器把同一份输入交给另一个进程。第二个进程成功了,最终结果能否保证每条记录只出现一次?如果第一个进程没有退出,只是回复得晚,它又该如何处理自己的结果? 上一篇的 RPC 重试与请求去重讨论了一次调用可能执行多次。批处理框架面对相同的不确定性,却有一个有利条件:许多任务可以从固定输入重新计算。代价是框架必须规定哪一次执行的输出能进入下游,不能把工作者留下的所有文件直接拼起来。 MapReduce 把这件事与任务切分、数据分组、调度放进一个计算框架。本文沿用 2004 年论文的模型讨论保证,再用独立 Go 实验检查进程被杀和旧 attempt 晚到的发布边界。实验有真实子进程和文件操作,不连接分布式存储,不模拟成生产集群。 先确定计算结果是什么 输入记录为 (ID, key),ID 从 0 到 17,key 表示 ID 的奇偶性。目标是按 key 收集 ID,并在每组内排序。正确结果只有两组:偶数组包含 0,2,...,16,奇数组包含 1,3,...,17。这个任务很小,但能检查重试后是否多算、漏算或分错组。 将输入按 ID...
分布式系统 01:RPC 重试、并发与请求去重
计数器最初为 0。客户端调用 Add(1),服务端把计数器改成 1,回复却在返回途中丢失。客户端等待超时,再调用一次,计数器变成 2。网络只丢了一条回复,业务却多执行了一次。 第 00 篇 已经区分“执行失败”与“没有观察到结果”。RPC 将这个区别带进接口设计:同一项业务操作可以对应多次网络尝试,接口必须说明如何识别它们,以及服务重启之后还保留什么证据。 RPC 隐藏了传输,没有消除失败 一次远程调用通常经过参数编码、请求传输、服务端解码与分发、业务执行、结果编码、回复传输和客户端解码。正常路径看起来接近函数调用,失败路径却多了两个独立进程和两个消息方向。 如果本地进程中的普通函数返回,调用者通常可以直接使用返回值。远程请求还可能卡在消息队列、连接缓冲区或服务端执行队列里;客户端停止等待时,服务端未必停止工作。客户端看到的超时,不能反推出业务修改已经回滚。MIT 2026 RPC 讲义把这种“不知道请求是否到达或执行”的情况作为 RPC 故障语义的起点。 TCP 提供的连接内字节流可靠性也不足以解决这个问题。服务端可以先提交业务,再在回复到达客户端之前崩溃;客户端重连之后,即使...
分布式系统 00:系统模型、故障时间线与先修自测
客户端发出 Put(x, 1),等待超时。服务端究竟有没有修改 x?只凭这条错误,无法判定。请求可能没到,也可能已经执行,只是回复没有回来。工程上需要重试、查询或人工核对,理论上需要先写清楚:哪些事件发生了,谁能观察到这些事件,协议允许哪些执行。 分布式系统的学习从这个区别开始。运行在不同进程里的代码通过消息合作,每个进程只掌握局部状态;一项业务动作可以跨越多个故障边界。正确性不能只根据正常路径上的最终输出判断。 这是系列的入口篇。验收目标是根据三个节点和客户端的时间线,区分确定发生、仍有可能和无法判定的结果;配套 Go 程序重放有限故障组合,不实现共识协议。 从课程结构到实验路径 MIT 6.5840 Spring 2026把 RPC、存储和复制实验逐步连起来;Stanford CS244B Spring 2024更突出原论文讨论与研究。两份课表覆盖事务、分布式计算和信任模型,学习范围远大于 Paxos、Raft、ZooKeeper 与 etcd。 系列按先修关系重新排列:先获得分析请求、状态和故障的语言,再实现任务调度、KV、复制与分片,随后讨论协调服务、事务、缓存、计算系统...
计算机网络 25:少一次握手付出了什么,会话恢复、0-RTT 与重放边界
第二次 TLS 连接显示 Reused,是否意味着没有握手?客户端指定了早期数据文件,是否意味着服务器已经接受它?恢复成功、早期数据被接受、应用处理完成,是三个需要分别观察的结果。 第 24 篇建立了证书与服务身份验证。本篇保留专用测试 CA,在新建连接之间传递会话材料,比较完整握手、普通恢复和带早期数据的恢复。实验只运行本机 OpenSSL,不访问真实业务接口。 恢复的是安全上下文,不是旧 socket RFC 8446 §2.2允许从先前连接建立恢复所用的预共享密钥关系。后续连接可以提出相应 PSK 身份,服务器接受后,新连接的安全上下文与先前认证建立的上下文相联系。 客户端仍然新建连接、发送 ClientHello 并处理服务器响应。它不是继续使用旧 TCP 序号空间,也不是把旧的加密字节直接接到新连接上。普通恢复不能只凭“第二次访问”或“耗时较小”判断,必须检查实现实际选择了何种握手路径。 PSK 可以结合新的临时密钥交换,也可以按协议允许的其他模式使用。是否包含新的共享秘密会影响前向保密性质,不能把所有恢复统称为“安全属性与完整证书握手完全一致”。本篇只报告所选实现的结...
计算机网络 24:TLS 如何确认通信对象,TLS 1.3、证书链、主机名、密钥与记录
TCP 已经连通,收到的证书也能解析,为什么 TLS 仍可能拒绝连接?证书内容可读、签名可验证、签发者可信、服务名字匹配,是不同条件。只证明其中一个,不能替代其他条件。 前置是第 16 篇连接状态。本篇把连接地址与预期服务身份分开,用专用测试 CA 验证三个场景:正常连接、主机名不匹配、不信任签发者。实验不向系统安装 CA,不访问公共 HTTPS 网站;会话恢复与早期数据留到第 25 篇。 先确定要连接谁,再检查它提供的身份 假设应用要连接 server.lab.test,测试中它的传输地址固定为 127.0.0.1。IP 地址告诉 socket 连接到哪里,预期名字告诉证书验证要匹配谁。连接到回环地址不要求证书必须含该 IP,前提是应用的参考身份确实是配置好的 DNS 名,并按该名字验证。 RFC 9525 §6.1要求客户端独立构造可接受的参考身份,不能拿服务端刚给出的证书名字作为预期值,再宣布它与自己匹配。否则任意证书都可能通过这项检查。 服务端证书的 subjectAltName(SAN)可以包含 DNS 名或 IP 地址。DNS 身份与 dNSName 匹配,直接 IP...
计算机网络 23:缓存返回的还是同一份资源吗,新鲜度、验证、Vary 与条件请求
同一个 URL 连续返回 200,能否证明每次都访问了源站?过期后收到 304,是否意味着正文为空?把响应状态、正文来源和缓存决策混在一起,会同时误判这两个问题。缓存可以直接生成带正文的 200,也可以在上游验证成功后继续使用已有正文。 第 22 篇解决消息边界。本篇在两个回环 HTTP 服务之间加入受限教学缓存,分别记录客户端、缓存与源站的交换,研究新鲜度、验证器和表示变体。实验不使用浏览器缓存或公共 CDN。 同一资源可以有不同表示 URL 标识目标资源,不保证每次选中的表示逐字节相同。请求头、协商条件以及资源随时间的变化都可能影响结果。例如同一路径按 Accept-Language 返回中文或英文;只按路径保存一份正文,会让后来的另一种语言请求取得错误变体。 缓存保存的对象也不仅是正文。后续复用至少需要关联请求条件、响应字段、验证器和时间信息。丢失 Vary 信息会混用变体;丢失 Date 与 Age 会误算有效期;收到 304 后丢失旧正文则无法构造完整响应。 这里分开回答两个问题:当前请求能匹配哪份已存响应,以及这份响应能否直接复用。变体匹配正确,不等于新鲜;时间尚新鲜...





