微内核
1.Microkernel and Monolithic-kernel
来自Wiki的图片:
2.微内核(以下出自 Microkernels)
1)简介
传统上,操作系统的“内核”是系统上运行的所有其他软件都需要的部分。因此,内核对应于可信计算基础(Trusted Computing Base ,TCB)的OS部分,这些部分的正确行为是任何其他部分正确操作的先决条件。
微内核的基本思想是最小化内核,并在TCB之外实现尽可能多的内核。内核应该只导出简单的、低级的操作,希望这些操作能够实现更高效的应用程序。这个想法可以追溯到[Per Brinch Hansen在1970年发表的<The nucleus of a multiprogramming system>],一个好的概述可以在[J. Liedtke. Toward Real Microkernels. 1996]中找到。传统的内核是“单结构的”,在某种意义上说,它们在TCB内以相对非结构化的方式实现其所有功能。从软件工程的角度来看,微内核的优点是显而易见的:
- TCB变得更小,减少了由于它的错误实现而产生错误的机会。
- 操作系统更加模块化,因此更加灵活和可扩展性。
- 在TCB中以前的服务现在可能有多个不同的实现,甚至可能并发运行。
微内核最初受到了极大的热情,在80年代后期,在学术和商业环境中都有很多关于微内核的工作。当人们开始遇到似乎与微内核实现的灵活性和效率有关的内在困难时,最初的热情消退了:
- 对于一些频繁的操作,例如网络化,上下文切换的开销对于内核实现来说太大了。因此,微内核不像最初想象的那样有效。
- 支持多个基本系统服务的实现存在很大的困难,特别是当多个并发运行时。因此,微内核并不像假设的那样灵活。
通过对原始微内核理想的各种妥协克服了这些困难。几个效率关键的特性被重新引入内核,并且通过可下载的二进制代码或可信的“内核加载模块”支持向内核添加新特性。
但是,将大多数驱动程序和服务器集成回TCB,在很大程度上消除了微内核方法的好处,最终导致更复杂的单内核操作系统内核。
最近,在微内核理念之后出现了几种第二代实现,它们声称可以解决第一代的灵活性和效率问题。这些实现如何实现这个目标有很大的不同,以下三个具有代表性:
- L4 [J. Liedtke. Toward Real Microkernels. 1996]拥有一个非常小的微内核,并按照第一代的精神使用用户级服务器。它通过非常快速的进程间通信和灵活的内存管理方案来实现它的目标。
- Exokernel,是一个微型微内核,它消除了所有传统的OS内核抽象,将内核的任务简化为安全的多任务资源。
- SPIN采用的方法是将代码下载到内核中,但是是以一种安全的方式,不会真正扩大TCB。
目前尚不清楚这些新一代解决方案是否真正适用于实际操作系统。缺少的是使用上述系统的策略构建的真实操作系统的存在,就像Chorus和Mach是使用第一代微内核策略构建的真实操作系统一样。尽管如此,上述系统的出现给微内核方法的未来带来了巨大的希望。
2)第一代
在八十年代末和九十年代初期,有几个真正的基于微内核的操作系统。也许其中最引人注目的两个是Mach和Chorus。Mach在CMU开发,是最成功的,并且已经被应用到几个商业系统中,比如NeXTSTEP OS。
2.1 最初的目标
- 一个小而简单的内核意味着更少的代码,以及更简单的代码,在TCB中,因此出错的几率更小。
- 一个干净的微内核设计将加强并支持整个操作系统的高度模块化结构。
- 较小的内核意味着更大的灵活性和可扩展性,因为妨碍更改的现有结构较少。添加新服务要容易得多,因为服务器彼此独立运行,理想情况下不需要内核提供任何特定于服务的支持。
- 以前内核中的服务,现在由外部服务提供,以此使用内核服务,如多任务处理和进程间通信(IPC)。
- 服务器可能会在操作系统没有崩溃的情况下崩溃,操作系统甚至可能启动一个新的服务器副本来替换掉发生故障的服务器。
- 设备驱动程序可以作为服务器运行,并获得上面描述的所有服务器好处。
- 微内核应该能够以不同服务的形式支持多个特性,例如APIs、覆盖一般的OSs等,而不需要在成熟的操作系统上进行仿真。
2.2 效率低下的原因
- 1.在用户模式下运行以前内核集成的服务,需要在内核或客户端使用服务时,使用(RPC)远程过程调用(用两个IPC实现)。RPC的成本比对内核的简单系统调用要高得多,大约是每RPC 200微秒,而传统的系统调用是40微秒。
- 2.在微内核操作系统中,当运行相同的应用程序时,内存引用的成本(每个指令的内存周期,MCPI)要比宏内核操作系统高得多。原因包括:
- 3.微内核的“遗留”接口和实现代码不支持某些操作系统特性的有效功能。这在很大程度上是由于微内核的开发历史,它是从宏内核派生出来的,继承了它们的许多接口和实现策略。
2.3 进程间通信的性能
OS | microseconds | instructions |
Mach | 115.0 us | 1150 |
L4 | 5.0 us | 50 |
Exokernel | 1.4 us | 30 |
SPIN | 102.0 us | 1100 |
3) 目前这一代
3.1 L4
L4是J. Liedtke和他的团队在1995年开发的,是L3的直接后代,L3是第一代微核。L4的从头开始开发,加上它的小尺寸和简单的接口,使它能够摆脱第三种"遗留"代码类型的微内核问题。
L4基于微内核是处理器相关的理论,即像代码优化器一样,微内核本身就不能移植,尽管它们提高了整个系统的可移植性。L4提供了三个抽象,地址空间、线程和IPC,并且只实现了七个系统调用。L4中的线程是用户级的,但是内核支持通信和地址空间管理。最后两个抽象,地址空间和IPC,在L4中特别有趣,下面将详细讨论。
3.1.1 地址空间
L4微内核提供了实现物理内存管理和各种保护方案的基本机制。基本思想是支持内核之外的地址空间的递归构建,如下面的图1所示。
图1 L4中递归构造的地址空间
初始地址空间表示物理内存,并由初始内存服务器拥有。虚拟页面的所有者可以使用L4提供的操作,将其所有权转移到(grant)或与(map)共享,这是一个同意的接收进程。所有者还可以从接收者的地址空间中删除任何共享页面(demap),而不需要在两个进程之间达成任何协议。
此功能允许实现内核之外的内存管理和分页。映射和解调足以在微内核之上实现内存管理和分页。授予仅在特殊情况下使用,以避免双重簿记(bookkeeping)和地址空间溢出。为了验证进程一致性,采用IPC实现内存管理和分页,进一步强调了快速IPC的必要性。
3.1.2 IPC实现
L4继承了L3改进版本的IPC实现,速度惊人,其性能如表1所示。IPC时间的这种数量级改进是通过各种优化技术实现的。下面我们将讨论其中的一些技术,这些技术带来了最大的性能收益。
- 传递短消息的IPC消息中很大比例是非常短的。通过在寄存器中传输这些消息,可以获得两倍的性能改进。
- 复制大量数据信息,大多数微内核通过两个副本在进程之间传输消息,从进程A空间到内核空间再到进程B空间。性能影响非常大,特别是对于长消息,因为会发生Translation Lookaside Buffer(TLB)和缓存未命中。L4允许通过临时与发送方共享目标区域的方式进行单拷贝传输,从而使512字节的消息性能提高了两倍,对更长的消息性能提高更多。
- 延迟调度,传统的IPC需要更新线程调度程序队列。可以通过延迟队列内/队列之间的线程移动来提高性能,直到队列被查询。这种“延迟”调度是通过在线程控制块中设置状态标志来实现的,然后在查询时扫描应该移动到不同队列的线程的队列。这削弱了队列的语义,偶尔会导致代价高昂的队列操作,但是总体上提高了小消息IPC的性能大约25%。
3.2 Exokernel
Exokernel,于1994 - 1995年在麻省理工学院开发。其开发背后的主要动机是内核提供的抽象代价太高而且限制了灵活性。本论文得到了“end-to-end”论证的支持:应用程序比OSs更清楚它们的资源管理决策的目标是什么,因此,应该尽可能多地控制这些决策。
因此,微内核应该只提供最小必要的原语集,以安全地多路复用硬件资源。高级功能由库OSs提供,库OSs是用户提供的,不受Exokernel信任。为了使Exokernel尽可能简单和高效,甚至它的导出接口也依赖于硬件。大多数时候,Exokernel只是导出硬件功能。
图2 基于Exokernel的应用程序/操作系统的结构
图2显示了基于exokernel的系统的结构,两个应用程序使用不同的库OSs。由于Exokernel必须适应不受信任的高级别OSs,其主要目标是安全地导出和复用硬件原语和资源。Exokernel使用了三种技术来实现这一点:安全绑定、可见资源撤销和中止协议,下面将讨论其中的每一种。
3.2.1 安全绑定
由于资源的实际管理留给了库操作系统,因此必须有一个将资源保护与资源管理分开的机制。安全绑定就是这种机制,Exokernel使用三种不同的技术实现它们:硬件机制、软件缓存和可下载的应用程序代码。
一种常见的硬件机制是TLB。安全绑定可以实现为从虚拟地址到物理地址的映射,例如,作为TLB入口。如果硬件TLB不够大,则将其封装在较大的软件TLB中,以缓存此类映射。在一些Silicon Graphics系统上可用的另一种硬件机制是具有与每个像素相关联的所有权字段的帧缓冲器。在那里,硬件处理帧缓冲区的保护,只有当像素改变所有权时才涉及到Exokernel。还可以将代码下载到内核中,在每个资源访问或事件中调用它来确定所有权以及内核应该执行的操作。包过滤器就是这种代码的一个例子。
实际上,下载代码的这个特性不仅可以用于安全绑定的实现。更一般地说,可以将特定于应用程序的安全处理程序(ASH)下载到内核中,并允许它执行一般的计算。这种功能是基于性能的,用于减少内核和用户模式之间的上下文切换,特别是在时间限制使这些切换不切实际的情况下,如包接收上的网络ack。ASHs和所有下载的代码都是不受信任的,并且通过代码检查(使用类型安全语言)、保护解释和沙箱(与SPIN类似)的组合而变得安全。
3.2.2 可见资源撤销
可见撤销比传统的隐形撤销具有更高的延迟,但允许库操作系统指导解除分配并了解资源稀缺性。Exokernel导出物理名称以提高效率和更好的资源管理,因为这允许消除间接级别,而且物理名称通常编码有用的资源属性。这使得可见撤销成为必要,因为库OS需要更新它的物理引用。
3.2.3 中止协议
由于库操作系统不受信任,因此Exokernel应该适应他们的不合作。当撤销请求没有及时得到满足时,Exokernel有权“强制”打破任何相关的安全绑定,并重新获得所请求的资源。Exokernel通知不友好的库操作系统撤销,但不采取任何更激烈的措施。另一种方法是杀死相关的应用程序,但这是为了减少损失而决定的。
3.2.4 保留
Exokernel方法是非常激进的,它激发了人们对原始微内核的热情。然而,这些微内核的命运应该有一个发人深省的影响。应该注意的是,主要的Exokernel论文没有讨论OSs的几个关键方面,比如文件系统,而且它们对高效框架缓冲区的特殊硬件的依赖感觉像是作弊。因此,硬件级非抽象的Exokernel方法可能只适用于专门的机器代码级应用程序。即便如此,这种不完整性可能只是当前Exokernel实现的不成熟的标志,而不是该方法的固有问题。确实有一篇新的论文,它讨论了高效的“Exo-file-system”的实现。
3.3 SPIN
SPIN,基于Mach微内核的最新版本,增加了下载安全代码的能力,从而扩展了内核。因此,它在某种程度上表明了为抵御第一代微内核操作系统的低迷性能而开发的最新一代技术。因此,这里的介绍既合适又必要。
SPIN使用语言和链接时机制来允许动态扩展内核服务。应用程序可以将新OS服务的代码下载到内核中,因此可以根据自己的特定需求创建细粒度的OS接口。扩展由内核编译器转换,动态链接到内核并在内核的虚拟地址空间内执行。
3.3.1 灵活性
上面的内核可扩展性方法立即提出了安全问题:用户提供的代码可以信任吗?SPIN通过使用类型安全语言Modula-3和沙箱下载的代码来解决这个问题。因此,编译器执行静态分析并插入运行时检查,确保编译后的代码不会违反内核、其他模块或用户级代码的完整性。由于内核不对导入代码的安全性做出任何假设,因此可以声称编译后的扩展不会增加TCB。每个单独模块的隔离保证了如果应用程序崩溃,例如,由于下载到内核的扩展模块错误,系统的其余部分不会受到影响。
SPIN提供了一组用于管理内存和处理器资源的核心服务。它们的级别类似于宏内核操作系统中的内部接口。内核扩展可以将核心服务用作构建块,也可以执行任意计算。核心服务包括支持对虚拟内存的应用程序控制的操作,例如地址的转换服务。核心服务还包括用于进程管理的低级接口,定义要由用户线程包和调度程序处理的一组事件,以及包括确定事件的接收者线程包的全局调度程序。
3.3.2 性能
内核扩展在灵活性和可扩展性方面的优势是显而易见的,甚至可以在线修改内核。至于性能,向内核添加功能在一定程度上减少了内核和进程之间的IPC数量。这在某种程度上补偿了测量的高IPC成本。即便如此,应用于L4的优化也可能适用于SPIN,从而降低了剩余IPC操作的成本。
另一个可能更重要的性能问题是内核编译器的效率。证明代码模块安全并不是一项简单的任务,安装新内核扩展所产生的延迟有时是不可接受的。但是,当前版本的SPIN使用了一个外部编译器,这是对以前的解释器或基于内部编译器的系统的改进。在SPIN中,编译器可以是多任务的,并且内核的大小更小。对编译器接口的进一步改进可能包括缓存已编译的代码以减少分摊的编译时延迟,以及处理嵌套IPC重定向等事务的专门优化。
4) 总结
首先让我们看一下上面检查的三个系统如何满足2.1节中所述的微内核的原始目标。与传统的宏内核相比,这三个系统的性能都是一样的,或者更好,这三个系统都可以支持新的应用程序结构(分别是递归地址空间、库OSs和内核扩展)。那么剩下的就是检查2.1节的枚举列表:
- 这三个都有小型TCB,L4和Exokernel的大小只有几KB。SPIN是三者中最大的,和Mach(几百KB)一样大。
- 这可以从两方面来论证这三种系统。L4提供了一组简单的接口,并且确实支持模块化结构。但是,Exokernel和SPIN允许应用程序和OS部件分别根据需要在内核外部和内部交织在一起。
- 这三个系统似乎都满足这个标准。Exokernel缺乏抽象,确实非常灵活;L4具有尽可能灵活的简单抽象;SPIN有一个直接可扩展的内核。然而,对于L4和SPIN来说,是否可以有两个完全不同的服务器同时运行同一资源,这一点尚不清楚,因为给定的抽象可能不够灵活。
- 这在所有三个系统中都得到了满足,尽管由于内核内部和外部运行的代码的交互,传统上比SPIN更复杂。
- 所有这三个系统都完全支持这一点,至少对于不需要访问硬件特性的服务器是如此,这些硬件特性可能会破坏整个系统,比如DMA。然而,对于这些服务器,没有什么可以消除系统损坏的可能性,尽管静态检查和沙箱应该会减少问题。
- 与上面相同。
- 对于Exokernel尤其如此,对于L4也是如此。Spin还应该能够支持多个特性,但由于其内核的重量级特性,尚不清楚是否所有代码都需要在内核中运行,如果需要的话,必要的内核膨胀是否会产生许多负面影响。
从上面可以看出,当前的微内核已经解决了2.2节中提到的IPC和遗留问题,但是它们似乎并没有解决微内核中高MCPI的问题。这实际上是一个值得关注的大问题,因为随着内部CPU速度的快速增长,缓存性能越来越成为一个问题。然而有人指出,使内核小到足以完全适应缓存,将消除这个问题。SPIN在内核中进行计算的方法也可能解决这个问题,尽管内核很大。
目前这一代微内核的一个共同方面是可扩展性,通过下载代码验证安全。这种能力对于未来的高速网络和分布式OSs显得至关重要,特别是在进程切换的相对成本急剧上升的情况下。因此,奇怪的是L4还不支持内核可下载扩展。然而,阅读L4的论文就会发现,该系统实际上没有很好的网络性能,人们可以猜测,他们用可下载的扩展来纠正这一点只是时间问题。
第二代微内核导出的接口的复杂性差异很大。L4和Exokernel都提供简单的低级操作。L4提供了地址空间和线程管理所需的基本内核抽象,而Exokernel提供了安全的多任务资源所需的裸硬件相关原语。另一方面,SPIN导出各种接口,应用程序看到继承自Mach的第一代微内核接口,以及内核下载代码的内部接口。Exokernel中ASHes的存在更难以区分全局描述。因此,适当的内核提供的操作集可能是有效的低级操作和在需要时使用高级操作扩展内核的能力的混合,例如,支持关键时间的功能,如网络。
Exokernel的硬件依赖可能是一个严重的缺点。我们发现,构建非常低层次的抽象(如L4导出的抽象)是非常依赖于CPU的,对于Intel x86家族的后续成员,需要完全不同的方法。这似乎表明,在Exokernels之上构建低级库OSs可能是每一代处理器都需要重新做的事情。如此高的不可移植性将成为任何真正接受基于Exokernel的操作系统的严重障碍。
总之,我们应该强调我们在引言中已经指出的内容。第二代微内核最重要的责任是完全缺乏基于它们的真实操作系统。只有每天的实践经验才能证明操作系统是真正可行的。由于这些微内核是最新的,它们的最终判断还为时尚早。时间是他们唯一确定的判断。