跳转到内容

Barrelfish / Multikernel — 把多核机器当成一个小型网络来设计 OS

是什么

Barrelfish 是 ETH Zurich 和 Microsoft Research 在 2009 年提出的一种新 OS 架构,它的核心主张叫 multikernel(多内核)

不要把 64 个 CPU 核当成一台机器,要把它们当成 64 台机器组成的小型网络。

日常类比:传统 OS 像一个大办公室,所有员工共用一个白板(共享内存),谁要写谁先抢锁。人少时还行;人到 60 个,白板前永远在排队,多请人反而更慢。Barrelfish 的做法是——给每个员工一张自己的白板,要协作就发消息,谁也不抢谁的笔。

这是对一个统治了 30 年的默认假设的正面挑战:操作系统内部应不应该用共享内存来组织?

为什么重要

不理解 multikernel 提出的背景,下面这些事都看不懂:

  • 为什么 Linux 内核里近十年加了一堆 per_cpu 变量、RCU、percpu_ref——都是在变相做”每核一份”
  • 为什么 Apple M 系列、手机 SoC(CPU + NPU + GPU)在 2020s 重新把 message passing 当默认——硬件层面已经没有统一的 cache coherence 域
  • 为什么数据中心 CXL 内存池、disaggregation 论文里反复出现”状态复制还是共享”的争论
  • 一个 SOSP 论文怎么影响主流 OS 走向,即便它本身没成为主流

核心要点

multikernel 立了 三条原则

  1. 所有跨核通信必须显式。不再依赖 cache coherence 在背后帮你同步——它代价不可见、不可控、不可扩展。要传数据,就发消息;要更新远端状态,就 RPC。

  2. OS 结构对硬件中立。核可以是不同 ISA、不同缓存层级、可以热插拔。OS 不应该假设”所有核长得一样、看到同一片内存”。

  3. 状态默认是复制的,不是共享的。每个核有自己的一份页表元数据、capability table、调度器状态。要一致就跑分布式协议(two-phase commit、agreement),就像分布式数据库做的那样。

合起来一句话:把 OS 内部当分布式系统设计

值得注意的是这三条不是凭空提出来的——它们直接对应着 2000 年代分布式系统已经反复验证过的工程教训:网络消息显式化(不要假装远端是本地)、不绑定具体硬件(节点可异构)、复制 + 协议而非共享 + 锁。Barrelfish 的贡献是把这些已知的远程分布式经验搬到”一台机器内部”。

实践案例

案例 1:Barrelfish 的运行时长什么样

  • 每核跑一个 CPU driver(极小的内核,只管本核的中断、调度、地址空间切换)
  • 每核再跑一个 Monitor(用户态进程,负责跨核协调)
  • Monitor 之间用 URPC(user-level RPC,本质是用一段 cache line 当邮箱,写入方写、读出方轮询)通信
  • 全局状态(谁拥有哪段内存、谁能发 capability)用 复制 + 两阶段提交 保持一致

对比传统:Linux 一个内核镜像跑在所有核上,访问同一份数据结构,靠 spinlock / RCU 协调。Barrelfish 是 N 个小内核 + N 个 Monitor,用消息协作。

案例 2:TLB shootdown 上的差距

unmap 一段内存时,要通知所有核把对应 TLB 条目清掉。

  • Linux 做法:发 IPI(核间中断)广播,等所有核 ack。核越多,等得越久;某核忙时全队卡住。
  • Barrelfish 做法:发消息给各 Monitor,并行处理;Monitor 间还可以用树形结构合并 ack。

论文在 4 socket、32 核的机器上测,Barrelfish 的延迟在核数增加时几乎平坦,Linux/Windows 则随核数线性涨。这是论文最有说服力的一张图。

案例 3:URPC 比 shared-memory IPC 还快

直觉上”发消息肯定比直接读写内存慢”。但跨 socket 时,shared memory IPC 要让 cache line 在两个 socket 间反复搬(cache line bouncing),代价巨大。URPC 用单向 producer-consumer 模式,只让 cache line 单向流动一次,反而更快。

这个反直觉的结果说明:在多 socket / NUMA 上,“共享内存”是一种抽象,硬件代价并不便宜。

案例 4:和传统微内核的关键差别

L4 / Mach 这类微内核也强调消息传递,但它们的消息仍假设共享内存底座——内核里的 capability 表、调度信息只有一份。Barrelfish 多走一步:连内核内部状态都按”分布式”组织,每核一份,状态变化要走协议。

可以这样区分:

  • 微内核:把内核做小,把服务推到用户态(垂直方向减少特权代码)
  • multikernel:把内核做”多份”,每核一份独立运行(水平方向打散内核)

两者正交,可以叠加——Barrelfish 本身的 CPU driver 就很像微内核。

踩过的坑

  1. 核数少时反而更慢:在 2-4 核上,URPC 的 overhead 比直接 shared memory 大。multikernel 的好处只有在核数足够多、cache coherence 真的撞墙时才显出来。

  2. 复制有内存代价:每核一份页表元数据、调度器状态——核多了内存占用线性涨。论文承认这是 trade-off。

  3. 编程模型变难:内核黑客原本写 lock(); shared->x++; unlock();现在要想”这数据在哪几个核有副本?怎么保证一致?谁是 leader?“。这是把分布式系统的复杂度搬进了 OS,门槛高。

  4. 历史没沿这条路走:2025 年回头看,Linux 没变成 multikernel——它用 RCU、per-cpu、lockless 数据结构把 lock 撞墙问题缓解了大半。multikernel 的预言在工业界部分落空。但论文提的诊断(共享内存不是免费的)影响了 Linux 后来十年的演进方向。

  5. 测试场景偏极端:论文里最亮眼的扩展性数据集中在 TLB shootdown、内存映射这类最坏情况——它们恰好是 lock 争用最严重的内核操作。常规工作负载(数据库、web 服务)的差距没那么夸张,读论文容易被”漂亮折线图”误导。

适用 vs 不适用场景

适用

  • 异构核 / 异构 ISA 系统(big.LITTLE、CPU+NPU+GPU 没有统一一致性域)
  • 几百核以上、cache coherence 协议本身成瓶颈的系统
  • 内核态需要”按核独立失败”的场景(一个核挂掉不拖垮全机)
  • 学术研究和教学——multikernel 把 OS 内部解构得很清楚
  • 安全/隔离要求强的环境——每核独立状态天然减少攻击面(一核被攻陷不直接污染他人)

不适用

  • 中等规模(4-32 核)通用服务器——Linux + RCU 已经够用
  • 对 latency 极敏感的低核数场景——message passing overhead 不划算
  • 已有大量 shared-memory 内核代码的迁移场景——重写成本巨大
  • 内存吃紧的嵌入式系统——每核一份元数据的开销不可承受

历史小故事(可跳过)

  • 1995 Hive (Stanford):早期把 OS 拆成多个互信”细胞”协作,但仍共享内存。算 multikernel 的精神祖先。
  • 2000s K42 (IBM):研究 OS 在大 SMP 上的 locality,强调 per-CPU object,但没走到完全消息传递。
  • 2009 Barrelfish (ETH + MSR):第一次明确说”把 OS 当分布式系统”,并实现完整原型。同期 MIT 的 fos、Berkeley 的 Akaros 思路接近。
  • 2010s:Linux 用 RCU / per-cpu / lockless 改善 scalability,工业界没采纳 multikernel。但论文成为 OS 课经典。
  • 2020s:异构 SoC、CXL 内存池、disaggregation 让”无统一一致性域”重新变成常态,multikernel 的思想被翻新使用。
  • 现在回看:Barrelfish 项目本身在 2010s 中后期热度下降,但 ETH 团队把这套方法学带到了后续的 Akaros、Multikernel-on-RDMA 等研究里。它更像一次”极端实验”——结论被吸收,载体被替换。

一句话记忆

Barrelfish 不是给你一个新 OS,是给你一种新视角:当你面对 64 核机器时,不要默认它是”一台多核电脑”,先问一问”如果它是 64 台用网线连起来的电脑,OS 该怎么写?” ——很多看似自然的设计选择,会立刻显得不再自然。

给零基础读者的额外提示

如果你之前没接触过操作系统内核:

  • 共享内存就是”两个 CPU 看同一块 RAM”——日常你写多线程程序,靠它通信
  • cache coherence 是 CPU 硬件帮你做的”两边缓存自动同步”,看似免费,背后协议很贵
  • lock(锁)是软件层的”轮流写”机制,多人抢一把锁时,等着的人不能干活
  • 消息传递 就是发短信——你不直接动对方东西,给对方发个请求,对方处理完回你

理解了这四个概念,再回头看 multikernel 的三条原则,会发现它整体非常自然。

学到什么

  1. 共享内存不是免费的——cache coherence 的代价在 64 核以上变得不可忽视,但平时被语言和硬件藏起来了
  2. 同一个问题可以选不同抽象——OS 可以选”一份数据 + lock”,也可以选”多份数据 + 协议”,前者简单后者可扩展
  3. 跨学科借力——把分布式系统几十年的成果(replication、agreement、message passing)搬进 OS,是一次教科书式的”领域迁移”
  4. 预言不一定全对,诊断仍然有价值——multikernel 没成为主流,但它指出的问题(lock 撞墙、coherence 不可扩展)推动了主流 OS 的渐进式改良
  5. 激进设计的角色——并非每个研究系统都要被产品采纳;它的价值可能是”把一种极端推到底”,让中间路线(Linux 的渐进改良)方向更清晰

延伸阅读

  • 论文 PDF:Baumann et al. SOSP 2009(16 页,可读性高)
  • 项目主页:Barrelfish OS(ETH Zurich 维护,源码在 GitLab)
  • 后续论文:Schüpbach et al. “Embracing diversity in the Barrelfish manycore operating system”(2008 workshop 版,铺垫思想)
  • exokernel-1995 —— 同样反对”OS 替你做决定”,但走的是”把策略推到用户态”的路
  • paxos-1998 —— Barrelfish 内部状态一致性需要的协议家族
  • amdahl-law-1967 —— 解释为什么核数增加 lock 撞墙就会让加速比饱和

关联

  • exokernel-1995 —— 同时代的另一种”重做 OS”思路:把策略推到用户态,而不是把内核拆分布式
  • paxos-1998 —— Barrelfish 复制状态需要的一致性协议;论文里直接借鉴
  • amdahl-law-1967 —— 共享内存 + lock 在多核上必然撞 Amdahl 上界,这是 multikernel 的动机起点
  • fast-paxos-2006 —— 复制协议在低延迟场景下的优化方向

反向链接

  • amdahl-law-1967 —— Amdahl 定律 — 串行比例决定并行加速比的上界
  • arrakis-2014 —— Arrakis 2014 — 让操作系统只管规则、硬件直接服务应用
  • demikernel-2021 —— Demikernel — 微秒级数据中心的 datapath OS 架构
  • disco-1997 —— Disco — 让没改过的商用 OS 在 64 核大机器上一起跑
  • flexsc-2010 —— FlexSC — 把系统调用从同步陷入改成异步队列
  • haven-2014 —— Haven — 把整个应用装进 CPU 黑盒,让云服务商也看不见
  • ix-2014 —— IX 数据面操作系统 — 用虚拟化把高吞吐和低延迟同时塞进内核
  • mirage-2013 —— MirageOS Unikernels — 应用即内核,把操作系统编译掉
  • paxos-1998 —— Paxos 1998 — 古希腊议会寓言里藏的共识协议
  • snap-2019 —— Snap 2019 — Google 把网络栈搬进用户空间的微内核实践
  • sprite-1988 —— Sprite 1988 — 把一屋子工作站伪装成一台大主机