计算机网络 02:socket 读写的单位与消息边界
客户端先发送 one,再发送 two。服务端调用一次 recv(1024),应该得到哪一条消息?如果答案必须是其中一条,应用就已经假定 TCP 会保留写入边界。这个假定不成立:TCP 向应用提供有序字节流,应用必须自行规定消息从哪里开始、在哪里结束。 第 00 篇用固定十字节回显区分发送返回与对端读取。本篇把负载改成变长消息,设计一个四字节长度头协议,并用主动切分的输入检验解析器。实验同时包含真实回环连接,但不会把多次 sendall 当成必然产生相同次数 recv 的依据。 字节流中没有一次写入的标记 RFC 9293 §2.2定义了可靠、有序字节流服务。对一个方向,应用写入 abc 后再写入 def,接收端看到的字节顺序应为 abcdef;一次读取可能得到一个字节、三个字节或六个字节。这里讨论的是无错误、最终成功交付时的顺序,不表示读取永远不会超时或连接不会失败。 传输实现可以把应用数据分成 TCP 段,也可以把多个段接收的数据交给一次应用读取。应用、TCP 分段、IP 包和链路帧属于不同边界。抓包里看到两个 TCP 段,无法要求服务端恰好调用两次 recv 才读完它们。 ...
计算机网络 01:带宽高为什么仍然慢
一条链路标注 100 Mbit/s,另一个连接只能达到 10 Mbit/s。下载同一个大文件时,前者可能更快;只发送一个很短的请求时,两者的完成时间却可能接近。带宽描述单位时间内能够传输多少比特,请求完成时间还包含传播、处理、排队以及应用等待。仅知道一个速率,无法算出一次请求的总耗时。 第 00 篇已经区分发送接口返回、对端读取与应用回复。本篇把这些事件之间的时间拆开,先给一个能手算的存储转发模型,再改变报文数和到达间隔,观察排队怎样出现。所有例子中的速率、距离和时延都是模型输入,不是测得的公网性能。 先固定长度与时间的口径 设要发送的比特数为 L,链路串行发送速率为 R bit/s。一个比特占用发送器的时间是 1/R 秒,L 个比特依次进入链路需要 L/R 秒,这一项称为发送时延,也常称为串行化时延。 给定 L = 1500 × 8 = 12000 bit,R = 10,000,000 bit/s,得到发送时延 0.0012 s,即 1.2 ms。这里的 1500 字节被定义为模型每个报文的全部发送量。如果实际场景把 1500 字节定义为 IP 包长,链路首部、帧校验、前导码...
计算机网络 00:一次请求的路径与先修自测
程序执行 sendall(b"network-00"),没有异常,随后打印“发送成功”。此时能否确定服务端已经读取这十个字节?能否确定它已经执行请求,甚至把结果写入磁盘? 这几个问题需要不同的观察点。发送接口返回、对端传输层接收、对端应用读取、业务处理完成,分别对应请求路径上的不同事件。即使它们在一次运行中相隔很短,也不能用前一个事件代替后一个事件的证据。 本篇从一个只监听 127.0.0.1 的回显服务开始。客户端发送固定十字节,服务端读取后原样返回,客户端再逐字节核对。实验只依赖 Python 标准库,不需要真实账号、外部网站或网络管理权限。前置知识是基本程序、整数、字节数组和函数调用;二进制、网络字节序及并发顺序在后文自测。 从监听端口到一条连接 IP 地址定位通信端点所在的网络接口或主机语境,端口进一步区分传输层端点。实验中的 127.0.0.1 是 IPv4 回环地址,连接在本机完成。客户端和服务端可以运行在不同进程里,也可以像附件一样处于同一进程的不同线程。把两者放入一个进程只是方便启动、同步和清理,并没有把 socket 交换替换成函数直接返回...
计算机体系结构 00:一次数组求和经过哪些层
计算机体系结构 00:一次数组求和经过哪些层 一个程序从源代码变成结果,中间至少经过四层问题。ISA 规定软件能看见的寄存器、指令和内存效果;微架构决定这些指令怎样在一台具体处理器里重叠执行;实现工艺决定电路能以怎样的延迟、功耗和面积工作;系统软件负责把进程、虚拟地址、异常和设备组织成可用环境。把这四层混在一起,最常见的错误是用“CPU 很快”“缓存慢了”“编译器优化了”这种句子解释一切。 本篇只做入口:沿一个数组求和程序,把源代码、指令、硬件可见状态和结果检查分开标注。证据等级为“手算”“功能执行”。这里不声称得到流水线周期、缓存命中率或真实 CPU 的内部状态;第 07 篇的基础执行器只提交 RV32I 子集的架构状态,后续流水线和缓存章节再加入时序模型。 问题:同一个结果来自哪几种机制 示例工作负载是把 5 个 32 位整数相加: 12345uint32_t a[5] = {3, 4, 5, 6, 7};uint32_t sum = 0;for (size_t i = 0; i < 5; i++) { sum += a[i];...
计算机体系结构 01:比特怎样表示数
计算机体系结构 01:比特怎样表示数 同一串比特没有天然含义。0x3f800000 可以当作无符号整数 1065353216,也可以按 IEEE 风格单精度浮点格式解释为 1.0;0xffffffff 在无符号 32 位整数里是 4294967295,在二补码有符号解释里常被读作 -1。机器执行的是比特操作,程序语言和 ISA 给这些比特加上类型、宽度、对齐和舍入规则。 本篇回答三个问题:定宽整数为什么会回绕;字节序和对齐为什么影响内存解释;浮点加法为什么不能按实数代数随意换括号。证据等级为“手算”和“功能执行”。本篇的 C 程序只执行定义良好的 unsigned 运算、memcpy 对象表示复制、对齐查询和浮点分组;不执行有符号整数溢出。 整数:宽度先于意义 以 8 位为例,1111 1111 如果按无符号数解释,就是: 1128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = 255 如果按二补码有符号数解释,最高位权重是 -128,其余位仍为正: 1-128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = -1 同一比特串没有变,解释规则...
计算机体系结构 02:怎样比较两台机器
计算机体系结构 02:怎样比较两台机器 比较两台机器时,不能只问“主频多高”。一个程序的 CPU 时间可以粗略拆成: 12CPU 时间 = 指令数 × CPI × 时钟周期时间 = 指令数 × CPI ÷ 频率 这条式子不是万能测量工具,但能防止三类误判:指令少不一定快,CPI 低不一定快,频率高也不一定快。真正要比较的是同一工作负载、同一正确性条件、同一计时区间下的完成时间;再用指令数、CPI、缓存、分支和带宽解释原因。 本篇证据等级为“手算”和“真机测量”。本机实验使用 macOS 27.0、Apple M4 Pro、14 个逻辑 CPU、24 GiB 内存、Apple clang 21.0.0 和 C 程序重复计时;环境查询命令为 sysctl -n machdep.cpu.brand_string hw.ncpu hw.memsize,脚本可采集的环境记录见 intro-environment.txt。没有硬件性能计数器,没有隔离背景负载,也没有锁定频率,因此不声称测得真实指令数、CPI、CPU 频率、缓存 miss 或能耗。 延迟和吞吐不是同一个指标 延迟...
计算机体系结构 03:ISA 是机器能执行的契约
ISA 是机器能执行的契约 本篇的核心问题是:给定一串 32 位比特,机器怎样知道这是加法、访存还是分支?ISA(Instruction Set Architecture,指令集架构)回答的不是“电路怎样做得快”,而是“软件交给硬件的这串比特,在架构状态上必须产生什么效果”。架构状态至少包括 PC、通用寄存器、内存中可见的字节,以及遇到不支持指令或非法访问时的处理边界。 本系列第一条实验线选择 RV32I 作为共同语言。RISC-V 官方非特权规范把 RV32I 定义为 32 位整数寄存器宽度的基础整数 ISA;基础整数 ISA 提供算术、load/store 和控制流指令,扩展如乘除法、压缩指令、原子操作和特权机制另行定义。本文只使用一个更小的教学子集:add/sub/and/or、addi/andi/ori、lw/sw、beq/bne/blt/bge。这个子集足够执行一个求和循环,但不能代表完整 RV32I,更不能代表一个操作系统可用的处理器。 证据等级:手算、功能执行。功能执行证据来自 examples/computer-architecture/base/ 中的 C11 ...
计算机体系结构 04:C 函数怎样变成机器程序
C 函数怎样变成机器程序 本篇的核心问题是:C 里的函数、局部变量、参数和返回值,怎样落到寄存器、栈和内存地址上?第 03 篇已经把一条指令解释为 PC、寄存器和内存的状态转移;这一篇在 ISA 之上加一层 ABI(Application Binary Interface,应用二进制接口)。ISA 规定一条机器指令怎样改变架构状态,ABI 规定多个编译单元、函数和库怎样约定寄存器用途、参数位置、返回值位置、栈对齐和可调用边界。 目标 ABI 选为 RISC-V psABI 的 ILP32 方向:整数寄存器 XLEN 为 32,int、long、指针宽度都是 32 位。RISC-V psABI 的整数调用约定把 x10 到 x17 命名为 a0 到 a7,用于参数和返回值;x1 是返回地址 ra;x2 是栈指针 sp;s0 到 s11 是被调用者保存寄存器。当前本机 Apple clang 21 不含 RISC-V 后端,不能生成真实 RV32I 目标文件,因此 RV32I ABI 只做规则和手算;同时用本机 AArch64 真实编译与反汇编展示“参数寄存器、返回值寄存器、返回地址...
计算机体系结构 07:有限 RV32I 参考执行器
有限 RV32I 参考执行器 本篇的核心问题是:怎样写一个足够小、边界清楚、后续还能复用的指令参考执行器?答案不是一次写完整 CPU,而是先固定状态、输入、输出和失败规则。参考执行器只回答“这条指令按声明的 ISA 子集提交后,架构状态是什么”。它不回答周期数、流水线冒险、缓存命中、异常恢复或操作系统交互。 本篇产出位于 examples/computer-architecture/base/。执行器用 C11 编写,输入是文本形式的 32 位机器码和数据字,输出是 JSONL 提交轨迹。后续 08–11 篇可以把这条提交轨迹作为正确性参照,再在 Python 时序模型里引入阶段、停顿和分支恢复。 证据等级:功能执行。已执行 bash examples/computer-architecture/base/scripts/run_all.sh,结果为 base regression: PASS。原始输出保存在 examples/computer-architecture/base/outputs/。 同名素材目录也保留了本篇可下载附件:rv32i_ref.c、run_all.sh...
计算机体系结构 08:多周期执行
多周期执行:怎样在几拍之间保存一条指令 一条 lw 要取指、读基址、计算地址、读取内存,最后写回寄存器。若这些动作必须在一拍里结束,时钟周期就要覆盖整条路径。把它拆成几拍可以缩短每拍必须完成的组合路径,也允许同一硬件部件在不同拍承担不同任务。代价是指令要占用更多周期,控制器还必须保存中间结果。 中心问题是:拆开的每一拍究竟保存什么,下一拍怎样知道继续做哪一步?本篇用第 07 篇相同的 .hex 求和程序,构建实际推进状态的多周期解释器。证据等级包括“教学时序模型”和带明确题设的“手算”。模型没有门级网表、综合时钟或面积数据,因此不能据此断言某块硬件变快或变小。 保存状态以后,组合逻辑才能跨拍复用 单周期通路中的某个中间值在该拍内一直可见。进入多周期设计后,组合逻辑输入可能在下一拍改变;后续仍要使用的值必须写进寄存器。指令字需要 IR,寄存器堆的读出值需要 A/B,地址或算术结果需要 ALUOut,读取内存的结果需要 MDR。实际实现可以合并或重新命名这些寄存器,所保存的信息不能凭空消失。 Berkeley CS152 的多周期数据通路讲义展示了这些跨周期寄存器及控制状态。讲义使用...











