Appearance
内核设计
- 写作时间:
2026-03-22 首次提交,2026-07-13 最近修改 - 当前字符:
6451
操作系统与硬件把操作系统放在应用与硬件之间:它既要管理资源,也要提供稳定抽象。接下来要追问的是,这么多职责怎样组织在一个可维护、可保护、又能高效执行的软件系统中。
来看一次文件读取。应用提交请求后,内核要检查 fd,并通过文件系统定位数据;页缓存未命中时,内核才把读取请求交给存储设备驱动。文件系统和驱动应该都放在最高权限的同一地址空间中,还是拆成受隔离的服务?前者调用直接,后者故障范围更小,这个取舍形成了内核结构的主要分歧。
我们先用 内核 明确讨论对象,再分别看同地址空间的 宏内核 和缩小特权核心的 微内核。真实系统还出现了不能简单归入二者的 结构变体。结构不是孤立选择,背后受到 设计原则 约束;这些原则又来自一代代系统解决旧瓶颈的 操作系统演化。
内核
内核是操作系统中以受保护权限运行、直接管理硬件资源并为用户程序提供基础服务的核心程序。
内核通常负责进程与 CPU 调度、虚拟内存、文件系统、网络协议、设备驱动和安全检查。虚拟内存是内核为进程建立地址翻译、分配与保护的机制,地址空间与分页会从映射关系开始完整构造这套机制。用户程序不能直接修改内核数据,只能通过受控入口请求服务;硬件中断和执行异常也会把控制权交给内核。特权边界会从 CPU 层面解释这项限制,这里先关注进入内核以后各项功能怎样组织。
“内核”不等于所有系统软件。Shell、编译器、桌面环境和大量系统服务都运行在用户空间,它们可以停止和替换;内核则维持进程、内存与设备的共同运行基础。一个 Linux 系统实例由一份正在运行的内核管理,却可以同时运行许多用户程序。虚拟机则利用硬件和宿主软件模拟一台独立计算机,因此同一物理机器还可以运行其他客体内核。
设计内核时至少要同时考虑三类目标:
| 目标 | 需要回答的问题 |
|---|---|
| 性能 | 一次系统服务要经过多少切换、消息和数据复制 |
| 隔离 | 一个组件故障能破坏多大范围 |
| 演化 | 新驱动、新文件系统和新策略怎样加入而不重写全局 |
这三类目标经常互相制约。把组件放在一起可以减少边界开销,却扩大共享状态和故障范围;把组件隔开可以限制内存破坏,却需要额外通信。接下来分别为这两种取舍建立结构模型。
宏内核
宏内核(monolithic kernel)是把主要操作系统服务放在同一个特权地址空间中,并允许组件通过普通函数调用协作的内核结构。
Linux 的调度、内存管理、统一文件系统层、网络栈和大多数设备驱动都运行在内核地址空间。文件系统要提交磁盘请求时,可以直接调用块层和驱动接口;调用参数不必先编码成跨进程消息,相关数据结构也能在受控规则下共享。因此宏内核的常用路径较短,组件之间可以紧密优化。
同一地址空间也意味着这些组件属于同一个内存故障域(failure domain),也就是一次错误内存访问可能直接破坏的共享范围。设备驱动若越界写入,可能破坏与自己无关的内核对象;内核函数若持锁后未释放,可能阻塞其他子系统。硬件特权级无法隔离两个都在内核态运行的 C 函数,所以宏内核的可靠性依赖代码正确性、接口约束、测试与额外防护。
宏内核不等于源代码没有模块边界。Linux 通过子系统接口划分职责,还支持可加载内核模块(Loadable Kernel Module, LKM)。设备驱动或文件系统可以编译成模块,在运行时加载和卸载,而不必重新启动到另一份完整内核。模块提高了部署灵活性,但加载后仍在内核地址空间执行,因此不提供类似进程地址空间的隔离。
Linux 由此常被称为模块化宏内核。这里的“宏”描述运行时的权限和地址空间关系,“模块化”描述代码与部署组织,两个判断并不冲突。
微内核
微内核(microkernel)是只在最高权限中保留调度、地址空间和进程间通信等最小机制,并把其他服务放入独立用户态进程的内核结构。
进程间通信(Inter-Process Communication, IPC)是两个隔离进程交换消息或共享数据的机制。微内核中的文件系统、网络服务和设备驱动通过 IPC 请求彼此工作,而不是任意解引用对方内存。内核负责把消息送到正确服务,并维持不同地址空间之间的保护。
假设文件系统服务向磁盘驱动请求一个块。宏内核可以完成一次函数调用;微内核通常要构造消息、让发送者等待、调度驱动服务、验证消息,再把完成结果送回。现代微内核会用快速 IPC、共享内存和批处理降低成本,但结构边界仍然真实存在。
这项成本换来更小的特权核心和更明确的故障边界。用户态驱动发生非法内存访问时,CPU 的地址空间保护可以阻止它直接改写微内核或其他服务;系统还可能重启该驱动并恢复服务。这里要保留边界:失控驱动仍可能错误配置设备、丢失请求或让服务不可用,微内核不会自动消除所有故障。
QNX、seL4 和 L4 系列展示了不同微内核路线。它们都强调缩小特权核心,但具体保留哪些机制、怎样组织用户态服务并不相同。“微内核”是结构类别,不是一套唯一实现。
结构变体
结构变体是围绕宏内核与微内核的性能、隔离和可扩展性取舍形成的其他内核组织方式。
混合内核(hybrid kernel)通常保留微内核式的分层与对象设计,却为了性能把许多服务放回内核态。Windows NT 与 macOS XNU 常被归入这一类。不过分类边界并不严格:如果大部分服务共享特权地址空间,它的运行时故障域仍具有宏内核特征,“混合”主要说明设计来源和内部组织。
外核(exokernel)采用另一种方向。它让内核尽量只负责安全地分配和复用底层资源,把文件系统、网络协议等高层抽象交给库操作系统(library OS)。不同应用可以选择不同库策略,从而减少通用抽象带来的限制;代价是接口、兼容性和应用部署更复杂。外核主要影响了研究与后续系统设计,没有取代通用宏内核或微内核。
这些结构名称帮助我们比较取舍,但不能仅凭名称推断安全或性能。一次具体请求经过哪些权限边界、复制和共享状态,才是可以验证的机制。
设计原则
操作系统设计原则是帮助设计者在机制、策略、权限和组件边界之间作出可解释选择的通用约束。
机制与策略分离(separation of mechanism and policy)是其中一项核心原则。机制提供“能够做什么”,策略决定“何时、对谁这样做”。CPU 上下文切换(context switch)负责保存当前执行状态并恢复另一份状态,这是机制;从具备运行条件的进程中选择下一位运行者,这是策略。选择规则可以改变,而底层切换过程不必随之重写。
这项分离依赖稳定接口。调度策略只调用“把任务加入队列、移出队列、选择下一个任务”等约定,切换代码也不需要知道策略为什么选中某个任务。CPU 调度会为这些对象补上正式名称,这里先抓住因果关系:能力与选择规则分开,二者才能独立演化。
最小权限原则(principle of least privilege)要求组件只拥有完成当前任务所需的权限、对象范围和有效时间。普通应用不需要修改页表,就不应获得这项能力;网络服务只需要绑定端口,也不应同时获得加载内核模块的权限。权限越小,错误或攻击成功后的影响范围通常越小。
Linux 权能(Linux Capabilities)把传统超级用户的一组全局权限拆成多个可以分别授予的能力。例如 CAP_NET_BIND_SERVICE 允许进程绑定低编号端口,CAP_SYS_MODULE 允许加载或卸载内核模块。这样,服务不必仅为一项操作获得完整 root 权限;不过某些权能仍然覆盖很广,授予前仍要分析它能够影响哪些内核对象。保护与安全会继续解释权能怎样进入进程凭据与权限检查。
模块化(modularity)要求组件通过明确接口表达依赖,避免任意访问对方内部状态。模块化本身不等于隔离:Linux 模块有接口边界,却仍共享内核地址空间;微内核服务同时具有接口与地址空间边界。区分这两层以后,“代码拆成多个文件”就不会被误认为已经建立安全边界。
操作系统演化
操作系统演化是系统软件围绕硬件利用率、交互性、可移植性和协作开发不断增加机制的历史过程。
早期计算机一次只运行一个作业,操作员要人工装载程序,昂贵的 CPU 会在准备期间空闲。批处理系统(batch system)把作业排队并自动依次运行,先减少了人与机器切换作业的空档。
作业自动排队以后,程序等待慢速 I/O 时 CPU 仍然无事可做。多道程序(multiprogramming)让多个作业同时驻留内存,一个等待 I/O 时运行另一个。调度、内存分配和进程保护由此成为操作系统必须统一处理的问题。
CPU 利用率提高以后,用户仍要等待整批作业结束才能看到结果。分时系统(time-sharing system)用短时间片快速切换交互式任务,让多位用户能够同时操作机器。终端、进程、权限和响应时间开始成为系统设计中心。
UNIX 在分时背景下进一步强调小而稳定的接口:进程、文件描述符、层级文件系统、管道以及可组合工具。它的影响不在于“发明了所有机制”,而在于把一组机制组织成可移植、可组合的系统模型。
GNU 工具与 Linux 内核随后提供了可自由修改的 Unix-like 软件栈。Linux 采用宏内核结构,但通过开放源码、模块化子系统和跨架构移植持续扩展。本书对照 Linux,不是因为它代表唯一正确架构,而是因为它同时提供了广泛使用的真实系统和完整可读的实现。
| 阶段 | 主要瓶颈 | 推进的机制 |
|---|---|---|
| 手工运行 | 装载作业时 CPU 空闲 | 批处理 |
| 单道批处理 | I/O 等待时 CPU 空闲 | 多道程序与调度 |
| 非交互运行 | 用户无法及时交互 | 分时与终端 |
| 系统接口复杂 | 程序难以组合和移植 | Unix 进程、文件与管道模型 |
| 封闭实现 | 协作、修改与移植受限 | GNU/Linux 开放开发生态 |
历史的价值不在于记住年份,而在于看到机制对应的问题。CPU 调度、虚拟内存和IPC 机制都会继续沿着“旧状态有什么具体瓶颈,新增机制改变了哪条约束”推导。
小结
| 概念 | 说明 |
|---|---|
| 内核 | 以受保护权限管理硬件并提供基础服务的核心程序 |
| 宏内核 | 主要服务共享特权地址空间,并通过函数调用协作 |
| 微内核 | 只保留最小特权机制,把其他服务放入隔离进程 |
| 结构变体 | 混合内核、外核和框内核等不同取舍路线 |
| 设计原则 | 机制与策略分离、最小权限和模块化等通用约束 |
| 操作系统演化 | 从硬件利用率到交互性、接口与开放协作的问题推进过程 |
内核结构没有脱离场景的最优答案;只有把一次真实请求经过的调用、消息、权限和故障边界展开,才能判断某种结构为目标系统付出了什么、换回了什么。
Linux 源码入口:
include/linux/sched.h:调度子系统接口与任务描述结构kernel/module/:可加载内核模块实现kernel/capability.c:Linux Capabilities 检查入口
特权边界将从软件组织回到硬件权限:即使采用宏内核,普通应用也不能直接调用这些内核函数,CPU 必须先建立用户态与内核态之间的权限边界。