跳转到内容

Mach — 把内核拆成消息互通的小服务

是什么

Mach 是 1985 年 CMU 几位研究者搞的一个操作系统内核。它做了一件颠覆当时常识的事:把内核里大部分功能搬到外面去

日常类比:以前的内核像一栋一体化大办公楼,水电、保安、厨房、文件柜全在同一栋楼里——任何一处出问题,整栋楼跳闸。Mach 把它改造成”管委会 + 一群独立小公司”:管委会只管收发邮件(消息)和分房间(地址空间),文件、网络、驱动各自变成楼里的独立租户。

这种思路叫微内核(microkernel)。Mach 是微内核里走得最远、最有影响力的一个,至今活在你手里的 macOS 和 iOS 内核(XNU)中。

为什么重要

不理解 Mach,下面这些事都连不起来:

  • 为什么 macOS 内核叫 XNU,里面同时有 BSD 和 Mach 两套接口
  • 为什么 iOS 安全隔离能做到那么细——Mach port 是底层抽象之一
  • 为什么 1992 年 Tanenbaum 和 Linus 大吵一架,吵的就是”微内核 vs 单体内核”
  • 为什么 GNU Hurd 喊了 30 年还没成型——Mach 教训过他们

核心要点

Mach 把内核能做的事压到 5 个抽象

  1. task(任务):一个资源容器 + 一个地址空间。类比:一间出租屋,里面摆什么家具自己决定。
  2. thread(线程):在 task 里跑的执行单位。一个 task 可以有多个线程同时跑。
  3. port(端口):受保护的消息队列,本质是一张”信箱凭证”。谁手里有这张凭证,谁就能往里塞信。
  4. message(消息):带类型字段的数据包,可以塞普通数据,也可以塞 port 凭证本身(把信箱钥匙寄给别人)。
  5. memory object(内存对象):虚存页面的来源。外部分页器机制让用户态进程也能当”硬盘”,自己决定怎么换页。

内核只管这 5 件事。文件系统、网络、UNIX 兼容层全都是用户态进程,互相通过 port 发消息聊天。

实践案例

案例 1:你 Mac 里跑的就是 Mach 后裔

打开 macOS 终端,输入:

Terminal window
ps -A | grep launchd

launchd 是 macOS 第一个用户进程,它和系统服务之间的通信走的就是 Mach IPC(mach_msg)。XPC、Notification Center、Sandboxing 全都建在 Mach port 上。

Apple 的设计是 Mach 2.5 风格的混合内核——保留 Mach 抽象,但把 BSD 系统调用塞进同一个地址空间,避免纯微内核的性能税。

案例 2:消息传递长什么样

伪代码(C 风格):

mach_port_t server_port; // 服务端的"信箱"
mach_msg_t msg; // 待发送消息
msg.header.msgh_remote_port = server_port;
msg.header.msgh_local_port = MACH_PORT_NULL;
msg.header.msgh_id = REQUEST_OPEN_FILE;
strcpy(msg.body.path, "/etc/passwd");
mach_msg_send(&msg); // 投到 server 的信箱

服务端从 server_port 取出消息、处理、回复。整个过程发生在用户态两个进程之间,内核只负责把信送过去。

案例 3:外部分页器是怎么”违反直觉”的

传统 UNIX 缺页时,内核自己读硬盘把页面调进来。Mach 里:

  1. 进程访问一块虚存,触发缺页
  2. 内核不读硬盘,而是给”内存对象”对应的用户态分页器进程发消息:“请提供第 N 页”
  3. 分页器爱从哪儿读从哪儿读——本地硬盘、网络、压缩内存、甚至另一台机器
  4. 把数据回给内核,内核填进物理页

这样分布式共享内存变得可能,因为分页器可以跨机器协同。代价是多一次 IPC,性能损耗在 Mach 3.0 上变得致命。

案例 4:port 是怎么充当”安全凭证”的

port 不是一根管道,而是一张带权限的凭证

  • 拿到 send right 的进程才能往里发消息
  • 拿到 receive right 的进程才能取消息(每个 port 只能有一个)
  • 你可以把 send right 当数据塞进消息里发给别人——动态授权

这其实就是 capability 安全模型的原型:能力即凭证、凭证可传递、可撤销。后来 seL4、Fuchsia 的 zircon 内核全是这一脉。

踩过的坑

  1. 性能税大到劝退:Mach 3.0 纯微内核比同期 UNIX 慢约 50%,单次 IPC 大约 114μs,相比 syscall 的 21μs,开销主要在端口能力检查、上下文切换和缓存抖动。

  2. 外部分页器看不见 OS 状态:内核不知道 page 属于什么用途,吃紧时换页决策很差。Mach 3.0 在内存压力下表现尤其拉胯。

  3. Tanenbaum-Torvalds 1992 大辩论里成了反面教材:架构漂亮但工程现实里被单体内核(Linux)按性能吊打,让微内核研究停滞约 10 年。

  4. 多 OS personality 是 demo 营销:宣传里同一台机跑 DOS + UNIX 互相调用,实际跨 personality 走多次 IPC + 切地址空间,效率惨到没人真用。

  5. GNU Hurd 30 年没成型:把 UNIX 服务(文件系统、网络、auth)拆成一组用户态 server、用 Mach 端口互相调用,听起来很美,但死锁、调试、性能、信号语义全是坑。这反过来印证 Mach 3.0 工程难度被低估了多少。

适用 vs 不适用场景

适用

  • 强隔离需求:iOS Secure Enclave、可信执行环境——隔离价值高于性能
  • 需要 OS personality:macOS 同时支持 BSD 和 Mach 两套 ABI
  • 形式化验证目标:seL4 走的就是 Mach 思路 + 极致精简

不适用

  • 追求裸金属性能:每个 syscall 走 IPC 太贵,单体内核(Linux)赢
  • 小内存嵌入式:Mach 内核本身约 5 万行 C,对小设备太重
  • 服务紧密耦合:所有功能反正都要互相调,拆成进程纯粹增加 IPC

历史小故事(可跳过)

  • 1981 年:Rick Rashid 在 CMU 的 PERQ 上做 Accent 内核,已有 port + message 雏形
  • 1985 年:Rashid 和 Avie Tevanian 启动 Mach,目标是把 Accent 思路移植到主流硬件并兼容 BSD
  • 1986 年:USENIX 这篇论文首次系统介绍 Mach
  • 1989 年:NeXT 选 Mach 2.5 做 NeXTSTEP 内核
  • 1996 年:Apple 收购 NeXT,Mach 通过 NeXTSTEP 成为 Mac OS X / macOS / iOS 的内核底座
  • 后续:Rashid 去微软创立 Microsoft Research;Tevanian 跟 Steve Jobs 到 NeXT、再到 Apple 当软件 SVP

你现在手机里跑的内核,本质上是 1986 年那篇论文的直系后裔。

学到什么

  1. 抽象层数 vs 性能不是免费的:每多一层 IPC 就多一份开销,理论再漂亮也得过 cycle 这一关
  2. 混合方案常常赢纯粹方案:Mach 2.5(混合)活下来,Mach 3.0(纯)输给 Linux
  3. port 这个设计真的好:把”凭证”做成一等公民,可以传递、可以撤销,是后来 capability 安全模型的源头
  4. 学术影响 ≠ 商业影响:Mach 学术上是微内核教科书,商业上是被 NeXT 抓住才存活;研究漂亮不等于自动落地

延伸阅读

关联

  • xnu-kernel —— macOS / iOS 内核,直接继承 Mach 抽象
  • l4-1995 —— 受 Mach 性能教训驱动设计的下一代微内核
  • gnu-hurd —— 把 UNIX 拆成一组用户态 server 跑在 Mach 上的尝试
  • unix-1974 —— Mach 的前身参照,单体内核代表
  • capability-system —— port 凭证是 capability 思想的直接祖先

反向链接

  • amoeba-1990 —— Amoeba — 把整个机房当一台操作系统
  • arrakis-2014 —— Arrakis 2014 — 让操作系统只管规则、硬件直接服务应用
  • ffs-1984 —— FFS — 把磁盘几何写进文件系统
  • l4-1995 —— L4 — Liedtke 用 12KB 内核反驳”微内核必然慢”
  • mach-vm-1987 —— Mach VM — 把虚拟内存抽象成”对象”,与硬件解耦
  • selinux-2001 —— SELinux 2001 — 给每扇门都装上门卫,而不是给管理员一把万能钥匙
  • snap-2019 —— Snap 2019 — Google 把网络栈搬进用户空间的微内核实践
  • sprite-1988 —— Sprite 1988 — 把一屋子工作站伪装成一台大主机
  • v-system-1988 —— V 分布式系统 — 把局域网当成一台机器,内核只剩进程加 IPC