[{"content":"一、被瓜分的遗产 1.1 二十年前的许诺 升级虚拟化栈是云运维最古老的痛：数据面 和设备模型住在宿主内核与用户态里，而宿主 始终无法回答一个问题——固件与软件的边界 到底划在哪？今天的宿主里几乎一切都是软件， 于是几乎每一次升级都只能是机器级事件： 升级内核意味着重启宿主，而重启随之带来 迁移与维护窗口。\n2005 年，Xen 团队在 HotOS 上发表了一篇 标题即问句的论文： Are Virtual Machine Monitors Microkernels Done Right? 问句的标尺是微内核的毕生追求：极小特权层、 服务放进隔离的域、故障域彼此独立。问句问 的是：VMM 是不是把这三条终于做成的那个 载体？若这三条做成，服务栈就不再与宿主 共命运——开篇之问便有了架构上的答案。\n论文的回答是\u0026quot;是\u0026quot;，论据是一条必要性判断： VMM 的职责里必须驻在最高特权层的，天然 只有极小一块——CPU/内存虚拟化与隔离本身； guest 间的边界本由硬件强制；设备驱动、 设备模型、控制面则都不必住在特权层， 原则上皆可推进普通域里运行。\n但这个\u0026quot;是\u0026quot;只兑现到形状为止。隔离那一半 天然成立；另一半——住进普通域的服务该像 普通进程一样可重启、可升级——论文自己的 Xen 是最早尝试兑现的。VEE'08 的 Improving Xen Security through Disaggregation 开出驱动域路线，SOSP'11 的 Breaking Up is Hard to Do （Xoar）把 dom0 拆成一组最小权限的服务域， 每个 guest 的设备模型单独跑在 stubdom 里， 从论文看，架构闭环已经完成。\n但部署史回答了另一个词。驱动域和 stubdom 进了主线，却从未进过生产云：Citrix 的 Windsor 想做这套拆分，无疾而终；死结在 xenstore ——这个全局注册表始终焊在 dom0 里，每个 guest 的设备树、配置、凭据都挂在它上面， 重启它就要重建整机 guest 的控制面；于是 dom0 重启在运维上等价于宿主重启。纯软件 在云规模的兑现，至今悬置。\n于是服务栈的升级没有免费选项：住在用户态 的原地重启，代价是租户承受一次卡顿；住在 内核里的只能随宿主重启——租户迁移或停机。 开篇所说的迁移与维护窗口，正由此而来。 这次失败说明的不是 架构错了，而是架构没能扛住生产。为什么 扛不住——第二章的四层难点就是答案。\n1.2 业界的实际回答 云厂商用十五年各自作答，但回答的不是 同一个问题。\nAWS：把 dom0 搬进硬件。 先逃离 Xen， 换上极简的 KVM 系 VMM，再把基础 设施栈整体搬上 Nitro 卡。 许诺里\u0026quot;可隔离、可重启的服务域\u0026quot;兑现了—— 只是载体从进程域换成了 PCIe 对端。架构 是对的，placement 换成了硅。\n微软：数据面下沉，控制面留下。 从早期的 Catapult FPGA SmartNIC 到 AccelNet： 网络策略放进 FPGA 和网卡硅片，宿主软件 只做控制面。这是一半的微内核方案：数据面 有了独立生命，服务栈没有。\nGoogle：先在软件里跑了十年。 Andromeda 的 VFP——驻在宿主内核的包处理引擎——证明 软件数据面足以承载超大云的 VPC：数据面 不必天生在硅里。但最后，Google 也与 Intel 合研了 IPU—— 软件路线最坚定的标杆，最终把网络与块 存储的数据面都搬离了宿主——这层卸载硅 Google 对外称 Titanium。\nKVM 生态：渐进式剥离，而非结构性拆分。 vhost 把数据面搬离 QEMU 的设备模型，落脚 内核； vhost-user 把它搬进独立的用户态 daemon，可单独重启； vDPA 再把它卸给硬件。落脚点三次变换， 方向始终如一：QEMU 的设备模型被一点点 掏空。microVM 一派 （Firecracker、 Cloud Hypervisor、rust-vmm）把 VMM 本体 裁到最小、用 Rust 重写——但裁的是 VMM， 不是服务栈：存储、网络、控制面仍与宿主 共命运。隔离赢了，可重启没有。\n四个回答，答的是隔离、数据面、攻击面； 唯一碰到重启性的 AWS，把答案锁进了不外卖 的硅里。纯软件里，没有一个回答原始问题： 固件/软件的边界能不能划得足够薄——固件 层之上的一切，能否原地升级与重启？\n回头看，许诺被瓜分兑现了：隔离给了 microVM——Firecracker 赢下 serverless； 数据面给了硅片——eswitch 流表与各类卸载 引擎，普通网卡里也有；真正把 DPU 与普通 网卡区分开的卡上通用算力，接走了最后一 块：纪律。唯独最朴素的\u0026quot;服务栈可无痛 重启\u0026quot;，在纯软件里没有归宿。\n这正是上一篇文章 审计 DPU 后留下的最实质的残值：一个可 独立升级和重启的 root 计算域——工程纪律 用硬件形式固化，而硬件的代价太高。本文 问的是另一半：同样的边界，纯软件守得住 吗？\n答案是守得住。而且思想的所有零件都已是 各家的现役装备——缺的只是有人把它们 组装成那台机器。\n二、四重引力 许诺在纯软件里的兑现悬置了二十年，不是 因为没人想到。难处在四层，像四重引力， 合力把架构拉回单体：前三层是工程问题， 最后一层是组织问题。\n机制在，编排缺。 重启语义从不缺席： PV（半虚拟化）前后端协议——guest 前端 与服务域后端以共享内存 ring 通信——状态机 留着 reconnect 的出口；vhost-user 把 reconnect 做成了显式特性；热迁移每天都在 做存在性证明—— guest 迁到另一台机器，就在那台机器的 dom0 里重建了全部后端连接。\u0026ldquo;换掉 dom0\u0026rdquo; 不需要任何新机制，只需要把 guest、后端、 注册表之间的整张关系网按依赖序拆开、 再按依赖序重建。难在这张网：xenstore 是枢纽，后端互相依赖，拆解与重建的顺序 本身就是问题。没有任何生态把这一步做成了 产品。\n自举循环。 dom0 持有自己 rootfs 所在 设备的驱动。要重启它，得先把硬件所有权 交出去——存储、网络、控制台；而接手这些 所有权的域，自己又要由 dom0 启动和管理。 驱动域化是自举的前提，自举又是驱动域的 安放处。Xoar 拆掉了服务的一侧，自举的 一侧原地未动。\n性能的引力。 每多一层域边界，就多一次 通知与拷贝。工程优化的引力永远指向把 数据面收回来：收回内核态、收回同进程、 收回同一块硅。vhost 是一次收回，SR-IOV 是更大的一次。隔离与性能在每一代硬件上 重新谈判，而性能几乎总是赢——所以光 \u0026ldquo;守住边界\u0026quot;不够，守边界的代价必须小到 性能没有叛逃的动机。\n组织的不对称。 拆除 dom0 的收益弥散给 未来所有升级——每一次升级都从舰队维护 窗口变成一次后台操作；风险却集中在提出者 这一次的故障率上。没有哪个发布经理愿意 为\u0026quot;明年的运维更顺\u0026quot;承担今年的事故。 前三层难在有技术解，第四层没有——它只能 靠设计吸收：把组装的代价降到足够低，把 每次重启的爆炸半径降到足够小。\n三、组装那台机器 目标只有一个：把固件/软件的边界划到最薄 ——瘦核是系统的 firmware，等价于自研 DPU 里硬化的逻辑，不属于软件、不作热升级的 对象，以版本化发布演进；固件层之上的一切 ——服务栈与控制面——可以在任意时刻升级与 重启，不重启宿主、不迁移。\n由此推出两条纪律：\n核心是固件，不是软件。 瘦核等价于自研 DPU 里硬化的逻辑：不作热升级的对象，以 版本化发布演进，年级别的罕见事件；发布 之间的 CVE 由 livepatch 作为安全阀吸收。 它只拥有严格属于它的东西——越小，发布 越少。 服务域必须无状态。 状态在哪里， 重启就死在哪里——所以状态一律外置， 不能丢的东西一律不放进服务域。 3.1 架构：三层 架构共三层，如下图：最下是横跨整节点的 瘦核——裁剪的 Linux + KVM，只持 CPU/内存 虚拟化、IOMMU、中断注入与启动介质；其上 并列两区——host 区住控制面：一个特殊 VM 承载主体，瘦核用户态的零状态守护进程执行 操作、写入流表；guests 区住按职能拆分的 服务 VM 与它们服务的用户 VM。所有 VM 各得 一条直通 VF 供自身通信；租户数据面经 VF 直通，不经过任何服务 VM；缓存按分区划， 宿主服务与租户各居其区。以下逐段展开。\n瘦核。 裁剪过的 Linux + KVM，只管 CPU/内存虚拟化、IOMMU、中断注入与启动 介质——没有数据面、没有控制面。这一层是 本系统的 firmware：等价于自研 DPU 里硬化 的逻辑，以版本化发布演进；KVM 本身就是 内核模块，正落在 livepatch 的射程之内—— 攻击面越小，CVE 越少、发布越少，发布之间 漏网的由 livepatch 作为安全阀吸收。瘦核里 不该有什么，比该有什么更清楚：没有存储 驱动、没有设备模型、没有工具栈；网络侧 只留 PF 驱动——eswitch 的管理交给控制面 守护进程。\n无状态控制面：host 侧的一个特殊 VM。 注册表、配置、编排状态一律外置——唯一 存放处是集群层面的控制面；节点内的控制面 VM 承载控制面主体，零状态，被动接收集群 的调度安排，只下发意图——落地是控制面 守护进程的事，二者各自可重启。控制面 VM 的重启同样转发不断——数据面住在 eswitch 流表里，与它无关。xenstore 的教训在前： 全局注册表不能再焊进任何需要重启的 东西里。\n控制面守护进程。 网卡的 PF 与其驱动归 瘦核；控制面 VM 下发的实际控制操作—— 创建 VM、删除 VM、绑解设备——由瘦核 用户态的这个零状态进程落地；eswitch 流表与每 VF 策略也由它写入。它的重启不是 内核的重启：流表住在硅片里，进程重启期间 转发不断，停的只是变更。这一条服务不住 隔离域，而以进程形式住在瘦核旁——设备 所有权（PF 不能离开瘦核而不连带复位） 压过域隔离；放弃的是故障隔离的粒度， 换来的是重启与数据面的命不再相缠。\n服务 VM。 按职能拆分，而非按机器： 块存储、文件存储各自成域；每域几台 按需而设，可以是一台，也可以是多台。 每个服务 VM 用 VFIO 直通持有 自己的硬件，数据面与协议终结都在其中 运行；每个 VM——服务 VM 与控制面 VM——还各得一条直通 VF 供自身通信。 每个服务 VM 都是一个 dom0——但是 无状态的：崩溃算一次重启，升级也是一次 重启，两者走同一条路。拆得越细，重启的 爆炸半径越小，升级的节奏越自由。用 VM 而非进程，还多买一样东西：重启域、部署域、 故障域塌缩成同一条边界——硬件强制的 边界，不花一分钱。\n缓存分区。 住在宿主核上的服务与租户 共享 L3：不管的话，服务 VM 的流量与守护 进程的扫描会污染租户的缓存行，而这种干扰 不可见、不可记账。如今多核的 L3 大多可 分区：Intel CAT 按 CLOS 划 cache way，AMD 的 L3 本属 CCX、划出一个 CCX 即物理隔离， ARM MPAM 按 PARTID 划；Linux 的 resctrl 把三者收在同一配置界面下。应对是配置而非 祈祷：把宿主服务所用的 CPU——服务 VM 与 各守护进程——集中到一两个缓存分区（AMD 上可以就是一个 CCX），租户用其余分区。 污染于是从不可控的外部性，变成一条配置 划定的边界：服务能占多少缓存，写在配置 里，不写在运气里。\n3.2 网络：数据面归网卡 guest 持有 SR-IOV VF；VPC 规则下推进网卡 的 eswitch 流表；每 VF 的限速与 QoS 是 网卡的标准能力。控制面守护进程剩下的只是 带外工作：下发规则、收集状态。\n于是控制面守护进程的重启变得乏味：规则 留在硅片里，转发不断；其重启窗口里暂停的 只是\u0026quot;变更\u0026rdquo;——配置下发、安全组编辑， 排队几十秒。 Azure 的 Accelerated Networking 这个模式已经跑了多年：策略在硬件里， 宿主被绕过。\neswitch 流表与 match-action 管线的表达力 上限，决定了\u0026quot;数据面归网卡\u0026quot;永远只是部分 的：推得下的是稳定而热的 match-action—— L2/L3 转发、封装、每 VF 限速；推不下的是 长尾——有状态连接、复杂 NAT 与负载均衡 语义、新协议。这不是代价项，而是设计 约束：它规定软件路径不是过渡措施，而是 永久组件，硬件快路径是加速器而非唯一 执行点。上限本身与商用/自研无关——自研 DPU 同样在 tape-out 时冻结，这是第二章 \u0026ldquo;迭代敏捷\u0026quot;反论在硬件里的本体；可编程 管线（P4 一类 match-action、厂商的 flow 编程 API）会把边界外推，但边界不会消失， 且随别人的路线图移动。\n两点需要进一步说明：\nVF 迁移已经支持。 mlx5 的 vfio migration variant driver 已合入主线 （Add mlx5 live migration driver）， 商用网卡的 VF 从此能迁移；生产侧的成熟 做法是合成回退路径：维护事件前后动态 收回和恢复 VF，应用绑定合成设备保持 连通——官方文档明文如此。 补充两点：其一，标准化的是迁移框架、 不是状态，迁移数据流是厂商不透明的， 覆盖限于同系列；其二，跨厂商迁移没有 路——但这不致命，跨 CPU 型号/厂商的热 迁移同样受限，舰队按构造本就同构。 卸载边界由别人划定。 哪些策略推得 下去，取决于网卡代际与厂商；边界两侧 异构——卡内是厂商的 flow API，卡外是 我们的软件，长尾跨边界时多付一跳。自研 DPU 也有上限，但边界两侧都归自己、长尾 落在卡上核；本设计用不拥有这份所有权， 换不造硅。 3.3 存储：协议终结在服务 VM guest 只见 virtio；存储服务 VM 前端终结 guest 的块请求，后端以存储协议对接存储 集群——RDMA 或 TCP、NVMe-oF 或私有 协议，随部署而定；凭据与拓扑对租户 不可见。\n它的重启窗口是整个设计里最工程化的一段， 但不需要冗余实例。多数情况下重启/升级 的是用户态的存储服务 daemon——进程级 重启，秒级或更快；只有服务 VM 的内核本身 要换——罕见——才轮到 kexec 预加载新 内核，把重启压缩到秒级。guest 感受到的 停顿等于重启时长；guest 侧的吸收能力远 宽于此：\nvirtio-blk 没有硬超时，guest 的文件系统 自行重试，吸收上限在几十秒量级——这是 裕量，不是预期停顿； 后端一侧，存储协议的重连与重试语义 兜底。 多实例的存在首先为了隔离：一个实例的 崩溃或重启只触及它自己的份额；其次为了 让重启更容易：逐实例滚动，不必把一次窗口 赌上全部。\n3.4 机制不新，路都走过 这套设计里没有一样机制是新的：第二章 第一层引的 reconnect 语义，加上 VFIO 的 解绑与重绑，就是组装需要的全部库存。Xen 缺的从来不是机制，是把\u0026quot;换掉 dom0\u0026quot;变成 一次编排动作。而这条路的每一段，都有人 走过：\nNitro 就是这个架构——只是服务 VM 搬到了卡上。它证明的是架构在云规模上 成立；服务域住在宿主内存还是卡上硅片， 是商务问题，不是架构问题。 AccelNet 证明了另一半：硬件数据面 加软件控制面，可以服役多年。 没人走过的是把它们装回纯软件——那是 本章的工作。\n结论 2005 年的问句至今仍 open，但答案的形状 已经清楚。本文的组装让宿主能够回答开篇 之问：固件/软件的边界被划到最薄——固件层 之上的一切可以在任意时刻升级与重启，不 重启宿主、不迁移，租户感受到的至是一段 可吸收的停顿；固件层本身，如自研 DPU 里 硬化的逻辑，以版本化发布演进。微内核 VMM 不需要新机制，缺的是一次认真的组装 ——而组装需要的每一块积木都已在别人的 舰队里跑了多年：reconnect 语义、VFIO、 kexec、eswitch 流表、缓存分区，没有一样 是本文发明的。\n这套架构的合理性，在于它把每一条边界都 变得显式而廉价：VM 边界加 IOMMU，在软件 里守住 DPU 用一条 PCIe 总线才守住的边界 ——硬件强制，不花一分钱；税记在可灵活 调度的宿主核上，不是搁浅的卡上硅片；卸载 的边界、缓存的分区，都写在配置里，不写在 厂商路线图或运气里。软件换回的是迭代： 没有 tape-out 的冻结，新语义不必等下一代 硅。\n隔离从来不是难点。维护才是——而本文还给 维护的，是它的平常心：崩溃、升级，都算 一次重启。\n","date":"2026-08-24T00:00:00Z","permalink":"/p/microkernel-vmm-done-right/","title":"把 VMM 微内核做对"},{"content":"一、成功的模式不抵失败的市场 DPU 的叙事讲了很多年：\u0026ldquo;虚拟化税归零\u0026rdquo;、 \u0026ldquo;性能提升\u0026rdquo;、\u0026ldquo;安全隔离\u0026rdquo;、\u0026ldquo;裸金属统一\u0026rdquo;、 \u0026ldquo;迭代敏捷\u0026rdquo;、\u0026ldquo;能效\u0026rdquo;，直至加冕为继 CPU、GPU 之后的\u0026quot;第三颗算力\u0026quot;。但市场给出的答案与这套 叙事格格不入：它成就了自研者，却没有成就 买家。本文逐项审查这套叙事——先看市场， 再拆主张，再算代价，最后看还剩下什么。\nDPU 的大规模成功部署，全部来自头部云厂商的自研垂直整合：AWS 2015 年收购 Annapurna Labs， Nitro 系统 2017 年随 C5 实例 完整亮相； 阿里云 2017 年发布 神龙架构， 2022 年推出 CIPU； 微软从自研 Catapult FPGA SmartNIC 起步，2023 年 收购 Fungible 团队， 2024 年推出自研 Azure Boost DPU； 华为擎天、 百度太行、 腾讯 同样是自研路线。头部厂商中唯一接近\u0026quot;采购\u0026quot;的是 Google——与 Intel 联合设计 Mount Evans， 但那也是按 Google 规格定制的 co-design，不是购买货架产品。\n然而商用 DPU 市场已经全军覆没：Fungible 累计融资超过三亿美元，2023 年初 被微软收购， 据报道 作价仅约 1.9 亿美元； Pensando 2022 年 被 AMD 收购 后逐渐边缘化；Nebulon 悄然收场， 据报道团队被 NVIDIA 吸收； Intel IPU 产品线 多次调整； NVIDIA BlueField 的公开落地集中在 AI 网络等特定场景。面向企业市场的 vSphere DSE（DPU 版 vSphere）未见规模采用，而其 DPU 固件升级需要宿主重启、靠 vMotion 疏散虚拟机来保证业务不中断——用迁移代替无感升级， 这本身就是最诚实的投票。\n国内独立 DPU 厂商的处境是同一份判决书的副本，而且更彻底。跑得最远的云豹智能正在冲刺港股 \u0026ldquo;国产 DPU 第一股\u0026rdquo;，但 招股书显示 三年营收从 17 万元涨到 3.7 亿元，累计亏损近 25 亿元， 九成以上收入来自单一客户腾讯—— 腾讯同时是第一大股东，而这朵云自己同样自研 DPU。第一大股东加九成营收，这不是独立厂商， 是一朵云的体外部门。再看其余\u0026quot;真正独立\u0026quot;的厂商：左江科技的 DPU 业务被监管认定\u0026quot;严重不实\u0026quot;， 财务造假坐实，公司退市，实控人被拘留； 中科驭数 仍在一级市场融资续命，最新 C+ 轮 投前估值约 50 亿元； 赛道已收窄到金融低时延交易等细分场景——那已经不是云；私有云侧只有 银行云平台、 高校边缘云 这类项目制的零星落地，公有云正面战场完全无缘；对外只有 \u0026ldquo;订单量翻倍\u0026quot;的官方口径， 从未递交招股书——财务不公开，沉默本身也是一种回答；星云智联、云脉芯联、大禹 智芯等同期创业公司已鲜有声响。换言之，国内没有任何一家跑通的独立 DPU 厂商—— 唯一跑到 IPO 门口的那家，恰恰是用\u0026quot;不再独立\u0026quot;换来的。\n如果 DPU 的技术价值是实质性的，它应该同时成就采购者和自研者；现实是它只成就了自研者。 这说明它的收益薄到只有在剥掉全部 vendor margin、按 BOM 成本自研时才可能翻正。成功 的模式不能为失败的市场开脱——恰恰相反，分裂本身就是市场给出的判决书。\n二、价值主张的归因审计 DPU 的叙事由七项价值主张构成。逐项归因之后，五项经不起审查。\n\u0026ldquo;虚拟化税归零\u0026rdquo;——搬家，不是消灭。 虚拟化栈确实需要消耗一定量的算力， 即所谓\u0026quot;虚拟化税\u0026rdquo;。但 DPU 只是把这笔税从宿主的核搬到卡上的核跑，运行的 还是同一份协议栈、同一个设备模型，工作一行没少，只是换了个地方记账—— 从可售的宿主 CPU 资源改记为一次性沉没的卡成本。这充其量是一次换汇，而且 换来的是搁浅资产：宿主核不干基础设施时还能跑租户负载，卡硅永远只能干基础设施。\n\u0026ldquo;性能提升\u0026rdquo;——硬件管线不在 DPU 手里。 小包线速确实是硬件的领地，软件只 适用于低速网络；但硬件转发管线在普通 SR-IOV 网卡里就有。DPU 真正多出来的 可编程部分跑在孱弱的 ARM 或 RISC-V 核上，性能反而不如宿主 CPU。 所谓\u0026quot;硬件卸载\u0026quot;外衣之下，只是把一样的软件部署到卡上跑而已——它连 SR-IOV 都依赖， 枚举 VF 的方式和普通网卡一模一样。\u0026ldquo;硬件卸载\u0026quot;的准确翻译是：同样的软件搬家到了另 一种孱弱的硬件上而已。而这种孱弱在网速迈向 数百 Gbps 之后，只会愈加成为瓶颈。\n\u0026ldquo;安全隔离\u0026rdquo;——边界是单向的。 virtio 就是一对基于 共享内存的 ring queue，安全与否取决于设备实现， 不取决于跑在哪里。卡固件闭源、审计者少、 补丁链条长，而被它替代的 QEMU 设备模型 常年接受公开 fuzz。 更关键的是 DMA：卡作为 PCIe 设备天然持有对宿主 内存的访问能力，裸金属场景下 IOMMU 无从裁剪—— 卡被攻破等于宿主被攻破。DPU 的安全边界防的是 租户碰平台，从来不防卡自身殃及租户。\n\u0026ldquo;裸金属统一\u0026rdquo;——真实，但只值半项。 宿主 不可信时，云盘和云网络的协议必须有卡来终结， 这是 DPU 最难替代的功能。但两端都在侵蚀它：无卡 裸金属（交换机划 VXLAN + iSCSI/NVMe-oF 远程 启动）是合法的、只是更瘦的产品，SoftLayer 和 Equinix Metal 早已证明；而对要求 VPC/EBS 完整 语义的客户，一台（几乎）占满整机的 VM 在性能上 逼近裸金属，且天然继承全部云语义。真正的刚需 只剩三类：自带 hypervisor 的栈、绑定物理硬件的 license、写明\u0026quot;专用硬件\u0026quot;的合规条款。\n\u0026ldquo;迭代敏捷\u0026rdquo;——说反了。 卡上 pipeline 功能在 tape-out 时定型，新协议要等下一代设计，固件驱动 锁步升级。不是 DPU 把软硬件解耦，是它把迭代和 硬件代际耦合。而升级数据面不惊扰租户，宿主软件同 样做得到。\n\u0026ldquo;第三颗算力\u0026rdquo;——名字即否定。 DPU 是 NVIDIA 收购 Mellanox 后的 rebranding。可辩护的部分只剩 \u0026ldquo;固定菜单的领域专用加速\u0026rdquo;，而领域专用恰恰自我 否定了\u0026quot;通用\u0026rdquo;。\n\u0026ldquo;能效\u0026rdquo;——账本已破产。 现代 x86 / ARM 宿主 CPU 的 perf/watt 让 DPU 上 ARM 核省电的前提不再成立；卡自身 75W+ 对普通网卡约 25W，是净增功耗。\n三、但是，代价呢？ 即使对上述审计全部存疑，DPU 的代价清单也是 独立的、硬性的：\n硬件不可逆。 流片定型即不可更改：有多少 I/O queue、支持多少 VF，在出厂那一刻就已固定； 有没有设计缺陷，则以 errata 的形式终身伴随 一代产品。而软件方案的缺陷则是一次 hotfix。\n每台服务器新增一个故障域。 商用 DPU 普遍在 卡上跑着完整的 Linux 发行版，内核 CVE、固件升级 一样不少，只是从宿主挪到了工具链更差、发布节奏 更慢的设备上。卡固件 bug 是全舰队相关事件—— 规模多大，故障域就有多少。\n在裸金属上制造单点。 无卡裸金属的云侧维护对 租户是透明的：交换机有冗余，存储有多路径， 升级不碰租户的机器。DPU 把唯一一个由云控制、 需要反复维护的故障域塞进了租户独占的机箱—— 而裸金属没有热迁移兜底，卡的固件重启直接砸在 租户 SLA 上。\u0026ldquo;无痛重启\u0026quot;本应是答案，其兑现范围 却恰好是：头部两三家闭园系统里为真且无法审计， 公开市场停留在幻灯片，在最需要它的裸金属上 拿不出任何公开数据。\n组织成本被系统性低估。 整建制的芯片与固件团队、 ASIC 流片、舰队事故——这些成本真实存在， 但任何对外讲述的 ROI 都从未披露过它们； 收益却可以按 vCPU 售价精确入账。更微妙的是， 卡的存在冻结了比较基准——纯软件的替代架构 从未被建造，对照实验永远无法成立，沉没的 NRE 让\u0026quot;继续投卡\u0026quot;永远显得划算。\n四、还剩什么 审计到最后，DPU 还剩下一样真实的东西：一个 可独立升级和重启的 root 计算域——基础设施 服务栈拥有独立于宿主内核和租户的生命周期， 它的升级和重启不惊扰任何人。这是七项主张过筛之后 最有实质的残值，也是微内核架构二十年前的许诺： 2005 年 Xen 团队的论文 Are Virtual Machine Monitors Microkernels Done Right? 是个问句，部署史替它回答了\u0026quot;no\u0026rdquo;——dom0 不可 阻挡地长成单体，没能抵挡住架构腐化。但 Xen 没 做好不代表软件上做不到。\nDPU 用一条 PCIe 总线，把软件层面守不住的边界 变成物理上不可跨越的。这是它最体面的技术注脚： 用硬件形式固化的工程纪律——组织价值， 不是技术价值。只是用硬件达成这份价值的代价太高了。\n五、结论 DPU 神话的完整结构是：七项价值主张，五项经不起 归因审查，一项真实但软件可替代——可替代就是 立不住——剩下半项真实但小众。七项主张， 最后只剩半项，而其背后则是一份不短的代价清单。它的成功属于 两三家超大云在特定历史窗口里的偶然与必然， 它的失败属于整个市场对其技术价值的公开评判—— 而后者才是定价的真相。\u0026ldquo;大家都在用\u0026quot;从来不是技术价值的 证据，它也可能只是历史决策、组织惯性和信号均衡的证据。\n薄收益加厚叙事，是神话的标准配方。而这个神话该结束了。\n2026.08.31 补充 自研卡便于虚拟机迁移——这件事在相当长的 时间里是真的。障碍从来不在\u0026quot;自研还是商用\u0026rdquo;， 而在设备状态能否随迁移保持一致：裸 VF 直通 时，队列上下文驻在网卡内部，要把它完整导出、 在新宿主的卡上原样恢复，商用硬件当年做不到 ——高性能与可迁移于是不可兼得。自研卡的解法 是让状态由程序持有：guest 的设备由卡上的固件 提供（Nitro 之下是 ena，神龙之下是 virtio）， 迁移时让新宿主的卡重新实例化一次即可， guest 无感。而且未必是 DPU，只要卡能把 guest 的设备演出来就够。这条路当年只有 自研者走得通。\n这一页也翻过去了，两条路都被商用卡走通： 早先一条走软件协议界面——mlx5 的 vDPA 模式 （VDPA support for Mellanox ConnectX devices） 2020 年起就向 guest 呈现 virtio 设备，数据 路径在卡上硬件；坚持直通裸 VF 的，mlx5 的 vfio migration variant driver 2022 年合入 主线内核 （Add mlx5 live migration driver）， NVIDIA 官方文档已提供完整的 SR-IOV Live Migration 支持——状态的导出与恢复补齐了，商用卡的 VF 从此也能迁移。 这笔当年独门的价值被商用路径磨平——剩下的 差别只有时间差与路线图归属，那是采购学， 不是技术。\n","date":"2026-08-20T00:00:00Z","permalink":"/p/end-of-dpu-myth/","title":"DPU 的神话该结束了"},{"content":"在可随机读取的压缩存储设计中，数据布局先要回答一个问题：压缩的边 界画在哪里。fixed-input 按定长输入切分、逐单元压缩、变长输出紧密 排列；fixed-output 反其道而行——变长输入\u0026quot;塞满\u0026quot;定长的输出块，使 压缩单元与存储块天然对齐。\nfixed-output 由 [1] 提出，并携一串动人的故事而来：消除读放大、对 齐 I/O、提高压缩率、节省内存。本文逐一核对它们的下落：这些故事 要么记错了主人，要么只在限定的口径里上演，要么查无实据；而代价 永久有效——作为一项格式设计决策，fixed-output 并无存在的必要。 本文否定的是这项技术作为通用设计选择的价值，不涉 及任何采用它的具体系统。系统的成败由实现质量、运营与生态共同决 定，一个不成立的机制不一定妨碍一个成功的系统。\n一、读放大与对齐：两则童话 读放大的机制很简单：解压必须从一条压缩流的头部开始（LZ4 等算法 无法从流中间寻址），单元越大，为一个小请求付出的 I/O 与解压就越 多。它只取决于单元大小，与输入定长还是输出定长无关。\n先看 [1] 自己的评测数据（读取 16MB，实际发出的 I/O 量；跨步读即 每 128KB 只读开头 4KB）：\n布局 单元大小 随机读 跨步读 fixed-input 128KB 165.27 MB 203.91 MB fixed-input 4KB 26.19 MB 26.23 MB fixed-output 4KB（输出） 26.12 MB 25.93 MB 4KB 粒度下两种布局的 I/O 量几乎相同；六到八倍的放大差异全部来自 单元尺寸。第一则童话到此显形：读放大的消除是粒度的功劳，与布 局无关——而给出这个对照的，正是提出它的论文自己。\n同一组数据里还藏着第二则童话。fixed-output 的输出块与存储块天然 对齐；而 fixed-input 的变长块紧密排列、必然跨越块边界，读取时要 整读首尾两个只用得到一部分的物理块，理论上有\u0026quot;碎块\u0026quot;损耗。理论成 立，红利却从未兑现：上表中两种布局的总 I/O 量相同——碎块字节 属于相邻单元，对后续读取同样有效，缓存将这项理论损耗抹平。\n机制层面的关键在于缓存顺带取回但暂未用到的数据，而两种布局缓存 的形态不同。fixed-input 只需要缓存压缩态数据，而 fixed-output 则 需要缓存解压态数据，否则就会浪费解压开销。缓存这一层非但不偏向 fixed-output，反而站在 fixed-input 一边。\n二、压缩率：限定口径里的大捷 fixed-output 对同单元尺寸的 fixed-input（4KB 对 4KB）确有约 10% 的 体积优势 [1]——一场大捷，机制也清楚：\u0026ldquo;塞满\u0026quot;使每条压缩流覆盖更 长的输入，而流越长压缩率越好。\n但这场大捷避开了现实的对手。4KB 输出的 fixed-output 比大窗口 fixed-input（128KB 单元）的镜像大 11% 到 29% [1]（实测两组语料： 0.52GB 对 0.47GB；100.9MB 对 78.0MB）。要追回这个差距，fixed-output 只能 放大输出块——也就是把粒度拧回去，同时把读放大请回来。这项技术 把压缩率和随机读性能焊死在同一个旋钮上，拧向哪一头都是让步。\n存储侧还有一笔小账：fixed-input 紧密排列、一字节不浪费； fixed-output 的每个输出块尾部填不满，自带内部碎片。\n三、同口径对比：孪生兄弟的故事 其实前两节藏着一个口径陷阱：fixed-input 的标签指输入， fixed-output 的标签指输出——同挂一个\u0026quot;4KB\u0026rdquo;，流长并不相同。 真正的同口径，是让每条压缩流覆盖相同的逻辑数据。以 2:1 压缩率 为例：fixed-input 的 256KB 单元与 fixed-output 的 128KB 输出块， 是同一条流的两种切法。逐项重算（服务一次 4KB 随机读）：\nfixed-input，256KB 单元 fixed-output，128KB 输出 单条流 256KB 输入 → ~128KB 压缩 ~256KB 输入 → 128KB 压缩 读取 I/O 整条流 ~128KB 整条流 128KB 解压量 从头解到目标产出，平均半条流 从头解到目标产出，平均半条流 读放大 ~32× ~32× 压缩率 相同 相同 全部归零：孪生兄弟之间，本无所谓高下。由此还多出一个推论——第 二节那 10% 的\u0026quot;优势\u0026quot;其实也是口径幻觉：\u0026ldquo;4KB 对 4KB\u0026quot;的标签之下， fixed-output 的流实际长了一倍：优势的源头是流长差，而非布局； 流长拉平，率差归零。fixed-output 的 128KB 输出块 ≡ fixed-input 的 256KB 单元加上对齐；而对齐的收益 从未兑现（见第一节）。\n同口径下，剩余的差异全部是结构性的：fixed-input 索引稀疏、逻辑定位 算术免费、存储零浪费；fixed-output 块对齐、I/O 大小先验可知（见第 六节）。公平对比之下，性能与压缩率的账上，再没有属于布局的盈 余。\n四、读取位置的算术：镜花水月 定位一次读取需要两步映射：逻辑偏移→压缩单元，压缩单元→物理地 址。两种布局各自只能免费完成一步，且方向相反：\nfixed-input: offset ──[÷C，算术]──→ 单元 ──[查表]──→ 物理位置 fixed-output: offset ──[÷4K]──→ 逻辑块 ──[?]──→ 单元 ──[×4K]──→ 物理位置 关键在于：fixed-output 免费的乘法步位于链路末端，而查询永远从逻辑 偏移发起——这步算术便利如镜花水月，在读路径上永远用不上。它缺 失的\u0026quot;逻辑→单元\u0026quot;映射是数据相关的，无法算术求得，只剩两个选择： 稠密索引（每个逻辑 块一项、每项 8 字节 [1]，比大窗口 fixed-input 按单元计的索引稠密一 个数量级以上），或者稀疏索引加二分查找（O(log n)）。\nfixed-input 则没有这个问题：偏移表中相邻两项相减即得压缩单元长度， 位置与长度一次查表齐活。在定位这根轴上，fixed-output 的账上只有 支出。\n五、可寻址性：一则传说 超出 [1] 之外，分发格式的设计讨论中还流传着一条对定长对齐的期 待：按需分发、懒加载、块级去重，似乎都以定长块为前提——传说正 是从这里开始失真。fixed-input 加一张偏移表，变长块同样是可寻址 对象——地 址就是索引项里的（偏移，长度），配合 HTTP range 请求即构成完整 的按需分发。生产级的 fixed-input 分发方案早已存在（OverlayBD 的 ZFile [2]；stargz 的 TOC [3]），且规模庞大。\n传说的下落就此查明：可寻址性的源头是索引，而非输出的定长。\n六、省内存：一枚颁错的勋章 [1] 还有一条内存效率的宣称：fixed-output 使原位解压成为可能，且每 次解压至多读取两个压缩块、大小先验可知，内存开销因此有界。这条 宣称同样经不起归因检验——省内存的关键技术，件件记在解压器名下。\n其一，部分解压：从流头解到目标产出即停，之后的字节不解。这是压 缩流顺序解码的直接推论，对两种布局同样成立。其二，滚动解压：利 用压缩算法的滑动窗口（如 LZ4 的 64KB 窗口），只保留窗口大小的历 史页面即可持续解码——窗口是算法的属性，不是布局的。其三，直接 灌入目标页：解压输出直接写入目标页缓存，省去临时输出缓冲， fixed-input 的读路径同样可以这样做。至于\u0026quot;大小先验可知\u0026rdquo;，第四节已 经给出——压缩单元长度就是偏移表相邻两项之差，并非定长输出的专 利。\n反向的账更值得记：fixed-output 为这条路径付出的复杂度反而更高。 它要求压缩算法提供定输出接口（destSize），这是格式级约束；定位 需要按逻辑块计的稠密索引；[1] 的原位解压自身也不免费，需要在构 建镜像时模拟解压、逐块判定可行性。这枚勋章该颁给解压器，而不 是布局；fixed-output 为它背的复杂度，一分不少。\n七、代价：格式的刺青 前六节核的都是收益一侧的故事，代价的账集中记在这一节。\n其一是工程复杂度，最终形态是代码量。fixed-input 的两端各是一个简 短的循环：构建端切分、压缩、追加、记下偏移；读取端一次查表、一 次解压。fixed-output 没有这样的直线：前面各节的每一项结构差异， 最终都要落成代码——构建端是一台状态机，读取端是一路分支，代码 量成倍提高。这份开销不按次计费，按实现数计费：内核驱动、用户态 工具、各语言绑定，每个采用者都要重写一遍。\n其二是构建速度。主流压缩库的主干接口是 fixed-input 的：定长输入、 变长输出、一次调用，库的优化投入全部集中于此。fixed-output 需要 的定输出（destSize）接口只是部分算法的支流变体；绕开它，就得用 通用接口反复试压——填入一段输入、观察输出是否越界、越界则回退 重压。两条路都慢；且算法库每次升级，红利先落在主干——fixed-output 只能隔岸观望。\n其三是演进负担。fixed-input 把自由度留在格式之外：单元大小是每 个镜像自选的参数，索引与解压策略都是实现细节，随时可调。 fixed-output 则把关键决策写进了格式：输出定长、块对齐、逐块的原 位标志，都是契约条款——日后每一次扩展格式、更换算法、优化读路 径，都得背着这张契约走，无从修订。\n故事的缺陷在讲述——换一个口径、做一次实测就会现形；代价的缺陷 在定义——只要格式还是 fixed-output，每一个镜像、每一个实现就都 要照付。实现可以迭代，格式一旦发布只能兼容：内部碎片不会因更聪 明的构建工具而消失，定位的查表不会因更勤勉的缓存而省掉，构建期 的接口错配也不会因算法库的升级而弥合。它们随格式的每一次使用而 复利——这正是\u0026quot;格式级的、永久的\u0026quot;的含义。\n于是整笔账就此合上：被宣称的收益，对手用实现手段就能免费获得— —粒度、索引、解压器，哪一样都不需要定长输出；而代价全部刻在格 式自己身上。故事会褪色，刺青不会。\n结论 把归因做完，神话逐一收场：读放大归于粒度，内存效率归于解压器， 对齐的红利查无实据，压缩率的大捷只在避开现实对手的口径里上演； [1] 之外的可寻址性期待，同样归于索引——每一项被宣称的优势，要么消 失，要么易主。代价则全部留在它自己账上：定位失去算术性、输出块 内部碎片、对压缩算法 destSize 接口的约束、构建与运行两侧的工程 复杂度、更慢的构建与无从修订的演进契约——格式级的、永久的。\n因此我们的结论客气但彻底：这项技术不值得采用。 事实上自 2019 年提出至今，没有任何第二个系统采纳这项技术——它唯一的载体， 仍是提出它的那个文件系统自己。设计只读压缩布局时，正确的做法是把 自由度留在粒度轴上（按负载选择单元大小）、把索引建好，然后使用 fixed-input。\n最后留两条方法论，它们比本文的具体结论更耐久：\n归因检验。 见到一项优势，先问\u0026quot;一个实现同样良好的对手方案能 否得到它\u0026quot;。能回答这个问题，多数技术营销会当场现形。 时间检验。 多数论文与技术的论点会随时间湮灭，这是常态。制品 的幸存不等于论点的幸存——一个系统活得好好的，它的核心主张可 能早已被它自己的演进悄悄替换。 参考文献 [1] Xiang Gao, Mingkai Dong, Xie Miao, Wei Du, Chao Yu, Haibo Chen. \u0026ldquo;EROFS: A Compression-friendly Read-only File System for Resource-scarce Devices.\u0026rdquo; USENIX Annual Technical Conference (ATC \u0026lsquo;19), 2019. https://www.usenix.org/conference/atc19/presentation/gao\n[2] Huiba Li, Yifan Yuan, Rui Du, Kai Ma, Lanzheng Liu, Windsor Hsu. \u0026ldquo;DADI: Block-Level Image Service for Agile and Elastic Application Deployment.\u0026rdquo; USENIX Annual Technical Conference (ATC \u0026lsquo;20), 2020. https://www.usenix.org/conference/atc20/presentation/li-huiba\n[3] stargz Snapshotter: eStargz (seekable tar.gz) lazy-pulling image support. https://github.com/containerd/stargz-snapshotter\n","date":"2026-08-17T00:00:00Z","permalink":"/p/against-fixed-output/","title":"Fixed-Output Compression 的神话"},{"content":"容器、虚拟机、Agent 沙箱，启动方式都一样：从一个镜像开始。然而主流镜像格式都 是为单一负载设计的——容器用 OCI tar+gzip 分层格式，虚拟机用 qcow2/VHD/VMDK—— 各自的结构性成本只会随规模增长而愈发严重。\nOverlaybd 另辟蹊径：分层的、按需加载的、可寻址压缩的块设备镜像格式。一种格式， 同时覆盖容器、VM隔离的安全容器、Agent沙箱和完整的虚拟机，并且已在世界上 最大规模的生产环境中历练多年。本文就来展开论证这一点。\n现有两大阵营，为何都不够用 现有镜像技术栈可以分成两个架构阵营：\n基于文件系统的镜像。 主流的 OCI tar+gzip 格式通过 overlayfs 提供服务；懒拉取变体（eStargz、SOCI）把解包推迟到运行时，但甩不掉 tar 和文件系统语义；单一文件系统镜像（squashfs、EROFS）；以及基于 FUSE 的格式（Nydus/RAFS）。 基于块设备的虚拟磁盘。 qcow2、VHD/VHDX、VMDK——都是\u0026quot;一个虚拟块设备 + 一张固定粒度的分配表\u0026quot;，一个快照一个文件，串成链。 拿这些负载的真实需求来对照——亚秒级冷启动、数千实例并发下的效率、作为业务关键基础 设施的稳定与安全、跨运行时和跨客户机系统的统一格式——两个阵营都差在架构上。\n冷启动 标准 OCI 镜像必须完整下载、解压、解包之后，进程才能跑起来——尽管启动时真正访问到的数据只 占很小一部分。镜像有几个 G，这一步就是几十秒到几分钟。P2P 工具（Dragonfly、 Kraken）能加速分发，却省不掉下载和解压。懒拉取变体把解包推迟了，但仍然受限于 归档结构和文件系统语义。\n查找开销随深度线性增长 文件系统堆叠下，每次文件访问——open、stat、readdir——都要自上而下逐层找目录树， 复杂度随层数 O(n) 增长。VM 阵营也有同样的毛病，只是换了个形态：qcow2/VHD/VMDK 快照链上的一次读未命中，要逐级向下穿透父文件，直到某个快照里找到数据为止。厂商自己 的建议就把问题说得很清楚——VMware 一条链最多支持 32 个快照、推荐 2–3 个；微软把 “超过 50个检查点”列为问题；QEMU 专门提供了 block-commit/block-stream 来收拢链。分发模型要求深栈（基础 OS、运行时、依赖、应用、每租户定制），这些格式却 只受得了浅栈。\n索引内存，随链深成倍放大 qcow2 的两级 L1/L2 表，在默认 64 KB 簇大小下每 100 GB 虚拟磁盘要约 12.5 MB——注意这是每个文件的开销，快照链上每一环都得自带一张表、一份缓存。30 个快 照就是 30 × 12.5 MB 的索引。而 QEMU 默认的 L2 缓存只有约 1 MB，逼你二选一： 要么多占宿主机内存，要么忍受缓存频繁未命中带来的额外 I/O。VMDK 和 VHD/VHDX 只是在这条权衡曲线上选了不同的点（用更粗的粒度换更小的表）。\n粒度困境 对固定粒度的表来说，索引大小正比于\u0026quot;虚拟容量 ÷ 簇大小\u0026quot;，写时复制的成本正比于簇大小。 簇小，写入便宜、表巨大；簇大，表是小了，但一次 4 KB 的写可能触发好几 MB 的复制。 固定粒度格式都逃不出这条曲线——你只能沿着它移动，没法跳出去。文件系统格式也有类似的 税要交，形式是文件级 copy-up：第一次修改下层数据，得把整个文件复制上来。\n服务路径的复杂与安全 基于文件系统的格式，必须在宿主机或 hypervisor 里解析复杂的元数据（xattrs、ACL、 符号链接、设备节点），而这些内容来自不可信的客户机——攻击面天然就大。送进 VM 之后， 每个元数据操作都变成一次 FUSE/virtio-fs 往返，嵌套路径上的一次open() 可能要 付出五次甚至更多次跨 VM 往返。更糟的是，virtio-fs/9p 已被证明能让客户机完全绕开 内存 cgroup 和临时存储的配额计量（kata-containers#12203）。 此外，用户态 FUSE 守护进程一旦崩溃，挂载的文件系统通常直接挂死——对 7×24 小时运 行的基础设施来说，这是不小的隐患。\nOverlaybd 的优势 Overlaybd 把每个镜像呈现为一个虚拟块设备，背后是一摞块级的层。从这一个决定出发的 一系列设计，恰好逐一化解了上面的问题。\n合并的 extent 索引：任意深度都是 O(1) 镜像加载时，overlaybd 把所有层的索引合并成一张 LSMT 索引，由变长的 extent 记录 组成（每条 16 字节）。从此链深跟数据路径无关：镜像有两层还是五十层，一次读都只需查 这一张索引。索引大小看的是碎片化程度，而不是虚拟容量——在阿里巴巴的生产环境中，大于 50 GB 的镜像，合并索引平均不到 300KB，小到哪怕几千个实例并发，也能全部常驻内存。\n为速度而生的查询引擎 LBA 查找，本质是在一组有序、互不重叠的区间里做段搜索。Overlaybd 把原来的 std::lower_bound 二分查找换成了线性化 B+ 树，配合 AVX-512 批量比较做向量化 ——快了 10 倍以上，单核每秒能扛数亿次查找：\n段数量 B+树 + AVX-512 B+树 + 位掩码循环 std::lower_bound 1K 220 M/s 42.2 M/s 18.3 M/s 10K 160 M/s 30.7 M/s 12.8 M/s 100K 108 M/s 21.8 M/s 8.6 M/s 1M 57.4 M/s 15.2 M/s 5.6 M/s 这个算法快到什么程度？它甚至能用在骨干网核心路由器的 IP 地址查找上（见我的论文 PlanB, NSDI'26）。\n扇区级写入：永远无需写时复制 可写层是格式本身的一部分，按 512 字节扇区粒度建索引——这已经是任何块 I/O 的最小单位 ——所以一次写必然覆盖整扇区，永远触发不了复制。写入成本只跟写入大小有关，跟文件或簇的 大小无关。这正是打破粒度困境的关键：映射粒度和写入粒度解耦，索引小和写入便宜，两个都要。\n按需拉取与可寻址压缩 容器和 VM 按需从远端读数据就能启动，1 GB 以上的镜像也能在一秒内起来。数据被压缩成一个个 小的、可独立寻址的单元（ZFile，LZ4/zstd），随机读只需取回并解压命中的那个单元；压缩后 传输量更小，所以压缩随机读往往比不压缩还快。基于访问踪迹或文件列表的预取，可以提前把缓 存预热好；数千实例同时启动时，P2P 分发还能把拉块的压力摊到各个节点上。\n无状态、极简的服务路径 宿主机看到的只是一串块读写：不解析文件系统结构，不解释符号链接和 xattrs，也不跟客户机共 享任何会话状态——每个请求都自带服务它所需的全部信息。由此带来的好处是一连串的：攻击面就是 块接口本身，那是计算领域最古老、最久经考验的边界之一；服务崩了，重新挂接设备就能恢复；快 照、克隆、跨机恢复、迁移，都简化成\u0026quot;一次磁盘同步 + 能访问同一组后备层\u0026quot;——对动辄要在试探性 分支上 fork 的 Agent 沙箱来说，这一点是决定性的。用户态块服务构建在 PhotonLibOS 之上，这个协程运行时轻松支撑数万路并发 I/O，没有一个连接配一个线程的开销。\n一种格式，承载所有负载 块设备是计算领域最通用的存储抽象——凡是能挂块设备的，就能用 overlaybd：\n容器（runc）：看到的是宿主机上从设备挂载出来的普通 ext4 文件系统，通过 containerd snapshotter 和 OCI 镜像仓库生态交付。 安全容器（Kata、Firecracker）：通过 virtio-blk 挂接设备，元数据操作 全部在客户机内部完成，零跨 VM 往返，只有数据 I/O 过边界。 Agent 沙箱：亚秒级冷启动、廉价的快照/fork、对不可信代码的强隔离，加上共享 基础镜像撑起来的大规模并发。 虚拟机：用合并的 extent索引取代按文件维护的分配表，链再深查找也是 O(1)，还能直接复用现有镜像仓库，获得 OCI 风格的分层分发。 任意客户机操作系统：Linux、Windows、Android、macOS，都认块设备，不需要装任何 guest 代理或内核模块。里面用什么文件系统随便挑：ext4、XFS、Btrfs、EROFS、NTFS。 值得一提的是，EROFS 对 O(n) 逐层遍历问题的回答——预先构建合并视图、压平到单个块设备上给 VM 直通——本身就等于承认了同一个结论：要跳出文件系统堆叠，出路就是块设备。\n生产环境验证 这不是实验室里的研究原型：\n阿里巴巴：overlaybd 在淘宝、天猫、阿里云等全部业务线上跑了多年，并在阿里云 上商业化，成为容器镜像加速服务；函数计算的函数 microVM 也跑在同一架构上。 Azure： AKS Artifact Streaming 构建在 overlaybd 之上。 Databricks： 报告 serverless 计算的 VM 启动快了 7倍； Superhuman 在同一套基础设施上搭出了 200K QPS 的推理平台。 DeepSeek Elastic Compute： Agent 执行环境跑在 overlaybd 格式的镜像上。 fly.io： 用 overlaybd 镜像启动 Firecracker microVM； hocus.dev： 用它支撑microVM 开发环境。 Google Colab： 27 GB 的运行时镜像，热启动约 170 ms，冷启动约 5.6 s。 Kimi AgentEnv（月之暗面）： 独立重新实现了这个开放格式，作为沙箱镜像底座，做到了亚秒级启动。 这套设计本身也经过了同行评议： DADI (ATC'20)描述了阿里巴巴的大规模部署， FaaSNet (ATC'21)则把它用在了函数计算上。\n开源 Overlaybd 是 containerd（CNCF 毕业项目）旗下的开源子项目，Apache-2.0 许可证，规范文档齐全（ LSMT、 ZFile），任何人都可以独立实现：\n数据 I/O 路径：containerd/overlaybd Snapshotter 与转换工具：containerd/accelerated-container-image P2P 分发：data-accelerator/dadi-p2proxy 结语 传统格式总要让你取舍：冷启动快、元数据小、写入便宜、运行时通用，几样不可兼得。 Overlaybd 的块级设计——合并的 extent 索引、扇区级写入、按需的可寻址压缩拉取、 无状态的块设备服务路径——把这些取舍一次性抹平，而且是用同一种格式服务所有使用方。 对需要大规模启动、打快照、分发负载的基础设施来说，它是更扎实的地基。\n","date":"2026-08-10T00:00:00Z","permalink":"/p/overlaybd-the-ultimate-image-format/","title":"Overlaybd——面向所有负载的终极镜像格式"},{"content":"博客开通了。\n基于 Hugo + Stack 主题，托管在 GitHub Pages，评论功能由 Giscus 提供，中英文双语发布。\npackage main import \u0026#34;fmt\u0026#34; func main() { fmt.Println(\u0026#34;Hello, blog!\u0026#34;) } ","date":"2026-08-09T00:00:00Z","permalink":"/p/hello-world/","title":"Hello World"},{"content":"摘要 Stackful 协程（又称用户态协作式线程）有望带来更直观、更易用的并发 编程。随着高并发程序需求的增长，stackful 协程近年受到越来越多关注。 然而，由于严重依赖上下文切换，它一直因性能逊于 stackless 协程而饱 受诟病。本文对多种先进的 stackful 与 stackless 协程实现进行了深入 的测量与分析。分析表明：尽管当前 stackful 协程的实现确实明显慢于 stackless，但 stackful 协程并非天生就慢。其性能不佳的主要原因在于， 现有实现没有充分利用\u0026quot;控制流是在协程之间协作式传递的\u0026quot;这一事实。基 于此，我们提出面向 stackful 协程的上下文感知上下文切换（CACS）。 CACS 不保存完整的寄存器集合，而是根据调用方（被调方）的上下文，只 保存（恢复）最少必要的寄存器集合。它还使分支跳转能够内联在调用点， 从而让分支预测更加准确。我们已在 Photon——一个基于协程的高度优化 的 libOS——中实现了 CACS。性能测量表明，借助本文提出的优化， stackful 协程在多数场景下超越 stackless 协程，并在对 stackful 尤其 具有挑战性的 generator 范式中打成平手。我们还提出了计算机体系结构、 编程语言、编译器和操作系统方面的若干配套改进建议，可进一步提升 stackful 协程的性能。我们的工作已在 GitHub 上开源。\n引言 现代服务器的网络带宽已与其内存或 CPU 互连的带宽处于同一量级，并可 挂载数十块 SSD，每块吞吐量超过 10GB/s。随着硬件 I/O 能力的进步，软 件栈必须同时具备高并发与高效率，才能释放现代服务器不断增长的性能潜 力。近期的相关软件改进包括 DPDK、SPDK 等高性能 I/O 框架——它们以 CPU 周期为单位来预算单个数据包或请求的处理开销。\n协程近年也受到越来越多的关注，用于高效地处理并发编程。已经或正在拥 抱协程的编程语言包括：C++20、2019 年的 Rust 1.39、2012 年的 C# 5.0、2015 年的 Python 3.5、JavaScript ES2017、2021 年的 Swift 5.5、2023 年的 Java 21、2019 年的 Dragonwell 8 等。Golang 自最初设 计起就将协程（goroutine）作为一等公民。\n协程分为两类——stackless 与 stackful。前者让所有协程共享一个默认 栈，后者为每个协程分配独立的栈。使用 stackless 协程时，代码在编译 期被转换为事件处理器，并在运行期由事件引擎（即 stackless 协程的调 度器）驱动。将 CPU 控制权转交给一个 stackless 协程，本质上只是一次 函数调用，参数指向其上下文。相反，将 CPU 控制权转交给 stackful 协程 则需要一次上下文切换。与函数调用相比，这种上下文切换被广泛认为是重 量级操作。但实际上，它远比内核任务切换高效，因为它不会产生用户态到 内核态往返切换的开销；而且利用同一程序内协程的协作特性，还可以对其 进行优化。\n尽管如此，主要出于性能方面的顾虑（以及其他问题），越来越多的系统正 在放弃 stackful 协程、转向 stackless 协程，尤其是 C++20、Rust、C#、 Swift 等强调性能的系统。C++ 标准委员会曾就 C++20 应采用 stackless 还是 stackful 协程展开过激烈争论。尽管 stackful 协程通常更易用、更 兼容现有代码库、在许多场景下也更高效，但 stackless 派构造了一个微 基准测试，证明\u0026quot;fiber（stackful 协程）的上下文切换开销比 stackless 协程大 20 倍\u0026quot;。stackful 派未能正面回应这一挑战，委员会最终为 C++20 选择了 stackless 协程。我们认为，这一悬殊的开销差异在该决定中起到 了重要作用，也影响了其他系统的类似决策。\n本文主张：stackful 协程并非天生就慢，只是其实现尚未充分利用 stackful 协程的协作本质，上下文切换的效率仍有提升的机会。我们对多 种现有协程实现进行了深入测量与分析，并在此基础上提出上下文感知上下 文切换（CACS），以提高寄存器保存的效率、分支预测的准确率和 CPU 缓 存的命中率。CACS 利用了这样一个事实：stackful 协程之间的上下文切换 只发生在特定的调用点。借助 CACS，每次上下文切换只保存切换回来之后 仍需要的寄存器，而这些寄存器由编译器在调用点确定。CACS 还使分支跳 转能够内联在调用点，从而让分支预测更加准确。我们通过设计 in-stack generator，将 CACS 应用于优化非对称 stackful 协程，使其性能与相应的 stackless 协程实现相当。我们还引入了名为 preserve_none 的新函数调 用约定，作为 CACS 对所有切换函数的扩展，以进一步提升性能。有了我们 的工作，yield 操作（调度并切换到下一个协程）的开销大幅降低。CACS 在 Photon（一个基于协程的成熟 libOS）中实现，该调用约定则在 Clang 中 实现。\n另一方面，我们证明 stackless 协程在处理多级调用（真实程序中不可避 免的模式）时效率低下，处理递归时甚至会产生与调用链长度成正比的开销。 尽管某些优化在部分场景下能把该开销降为常数，它仍远高于 stackful 协 程的相应开销。我们的结果表明，stackful 协程是实现高效并发的更好选 择。\n本文的贡献如下：\n我们对最先进的 stackless 与 stackful 协程实现进行了深入的性能刻 画，分析了各种测量差异的根本原因。 我们观察到，利用\u0026quot;协程是协作式调度的用户态线程\u0026quot;这一事实， stackful 协程的性能尚有未开发的提升机会。 我们提出并实现了 CACS 来优化 stackful 协程的性能，证明它能有效 消除上下文切换的性能顾虑，从而抬高 stackful 协程的性能上限。 CACS 使 stackful 协程甚至适用于 generator 范式等具有挑战性的场 景。总体结果令人鼓舞：单个 Xeon CPU 核心执行一次 yield 操作仅需 约 1.52 纳秒（约 3.34 个周期），与函数调用的开销相当，比最先进 的结果快数倍。 尽管 CACS 的结果令人鼓舞，当前实现仍受限于现有的体系结构、编程 语言、编译器和操作系统。我们提出了这些领域的若干配套改进建议， 可进一步提升 stackful 协程的性能。 阅读全文 ","date":"2024-10-14T00:00:00Z","permalink":"/p/stackful-coroutine-made-fast/","title":"Stackful Coroutine Made Fast"},{"content":"IPMI可以让系统管理员远程控制服务器硬件，无论操作系统处于什么状态，甚至无论是否开机，只要电源已接通，就可以接受控制，因而IPMI对于实验用途的服务器意义巨大。我们的集群之前一直使用专用的IPMI网络，这需要额外的交换机、网线投入，也需要额外的安装、维护投入，而且会使线路杂乱而不利于管理。\n这次我们新配置的集群打算使用共享端口及其线路的方式来使用IPMI，其基本原理是通过VLAN复用eth0端口/线路，业务网和IPMI网分别运行在不同的VLAN当中。当eth0端口收到报文，会先判断其VLAN ID，若与IPMI的ID一致，则将报文转发给IPMI子系统，否则交给正常的报文处理逻辑。\n当我兴致勃勃地进入BIOS，想给IPMI配置网络的时候，却发现只能修改IP地址、子网掩码和网关，而网络则只能是“dedicated”，该选项是灰色，不能改。如下图所示：\n我怀疑“IPMI LAN Selection”选项不可改是因为主板集成的是比较新的万兆网卡，IPMI相关功能尚不完善，于是暂时放弃。不过当晚在网上搜索资料时，无意中发现该网卡是支持“IPMI passthrough”功能的，从字面看貌似相关功能应该是完备的，于是我决定再次尝试一下。\n这次我先在BIOS里设置好IPMI的静态IP地址、子网掩码和网关，然后通过IPMI专用端口连接网络，再用浏览器打开IPMI的web配置界面，在Configuration / Networking页面里找到了共享端口相关的设置，如下图所示。我将VLAN设置为“enable”，VLAN ID设置为“100”，Lan Interface设置为“Share”，然后通过eth0端口和VLAN 100成功访问到了IPMI功能。\n配置好共享端口之后，再次进入BIOS就可以看到IPMI的网络端口已变成“Share LAN”，而专用网络连接的状态变成“No Connect”。\n","date":"2014-03-26T22:51:49+08:00","permalink":"/p/ipmi-shared-lan-setup/","title":"共享端口的IPMI设置"}]