匿名页和 COW:fork 为什么便宜,写入为什么变贵
上一篇把缺页异常拆成了 fault 分派的几条路径。do_anonymous_page 处理首次访问匿名页,do_wp_page 处理写保护异常。这两条路径是 Linux VM 里两种延迟行为的核心:匿名页延迟到首次访问才分配物理页,COW 延迟到首次写入才复制。 核心问题可以压成一句话: 匿名页是没有文件 backing 的内存承诺;COW 把页面复制成本推迟到第一次写入。 问题从哪里来 malloc(1GB) 在多数 Linux 系统上不会失败,也几乎不消耗物理内存。fork() 一个驻留几 GB 的进程,能在毫秒级返回,不会发生几 GB 的复制。这两件事在朴素的“内存就是数组、复制就是按字节抄”的模型里都讲不通。 把它们拆开后: malloc 返回的是用户态地址,背后通常是 brk 或匿名 mmap。这一步只是建立一段匿名 VMA,没有分配任何物理页面。第一次访问这段区间的某一页时,缺页异常走到 do_anonymous_page,从空闲页池里取一页、清零、填进 PTE。1GB 全分配但只访问 4KB 的程序,物理内存增长就只有 4KB。 fork 复制的是 mm_s...
缺页异常:一次访问如何进入内核
上一篇区分了 VMA 与页表:VMA 是地址区间策略,页表是 CPU 当前可消费的翻译记录。当 VMA 合法、PTE 却不满足这次访问时,硬件会把异常抛回内核,由内核的缺页处理路径接管。这一篇把这条路径走一遍。 核心问题可以压成一句话: page fault 不是错误的同义词,它是 Linux VM 延迟兑现承诺的主要入口。 问题从哪里来 “缺页异常”这个翻译容易引误解。它在英文里是 page fault,词义中性,本意只是“缺一次翻译”。大量正常程序每秒会产生上百次甚至数千次 page fault,进程跑得很好,没有任何错误。 mmap 完成后第一次访问、fork 后子进程写共享只读页、读一个尚未在 page cache 的文件、被回收过的匿名页再次访问、栈在合法范围内增长,都会触发 page fault。这些 fault 走完之后,程序继续执行下一条指令,用户态察觉不到任何异常。 只有少数情况会让 page fault 变成可见错误:访问完全不属于任何 VMA 的地址、对只读 VMA 写入、对不可执行区段取指令、内核回收阶段无法找到合适页面来满足需求。这些情况要么导致信...
页表:CPU 能读懂的翻译结构
上一篇把地址空间组织成一组 VMA,回答了“这个地址原则上是否属于进程,应该按什么规则处理”。这一篇切到另一层结构:页表。VMA 是内核策略的元数据,页表是 CPU 的 MMU 实际查询的硬件数据结构。两层一致时访问能继续,不一致时进入缺页异常。 核心问题可以压成一句话: VMA 决定一个地址原则上是否合法,页表决定一个虚拟页此刻能否被 CPU 翻译。 问题从哪里来 很多教材把页表画成“虚拟页号到物理页号的一张大表”。这张表足够回答“地址能不能翻译”,但解释不了几件实际发生的事。 一次 mmap 成功后,VMA 已经登记,可是第一次访问还会触发缺页异常,进程并没有出错。一段映射可能在 maps 里看得到、长期不被访问、/proc/<pid>/pagemap 报 present=0,进程也没有出错。一个共享文件页可以同时被多个进程访问,每个进程的页表里都有一份 PTE,但物理页只有一份。一个匿名页可能此刻在内存里、PTE present;过一会儿被 swap 出去,PTE 变成 swap 类型;再被访问时通过缺页恢复,PTE 又重新指向 PFN。 把这些现象统一起...
地址空间不是数组:mm_struct 和 vm_area_struct
上一篇把 Linux VM 的主线压成“先承诺,再兑现”。这一篇先看承诺本身:一个进程说“这段地址可以读写”“这段地址来自某个文件”“这段地址不能访问”,这些信息在内核里不是按字节存成数组,而是按区间组织成一组 VMA。 用户态看见的是地址。内核看见的是地址空间、区间、权限和来源。 1virtual address -> mm_struct -> vm_area_struct -> policy mm_struct 描述一个用户态地址空间,vm_area_struct 描述其中一段连续虚拟地址区间。页表回答“这个虚拟页当前能不能翻译到物理页”,VMA 先回答另一个问题:“这个地址原则上是不是属于进程,应该按什么规则处理”。 从 /proc/self/maps 看地址空间 先从最容易观察的地方开始。运行一个普通进程,查看 /proc/<pid>/maps,会看到类似这样的行: 123455f2b6f3a000-55f2b6f3b000 r--p 00000000 08:01 131104 /usr/bin/cat55f2b6f3b000-55f2b6...
密码学签名全景:从 HMAC 到数字签名的完整图谱
从一个困惑开始 你可能在不同场景见过这些词:HMAC-SHA256、RSA 签名、ECDSA、HS256、RS256、X.509 证书签名……它们都叫"签名",但到底有什么区别?什么时候该用哪个? 这篇文章一次性理清整个图谱。 签名家族的完整分类 先看全貌——所有"签名"机制可以按照是否使用密钥和密钥类型分为三层: graph TD subgraph "Layer 0: 无密钥" H["Hash 函数<br/>SHA-256 / SHA-3"] end subgraph "Layer 1: 对称密钥" MAC["MAC 消息认证码"] HMAC["HMAC<br/>HMAC-SHA256"] CMAC["CMAC<br/>AES-CMAC"] MAC --> HMAC ...
重读 Linux VM:给系统研究生的虚拟内存导读
Linux VM 容易读散。它表面上在讲 mmap、页表、缺页异常、匿名页、page cache、folio、LRU、swap、NUMA、huge page、slab、cgroup memory 和 OOM killer,读起来像一串彼此相邻但没有主线的内核机制;主线可以压成一句话: Linux VM 是一套把虚拟地址、物理页、文件、进程、CPU MMU 和 I/O 统一到同一个状态转移系统里的内核机制。 这句话一旦立住,虚拟内存就不再只是“地址翻译”或“把内存变大”。地址空间描述进程能够访问什么;页表描述 CPU 当前怎样翻译地址;物理页描述内核手里真实可调度的资源;page cache 把文件内容放进同一套页管理体系;缺页异常负责把尚未兑现的映射变成真实页面;页回收和 swap 则在内存压力下重新分配这些页面。 本系列以 Linux 6.x(主要参照 6.1–6.8)为基准。涉及版本敏感的机制时(如 maple tree 6.1 引入、per-VMA lock 6.4 稳定、Multi-Gen LRU 6.1 合入、folio 迁移 5.16–6.0 持续进行),各篇会标...
Self-Improving Agent 的边界:外部记忆能不能 shadow 模型权重?
2026 年讨论 self-improving agent 时,最容易混在一起的有三件事:模型权重会不会自己变强,agent 会不会从运行经验里积累结构,harness 会不会自动改造自己。 这三件事都可以被叫作"自我改进",但边界完全不同。第一种发生在 L1,也就是模型权重和训练管线里;第二种发生在 L3/L4,也就是外部记忆、skill、session search、user model 和可检索的外部世界里;第三种发生在 harness 本身,也就是工具、middleware、调度、回滚和验证结构里。 如果不先把层级分开,“Hermes 是不是 self-improving agent”、“OpenClaw 是不是第一个自进化 agent”、"外部记忆能不能替代动态权重"这几个问题都会变成口号战。 一、权重没有变,行为会变 一个冻结权重的模型不会因为昨天完成了一次任务,今天的参数就多出一个新神经回路。对主流商用 API 和开源推理栈来说,部署后的模型权重大体是静态的。所谓 self-improving agent,绝大多数不是在改...
LLM Harness 路线图:从抽卡模型到可验证工程系统
这一组文章原本可以写成一篇长文,但那样会把最重要的判断埋掉。 LLM Harness 更像一条链路,从“模型为什么不稳定”一路走到“工程系统怎样托住它”。每一环都能单独展开,也都必须接到下一环。裸模型的随机性解释了为什么需要边界;上下文换入换出解释了为什么长程任务需要外部状态;环境可供性解释了为什么工具和数据入口会改变智能水平;Spec 和测试解释了边界怎样被写成工程资产;Agent Teams 解释了注意力怎样被隔离和降维;软件入口高阶化解释了这一切最终会改变什么。 flowchart LR A[裸模型: 概率分布] --> B[上下文: 工作集管理] B --> C[环境: 取到正确数据] C --> D[Spec / Test: 可验证边界] D --> E[Agent Teams: 注意力降维] E --> F[软件入口: Agent 化] D --> G[Harness: 控制系统] B --> G C --> G E --> G 先从抽卡感...
AI 不会吞掉软件,只会吞掉入口
“AI 会吞掉所有软件”这个说法很有传播力,但技术上不够准确。更可能发生的事情是:软件本体继续存在,软件入口被 AI 重写。 计算器、数据库、编译器、PDF 库、浏览器、CAD、CI、权限系统、ERP 不太会因为 LLM 出现而消失。它们处理的是确定性状态、格式、权限、计算和副作用。LLM 不适合直接替代这些系统,却很适合成为它们上方的意图入口。 从交互史看,GUI 的意义之一,是把人和计算机之间的操作鸿沟压低了。用户不必记住底层命令和数据结构,也能通过窗口、图标、菜单和指针操作复杂系统。Agent 时代出现的是另一道鸿沟:LLM 和传统软件之间的鸿沟。CLI、API、MCP 和 Skills 把这道鸿沟压低了,因为它们把软件能力变成模型能读、能调、能记录、能验证的文本接口。 确定性系统不会消失 裸模型不是确定性程序。它可以写出计算器代码,但不应该代替计算器执行财务计算;它可以解释 PDF 结构,但不应该靠想象修改 PDF 二进制;它可以生成 SQL,但实际执行、回滚、审计和加锁的仍然是数据库。 这不是模型能力不够强时的临时现象,而是范式边界。真实软件世界需要可复现、可审计、...
Agent Teams 为什么有效
多 Agent 不是把同一个模型多开几份,也不是给系统加几个“人格”。它有效的地方,是把一个会互相稀释的上下文问题,拆成几个更窄、更干净、更容易验证的工作单元。 复杂任务不是信息越多越好。一个 Agent 同时负责需求、架构、编码、测试、文档和发布,很容易在同一个窗口里把角色、证据和目标搅在一起。Agent Teams 的价值,是让每个工作单元只面对一个足够小的世界。 注意力需要降维 考虑一个频谱分析的类比:复杂任务像一段混合信号,包含多个频率分量叠加。单 Agent 试图同时处理所有频率,相当于用一个宽带滤波器——信噪比低,各分量互相干扰。多 Agent 相当于用一组窄带滤波器,每个只提取自己负责的频段,互不干扰,最后再合成输出。 模型的注意力也有这个问题。任务越大,上下文里越容易同时出现目标、反例、日志、旧方案、失败尝试、代码片段和用户补充。它们都在争夺同一个窗口里的注意力。多 Agent 的第一层收益,就是把同一团上下文切成几个局部上下文。 flowchart TB U[用户目标] --> O[Orchestrator: 保持全局目标] O --&...

