MIT 6.1810 · 第 4 课

OS 组织与系统调用

内核应该提供什么抽象?是所有功能都放进内核(宏内核),还是只保留最小功能(微内核)?本课以 L4 微内核为例,探讨这两种设计哲学的权衡。

授课:rtm (Robert Morris) 日期:2025-01-30 难度:中等

传统方法:大抽象 + 宏内核

UNIX、Linux、xv6 都采用这种设计哲学:

大抽象的好处

受益方收益
程序员便捷(操作文件而非磁盘块)、可移植(POSIX 标准)
内核帮助共享和管理资源(分配磁盘空间给不同程序)
内核帮助安全(文件权限检查)

宏内核的问题

问题说明
复杂 → bug 多Linux 有数百万行代码,CVE 层出不穷
过度通用 → 慢发送一个字节经过管道:缓冲、锁、sleep/wakeup、调度器——代码路径极长
强制设计决策不能等待非子进程、不能修改其他进程的地址空间、DB 无法自定义磁盘布局

微内核:另一种思路

代表:L4、Mach(CMU,最有影响力的早期微内核)。

核心思想

最小的内核,把大部分 O/S 功能移到用户态服务进程。

内核只提供:

期望收益

设计挑战

L4 微内核

L4 经过多年演进,至今仍用于手机和嵌入式控制器。强调最小性:

L4Linux
几十个系统调用300+ 系统调用
~13,000 行代码数百万行代码

L4 基本抽象

抽象说明
Task(地址空间)一组页映射的集合
Thread执行上下文(寄存器、栈)
IPC线程间的同步消息传递
Page Fault 拦截pager 可处理其他地址空间的页错误
硬件访问直接访问设备寄存器/端口
中断接收中断作为 IPC 消息投递

L4 系统调用

示例:L4 上的 exec()

假设有一个用户态文件服务器,T1 想创建新任务并执行文件中的程序:

  1. T1 创建新 task T2(此时 T2 没有任何内存)
  2. T1 告诉 L4 让 T2 从 PC=0 开始执行
  3. T2 立即访问无效内存 → 页错误
  4. L4 通过 IPC 把页错误信息发送给 T1
  5. T1 通过 IPC 请求文件服务器读取文件块
  6. T1 得到文件块后,告诉 L4 映射到 T2 的地址空间,恢复 T2

这就是 按需分页(demand paging)——通过页错误驱动文件访问。

IPC 性能:微内核的阿喀琉斯之踵

慢速 IPC 设计(基于 UNIX 管道)

// 异步、缓冲
send(id, msg):   // 拷贝 msg 到内核队列,返回
recv(&id, &data): // 等待队列消息,拷贝到用户空间

// 一次 RPC 需要 4 次系统调用:
//   client: send() → recv()
//   server: recv() → send()
// = 8 次用户/内核切换 + 4 次消息拷贝

L4 的快速 IPC

Jochen Liedtke, 1993, "Improving IPC by Kernel Design":

优化效果
合并 call/sendrecvIPC 通常是 RPC,合并后减少 2× 用户/内核切换
同步(Synchronous)call() 直接切换到目标的 sendrecv(),无需经过内核队列
无缓冲(Unbuffered)同步意味着双方缓冲区地址已知,直接内存到内存拷贝
小消息用寄存器无需内存拷贝
大消息用页映射发送方页映射到接收方地址空间,零拷贝
内核代码 cache 友好减少 cache miss

结果:IPC 成本降低 20×!

在微内核上构建完整操作系统

问题

工作站/服务器需要兼容现有应用(如 UNIX 程序)。如何在微内核上提供完整的 UNIX 环境?

实用方案:把 Linux 作为用户态服务运行

L4/Linux 方案:修改少量 Linux 代码,把硬件访问替换为 L4 系统调用/IPC。Linux 本身变成用户态的"O/S server"。

L4/Linux 系统调用流程

用户进程:
  fork() → libc → IPC(call) → 阻塞等待回复

L4 内核:
  转发 IPC → Linux server

Linux server:
  执行 fork() 实现(分配内存、创建新 L4 task、复制父进程内存)
  IPC(reply) → 用户进程恢复

Linux server 如何访问用户内存?

Linux server 分配所有用户内存,因此用户内存也映射在 server 地址空间内。copyin()/copyout() 可以直接解引用用户指针。fork/exec 时 server 可直接操作用户内存——IPC 只需传地址,不需拷贝数据。

性能评估

Härtig et al., 1997, "The Performance of micro-Kernel-Based Systems"。

IPC 微基准测试:getpid() 成本

系统getpid() 时间
Linux1.68 μs
L4/Linux3.95 μs
Mach+Linux15.41 μs

L4/Linux 大约是 native Linux 的 2×——但比 Mach 快 4×。

整体系统基准测试:AIM

AIM 启动大量进程,随机使用磁盘、内存分配、管道、计算——模拟真实工作负载。

结果:Native Linux 最好,L4/Linux 仅慢一点,Mach+Linux 明显低效。

结论:2× 系统调用开销对整体性能影响不大——微内核的性能惩罚是可以接受的。

现状

领域微内核使用情况
嵌入式广泛使用(微控制器、Apple Secure Enclave)
通用计算未普及(无足够理由从 Linux 切换)

微内核的许多思想已被现代 UNIX 采用:


知识检查

Q1:微内核设计的主要目标是什么?

Q2:L4 快速 IPC 的关键优化是什么?

Q3:L4/Linux 中,Linux 内核运行在哪里?

Q4:根据 L4/Linux 论文,L4/Linux 的 getpid() 时间约为 native Linux 的多少倍?

Q5:宏内核设计的主要缺点是什么?


本课要点

大抽象 vs. 小抽象

UNIX 提供大抽象(文件系统、socket),L4 只提供最小抽象(地址空间、IPC)。各有利弊。

宏内核 vs. 微内核

宏内核性能高但复杂;微内核简洁但 IPC 开销大。现代 OS 是两者的混合。

IPC 是关键

微内核的性能瓶颈在 IPC。L4 通过同步、无缓冲、寄存器传参将 IPC 成本降低 20×。

实用主义

L4/Linux 把 Linux 作为用户态服务——兼容现有应用,同时利用微内核的优势。

延伸阅读