参考: 中国科学院大学 2025年秋《高级操作系统教程》课件
重要设计原则:策略与机制的分离
- 策略(Policy):要做什么 – 相对动态
- 机制(Mechanism):该怎么做 – 相对静态
- 操作系统可仅通过调整策略来适应不同应用的需求
操作系统架构分类:
操作系统结构的演进与生态:
- 系统软件需要一条演进之路
- 尽可能集成现有的POSIX API/Linux ABI
- 避免棘手的系统调用(如fork)
- 避免不可扩展的POSIX API
- 系统软件一直在不断演化
- 例:Linux Userspace I/O (UIO),向微内核近了一步
- 单节点下也存在更多的分布式、低时延的可编程设备
- 非易失性内存的出现可能推动存储层次在OS中的完全改革
Monolithic-Kernel, 宏内核
整个系统分为内核与应用两层
- 内核:运行在特权级,集中控制所有计算资源
- 应用:运行在非特权级,受内核管理,使用内核服务
实例:Windows, Linux, Unix, BSD, xv6
优点:
- 宏内核拥有丰富的沉淀和积累
- 拥有巨大的统一的社区和生态
- 针对不同场景优化了30年
- 内核设计者不需要决定哪一部分运行于完整特权之下
- 操作系统各组件之间更容易交互(但更复杂)
缺点:
- 结构性缺陷
- 安全性与可靠性问题:模块之间没有很强的隔离机制
- 实时性支持:系统太复杂导致无法做最坏情况时延分析
- 系统过于庞大而阻碍了创新:Linux代码行数已经过2千万
- 难以满足很多场景
- 向上向下的扩展
- 很难去剪裁/扩展一个宏内核系统支持从KB级别到TB级别的场景
- 硬件异构性
- 很难长期支持一些定制化的方式去解决一些特定问题
- 功能安全
- 一个广泛共识:Linux无法通过汽车安全完整性认证(ASIL-D)
- 信息安全
- 单点错误会导致整个系统出错,而现在有数百个安全问题(CVE)
- 确定性时延
- Linux花费10+年合并实时补丁,目前依然不确定是否能支持确定性时延
- 向上向下的扩展
Microkernel, 微内核
设计原则:最小化内核功能
- 将操作系统功能移到用户态,称为”服务”(Server)
- 在用户模块之间,使用消息传递机制通信: IPC
优点:
- 易于扩展:直接添加一个用户进程即可为操作系统增加服务
- 易于移植:大部分模块与底层硬件无关
- 更加可靠:在内核模式运行的代码量大大减少
- 更加安全:即使存在漏洞,服务与服务之间存在进程粒度隔离
- 更加健壮:单个模块出现问题不会影响到系统整体
缺点:
- 性能较差:内核中的模块交互由函数调用变成了进程间通信
- 生态欠缺:尚未形成像Linux一样具有广泛开发者的社区
- 重用问题:重用宏内核操作系统提供兼容性,带来新问题
实例
Mach 微内核
第一代微内核,CMU 开发
实现功能:
- 任务和线程管理
- 任务,是资源分配的基本单位;线程,是执行的基本单位
- 对应用提供调度接口,应用程序可实现其自定义的调度策略
- 进程间通信(IPC):通过端口(port)进行通信
- 内存对象管理:虚拟内存
- 系统调用重定向:允许用户态处理系统调用
- 支持对系统调用的功能扩展,例如,二进制翻译、跟踪、调试等
- 设备支持
- 通过IPC实现(通过port来连接设备)
- 支持同步设备和异步设备
- 用户态的多进程
- 类似用户态的线程库,支持wait()/signal()等原语
- 一个或多个用户态线程可映射到同一个内核线程
- 分布式支持
- 可透明地将任务与资源映射到集群中的不同节点
- Mach 允许用户态代码实现 Paging
- 应用可自己管理自己的虚拟内存
- 重定向功能(Redirection)
- 允许发生中断/异常时,直接执行用户的二进制
- 这种连接不需要对内核做修改
L3/L4
- L4的IPC性能比Mach快20倍
- IPC仅传递信息
- 使用寄存器传参,限制消息长度
- 内核去掉了IPC的权限检查等功能,交给用户态判断
- 系统服务的接口直接暴露给用户态,可能导致DoS攻击
启发了大量相关系统: Pistachio, L4/MIPS, Fiasco 等
seL4
- 基于L4的微内核
- IPC机制:端点(endpoint)
- 通过Capability进行IPC的权限判断
- Capability可被复制和传输
- 第一个完成形式化验证的内核
QNX Neutrino
满足实时要求: 广泛用于交通、能源、医疗、航天航空领域,如波音
Google Fuchsia
- Google 开发的全新 OS
- 试图覆盖多个领域,用途尚未完全宣布
- 使用 Zircon 微内核
- 仅提供IPC, 进程管理, 地址空间管理等功能
主要优化目标:优化 IPC 开销
IPC 开销来源:
- 特权级的频繁转换
- IPC 调用本身在进程间的数据拷贝
- 减少 IPC 数量
- 将部分对性能要求高的 server 重新放回内核(混合内核架构)
- 合并部分 server (将 server 之间的 IPC 调用改为函数调用)
- 减少单次 IPC 延时
- DMA 传输数据
- 零拷贝(server 进程间共享部分内存)
Exokernel, 外核
- 不提供硬件抽象
- “只要内核提供抽象,就不能实现性能最大化”
- 只有应用才知道最适合的抽象(end-to-end原则)
- 不管理资源,只管理应用
- 负责将计算资源与应用的绑定,以及资源的回收
- 保证多个应用之间的隔离
设计原则:“将管理与保护分离”, 整个操作系统分为 Exokernel(内核态) + Libos(用户态)
优点:
- OS无抽象,能在理论上提供最优性能
- 未修改应用性能最多提升4x,定制化应用性能最多提升8x
- 应用对计算有更精确的实时等控制
- LibOS在用户态更易调试,调试周期更短
缺点:
- 计算资源的利用效率主要由应用决定
- 定制化过多,导致维护难度增加
生态问题:Linux as a LibOS?
- 将Linux作为LibOS或Unikernel
- 例:LKL - Linux kernel library (https://github.com/lkl)
- 将系统调用变为普通函数调用
- 可提供一定的兼容性,有效避免重复开发
- 许多新问题
- Linux是否适合作为LibOS/unikernel?
- fork()如何处理?
- 尚在探索阶段
LibOS
- 策略与机制分离:将对硬件的抽象以库的形式提供
- 高度定制化:不同应用可使用不同的LibOS,或完全自定义
- 更高性能:LibOS与应用其他代码之间通过函数调用直接交互
Exokernel 外核设计
功能:
- 追踪计算资源的拥有权 -> 安全绑定(Secure binding)
- 保证资源的保护 -> 显式回收(Visible revocation)
- 回收对资源的访问权 -> 中止协议(Abort protocol)
安全绑定(Secure binding)
将 LibOS 与计算资源绑定
- 可用性:允许某个 LibOS 访问某些计算资源(如物理内存)
- 隔离性:防止这些计算资源被其他 LibOS 访问
例:利用software TLB保证LibOS只使用了自己的物理内存
- LibOS 可直接修改页表,因此可能会将自己的页表指向其他 LibOS 物理页
- Software TLB 是软件可控的 TLB,MIPS 等处理器支持
- 每次发生 TLB miss 时,由 Exokernel 负责遍历页表并填写对应的 TLB 项
- Exokernel 可在填写 TLB 项时检查 LibOS 对内存的使用是否合法
显式回收(Visible revocation)
- Exokernel 与应用之间的协议
- Exokernel 显式告知应用资源的分配情况
- 应用在租期结束之前主动归还资源
中止协议(Abort protocol)
- 若应用不归还资源,则强制中止
- Exokernel 拥有对资源的控制权
- 主动解除资源与应用间的绑定关系
实例
Unikernel(单内核)
- 可以看做虚拟化环境下的LibOS
- 每个虚拟机只使用内核态
- 内核态中只运行一个应用+LibOS
- 通过虚拟化层实现不同实例间的隔离
- 适合容器应用场景
- 每个容器就是一个虚拟机
- 每个容器运行定制的LibOS以提高性能
部分开源项目:
- Rumprun
- POSIX接口,BSD兼容的运行时环境
- 运行在Xen虚拟化平台之上
- Drawbridge
- 来自微软,兼容Win32接口的运行时环境
- OSv
- 与Linux兼容的应用环境,单地址空间
Libra
基于虚拟机实现的JVM
Drawbridge
MultiLibOS
Multikernel, 多内核
设计动机:解决多核与异构带来的扩展性问题
- 多核:OS内部维护很多共享状态
- Cache一致性的保证越来越难
- 可扩展性非常差,核数增多,性能不升反降
- 异构: GPU 等设备越来越多
- 设备本身越来越智能——设备有自己的CPU
- 通过PCIe连接,主CPU与设备CPU之间通信非常慢
- 通过系统总线连接,异构SoC
设计思路:
- 默认的状态是划分而不是共享
- 硬件资源在操作系统启动时必须静态划分
- 维持多份状态的copy而不是共享一份状态
- 提供单一系统镜像
- 显式的核间通信机制
- 在每个core上运行一个小内核 (包括CPU、GPU等)
- OS整体是一个分布式系统
- 应用程序依然运行在OS之上
问题:
- 资源调度困难
- 安全性问题:内核 A 可以通过直接访问底层硬件(每个内核的特权级都是最高的)访问内核 B 的资源
- 软件无法解决
- 硬件方案:标签化内存控制,IBM 的硬件隔离虚拟化
Barrelfish
第一个多内核架构 http://www.barrelfish.org/documentation.html
- 来自ETH Zurich和微软研究院
- 支持异构CPU
- 在CPU核与节点之间提供通用异构消息抽象
设计结构:
- 每个core对应一个内核
- 类似”CPU驱动”,适应不同CPU
- 负责执行系统调用,处理中断/异常
- 事件触发,单线程,不可中断
- 内核调度并运行”Dispatcher”
- Dispatcher
- 类似线程
- 多个Dispatcher组成一个Domain
- Domain
- 类似进程
提供本地 IPC 和远程 IPC 实现对于不同资源的访问
Popcorn Linux
- 支持异构体系结构(ARM、x86等)
- 多个 Linux 内核副本
- 一套代码编译不同副本
- 不同 ISA 不同副本
- 多个副本同时向上提供 OS 服务
将 Linux 移植为 Popcorn Linux ,需要改启动代码,虽然扫描到所有硬件,但只能使用其中一部分硬件(静态划分)
Tessellation
设计理念:
- 时空分割(STP,Space-Time Partition)
- 二级调度(Two-Level Scheduling)
- 应用先分配到内核上,再由内核分配到相应的 CPU 上
FOS
- 设计理念
- 空间共享取代时间共享
- 将应用程序与系统服务区别对待,为系统服务提供专用核心
- 消息传递机制代替共享内存机制,内核之间不共享数据
- 三层架构
- 微内核层
- 系统服务层
- 应用程序层
- 每个核上跑一个 microkernel
- 应用程序只绑定到服务该程序的内核,减少上下文切换和 cache 切换
HeilOS
设计理念:
- 多内核、单一镜像架构
- 一个主内核 + 其他微内核
- 亲和度(Affinity Metric)
- 二级编译
K2
- 双内核:适配强弱核
- 基于同构 ISA ,调度大小核
LegoOS
SplitKernel
- 分离内核功能
- 每个核上跑一个 microkernel
- 把一个内核的功能拆开,每个子功能作一个 microkernel 分给其他核
- 不同的硬件上运行不同的硬件程序
- 模块之间通过消息传递来交换
- 全局的资源和错误管理