近年来,我国自主研发操作系统被反复提及。近期,一个计算机领域内非常专业的词来到了几乎所有人的视线内,这就是——微内核。人们对操作系统内核的讨论越来越热烈,甚至有人说我不懂什么叫微内核,什么是宏内核,接下来本文将带领大家了解操作系统内核架构,微内核与宏内核到底孰优孰劣?
操作系统内核架构
随着操作系统功能的不断增多和代码规模的不断扩大,提供合理的层级结构,对于降低操作系统复杂度、提升操作系统安全与可靠性来说变得尤为重要。图1列举了一些常见的操作系统内核架构。下面我们对这些结构进行简要的分析。
图1 操作系统内核架构的频谱:简要结构(如DOS)、宏内核(如UNIX/Linux)、
微内核、外核与多内核等
1.1 简要结构
一些功能较为简单的操作系统,会选择将应用程序与操作系统放置在同一个地址空间(addressspace)中,无须底层硬件提供复杂的内存管理、特权级隔离等功能。MS-DOS(MicroSoft Disk Operating System)是采用了简要结构的一个典型例子。该结构的一个优势在于,应用程序对操作系统服务的调用可直接通过函数调用高效完成,但缺点也同样明显,任何一个应用或操作系统模块出现了问题,均有可能使整个系统崩溃。随着操作系统功能的不断增加,简要结构会使操作系统的设计与实现难度越来越高,难以持续演进。
尽管缺乏隔离能力,简要结构的操作系统依然采用了一定的模块化与层次结构以降低复杂度。图2展示了MS-DOS的内部结构:MSDOS.Sys模块通过命令行接口与用户交互,并负责与设备驱动交互以实现对硬件设备的管理;I/O子系统(IO.Sys)实现对硬件设备I/O访问的管理,并以I/O请求作为抽象为MSDOS.Sys和设备驱动I/O提供服务。
图2 MS-DOS的系统结构
除了MS-DOS外,当前采用简要结构的操作系统还包括FreeRTOS与uCOS等。这些操作系统主要运行在微控制单元(MicroController Unit,MCU)等相对比较简单的硬件上,这些硬件通常没有提供现代意义上的内存管理单元(MemoryManagement Unit,MMU),隔离能力较弱或缺失,难以运行(往往也不需要运行)复杂的操作系统。
1.2 宏内核架构
宏内核(Monolithic kernel)又称单内核,其特征是操作系统内核的所有模块(包括进程调度、内存管理、文件系统、设备驱动等)均运行在内核态,具备直接操作硬件的能力,这类操作系统包括UNIX/Linux、FreeBSD等。图3展示了一个典型的宏内核架构。在操作系统代码中,通常会有类似arch/arm/的目录,用于封装与体系结构相关的功能实现。
图3 宏内核的基本结构
由于操作系统内核的功能日趋复杂,宏内核架构的操作系统也逐步采用M.A.L.H方法对功能进行模块化、抽象、分层、层级等,以控制其不断增长的复杂度。下面是一些典型的方法。
1.模块化:现代操作系统(如UNIX、Linux、Windows等)的内核均采用模块化的策略来组织各个功能。为进一步提高功能的可扩展性,现代操作系统通常还提供了可加载内核模块(Loadable Kernel Module,LKM)机制。例如,当前大部分设备驱动是以可加载模块的形式存在的,与内核其他模块解耦,使驱动开发与驱动加载更加方便、灵活。
2.抽象:现代操作系统内核均广泛采用抽象的方法来降低复杂度并提高可维护性。例如,UNIX将文件作为一个重要的抽象,提出“一切皆是文件”(everythingis a file),将数据、设备、内核对象等均抽象为文件,并为上层应用提供统一的接口。
3.分层:宏内核架构的操作系统一开始就采用了分层的架构。例如,图灵奖获得者EdsgerDijkstra在1968年提出的“THE”操作系统将操作系统分为6层,如图4所示。现代操作系统内核也均存在一定程度的分层结构,以更好地组织各种功能。图5展示了Linux文件系统的分层结构。
4.层级:层级的概念同样被广泛应用于内核的资源管理中,如调度子系统中对进程优先级的分类,控制组(cgroup)对进程层级的分类,内存分配器对不同内存的分类等。
图4 “THE”操作系统的分层结构
图5 文件系统的分层结构
通过各种复杂度控制方法,Linux已经演进为一个超过2800万行代码的复杂系统,成为世界上最大的开源协作项目,每年有超过1800人为Linux提交补丁来修复问题以及添加新功能。然而Linux同样面临挑战:一个通用的、适用于大部分场景的设计,常常意味着很难满足特定场景下对安全性、可靠性、实时性等方面的需求;同时,在一个庞大的系统中进行创新也变得越来越困难,这使得一些较大的创新(如网络、文件系统、设备驱动等)开始往用户态迁移。
1.3 微内核架构
随着宏内核操作系统的内核功能不断增长,系统的复杂度也持续增加,在可靠性、安全性等方面导致了更多的问题。这是因为在宏内核架构下,所有内核模块均运行在特权空间,一个单点的错误就可能会导致整个系统崩溃或者被攻破。哪怕是具有极强编程能力的操作系统内核程序员编写的内核系统也很难避免bug。在Andrew Tanenbaum等人的论文中提到,在一般的工业界系统中每千行代码大约会有6~16个缺陷。虽然很多缺陷在正常运行时不会被触发,部分缺陷即使被触发也不会引起显著后果,但对于一个千万行代码级的软件而言,潜在的缺陷数量也是触目惊心的。
因此,研究人员尝试对宏内核架构的操作系统进行解耦,将单个功能或模块(如文件系统、设备驱动等)从内核中拆分出来,作为一个独立的服务部署到独立的运行环境中;内核仅保留极少的功能,为这些服务提供通信等基础能力,使其能够互相协作以完成操作系统所必需的功能。这种架构被称为微内核(Microkernel)。在微内核架构下,服务与服务之间是完全隔离的,单个服务即使出现故障或受到安全攻击,也不会直接导致整个操作系统崩溃或被攻破,从而能有效提高操作系统的可靠性与安全性。此外,微内核架构带来了机制与策略的进一步分离,也可以更方便地为不同场景定制不同的服务,从而更好地适应不用的应用需求。
Tips
小知识:最早的微内核操作系统
一些读者可能认为微内核架构是一个比较新的设计。事实上,早在1969年,UNIX系统开始设计的时候,类似微内核架构的操作系统就已经出现。Per Brinch Hansen开发的RC 4000多路编程系统在历史上第一次将操作系统的组件分离为各个相互交互的功能组件,以及一个负责消息通信的内核。Per Brinch Hansen在RC 4000中也首次提出了分离机制与策略的原则,以及管程(Monitor)这个概念。
微内核的发展到目前为止经历了三代。Mach是第一代微内核的代表。1975年,Mach起源于罗彻斯特大学,后来主要在卡内基梅隆大学开发。Mach将很多内核功能以单独服务的形式运行在用户态;然而,Mach对进程间通信(Inter-ProcessCommunication,IPC)的设计过于通用,加上Mach微内核自身资源(包括内存与CPU缓存等)占用过大的问题,使得其性能与同时期的宏内核相比存在差距,甚至有人据此将微内核与性能差关联起来。
微内核的性能一定差吗?德国国家信息技术研究中心的JochenLiedtke深入分析了Mach微内核系统的性能,指出较差的性能不是微内核的必然结果。Jochen认为,高性能IPC的设计与实现必然是与体系结构相关的,过度抽象将极大影响IPC的性能,而利用体系结构相关的状态进行优化则可将IPC性能提升到极致。为此,Jochen Liedtke设计并实现了L4微内核系统,并提出了微内核的最小化原则:一个操作系统内核的功能只有在将其放在内核态以外会影响整个系统的功能时,才能被放置在内核态。通过高性能的IPC实现以及极小化的微核(即微内核系统的内核态部分,又称祂ernel),微内核架构操作系统的性能可以达到甚至超过同时期的宏内核架构操作系统。L4被认为是第二代微内核操作系统的代表。
随着L4等微内核操作系统在实时、高安全等场景的广泛应用,研究人员开始对微内核的安全性进行进一步的增强。EROS首次将能力(Capability)机制引入微内核操作系统中,并高效地实现了该机制(关于Capability机制详见《现代操作系统:原理与实现》 16.2.6节)。Capability机制允许更精确、更细粒度地给不同应用程序授予对内核对象的调用权,从而能更好地提升系统安全性。seL4[10-11]是一个典型的基于Capability机制的微内核;谷歌正在实现的Fushcia微内核操作系统同样基于Capability机制实现了访问控制。此外,seL4还引入了形式化证明方法(详见第18章),通过数学的方式证明了其微核部分满足从设计到实现的一致性,以及微核上的服务具有互不干扰(non-interference)等属性。这些安全增强能力成为第三代微内核架构操作系统的重要特征。
宏内核vs.微内核
自宏内核与微内核这两种操作系统架构出现伊始,人们就两者的优劣与特点展开了多次深入的讨论。当前,随着一些新场景、新诉求的出现,使类似微内核架构的操作系统架构再次受到关注。
1.弹性扩展能力:对于一个宏内核来说,很难仅仅通过简单的裁剪或扩展,使其支持资源诉求从KB到TB级别的场景。
2.硬件异构性:异构硬件往往需要一些定制化的方式来解决特定问题,这种定制化对于宏内核来说很难得到长期的支持。
3.功能安全:由于宏内核在故障隔离和时延控制等方面的缺陷,截至目前尚无通过高等级功能安全认证(例如,汽车行业的ASIL-D)的先例。
4.信息安全:宏内核架构的操作系统存在较大的信息安全隐患,例如内核态驱动容易导致低质量的驱动代码入侵内核,粗粒度权限管理容易带来权限漏洞等。
5.确定性时延:由于宏内核架构资源隔离较为困难,且各模块耦合度高导致难以控制系统调用的时延,因此较难做到确定性时延;即便为时延做一些特定优化(例如Linux-RT补丁),时延抖动仍然较大。
在真实世界中,正如体系结构领域的RISC与CISC之争一样,宏内核与微内核往往会互相借鉴。例如,Intel处理器采用了CISC的指令集架构,但其微架构实现则采用了RISC架构;类似地,Linux等宏内核架构操作系统也采用了一些微内核的设计思想。例如,尽管Linux的创始人Linus Torvalds在20世纪90年代与AndrewTanenbaum论战时表明将驱动放到用户态是个不靠谱的想法,但近期Linux也逐步采用了一些用户态驱动模型(如UIO与VFIO等);Android操作系统在Treble项目中同样将部分驱动放到了用户态,并通过名为Binder的IPC机制来与这些驱动进行交互。
Tips
小思考:小的操作系统内核就是微内核吗?
不是。有一些操作系统内核(如FreeRTOS、uCOS-II等)虽然很小,但是不具备现代意义上的操作系统功能,包括虚拟内存、用户态和内核态分离等。因此它们应该被归为本章提到的简要结构内核。
软件是计算系统的“灵魂”,而操作系统则是软件运行和支撑技术的核心,“CPU+操作系统”更是成为信息产业生态的核心、信息时代安全的基石。自1956年第一个实用操作系统诞生以来,操作系统已历经60多年的发展。它一方面是伴随以CPU为代表的硬件及其组成结构的发展,另一方面是为了支持多机、分布和网络环境,以及满足新型计算模式和新型应用的需求。迄今,以20年左右为周期,操作系统已出现从主机计算时代到个人计算时代,再到移动计算时代的两次重大变迁,每次变迁均涉及计算设备及其用户两方面的数量级的跃升,同时诞生了新的“CPU+操作系统”生态。当然,从技术本质看,操作系统“向下管理各种计算资源,向上为应用程序提供运行环境和开发支撑,为用户提供交互界面”的角色定位未变。
当前,万物互联、人机物融合计算的泛在计算时代正在开启。以云计算、大数据、人工智能和物联网等为代表的新型应用场景,多种不同架构的CPU、GPU和加速器与以新型存储、传感设备等为代表的新型硬件,以及嵌入式、移动计算、边缘计算、云计算等不同规模的计算系统,使得操作系统的内涵和外延均发生了重大变化,新一轮的重大变迁正在孕育中。支撑泛在计算的“泛在操作系统”将成为新的操作系统形态,并催生新的“CPU+操作系统”生态。这一轮变迁将促进新的操作系统研究与实践,在带来新机遇的同时,也会产生新的挑战和更激烈的竞争。