外内核(Exokernel)。论文发表时间是 1995 年。
1 问题背景
传统的操作系统直接定义了物理资源和应用程序之间的接口,这种做法限制了应用程序的性能和实现自由度。同时,操作系统高度的抽象,隐藏了背后机器资源的信息。这些硬编码的抽象实现是不可取的,它否定了应用程序的局部优化可能带来的好处、挫败了对现有实现改进的想法以及限制了软件开发者的灵活性。不能对这些高级抽象进行修改,会造成性能的降低、复杂度的增加以及限制应用程序的功能性。原因分别在于,没有一种对物理资源的抽象方式对所有软件适用;对应用程序来说,硬件细节被隐藏了;只有硬件和软件之间的接口可以被使用。
为了解决上述问题,需要提供应用级的机器资源控制,外内核就定义了一个低级接口,遵循了一个简单旧观点:原语级别越低,越能被高效地实现。为了给应用程序级资源管理提供最大的可能,外内核架构由一个精简外内核单板组成,该单板通过一组低级原语安全地多路复用和输出物理资源。库操作系统使用这些低级的外内核接口,定制化实现高级抽象以达到应用程序的性能和功能需求。此外,库函数也可以实现良好的可移植性和兼容性。可移植性,例如 POSIX,任何基于 POSXI 实现的应用程序都可在 POSIX 上运行。而对于兼容性,与微内核系统一样,外内核可以通过三种方式提供向后兼容性:一是操作系统及其程序的二进制模拟; 第二,通过在外内核之上实现其硬件抽象层; 第三,在外内核之上重新实现操作系统的抽象。
2 外内核实现
外内核的整体的设计思路要素如下图所示。
2.1 三个原则
外内核不应该来管理资源,而是应该分配资源、回收资源和管理资源的所有权。
- 暴露分配:外内核应允许库操作系统请求特定的物理资源。
- 暴露名称:外内核应该暴露物理名称。
- 暴露回收:外内核应该利用可见的资源回收协议,以便行为良好的库操作系统可以执行有效的应用程序级资源管理。
外内核如何决定资源的分配和回收,可以使用传统的分区策略(配额和保留)。
2.2 三种技术
2.2.1 安全绑定
安全绑定可以从以下两个方面来提升性能。一是以简单的方式来表达安全绑定;二是只在绑定的时候进行绑定身份验证。为了支持安全绑定,需要实现一套原语使得应用层面的软件可以表达保护检查。
有三种技术可以实现安全绑定:
- 硬件机制。例如,文件服务器可以在内存页面中缓冲数据,并通过为授权应用程序提供物理页面的功能来授予对它们的访问权限。外内核将强制执行功能检查,而不需要有关文件系统授权机制的任何信息。
- 软件缓存。外内核可以使用大量的软件 TLB 来缓存那些不适合硬件 TLB 的地址翻译。软件 TLB 可以被视为常用安全绑定的缓存。
- 下放应用代码。在每次资源访问或事件时都会调用此代码,以确定所有权以及内核应执行的操作。将代码下放到内核中允许应用程序控制线程在发生内核事件时立即执行。从中可以发现提升性能的两个方面。一是消除了内核之间的交叉;二是下放代码的执行时间是有边界的。例如 ASH(Application-specific Safe Handler),它主要功能之一是它可以发起消息。基于此,可以大大减少往返延迟,因为回复可以当场传输,而不是推迟到应用程序调度。此外,下放代码有一个突出的特征是可以使用高级语言。高级语言具有更多的语义信息,可以为优化提供更多的信息。
2.2.2 可见的资源回收
资源回收分为不可见和可见两种。传统的操作系统都使用的不可见的方式。不可见的资源回收具有比可见回收更低的延迟,但是不可见回收使得操作系统不能参与资源回收并且也不知情。
对与外内核,则在不可见资源回收和可见资源回收之间取了平衡。对于大部分资源,采用可见的资源回收方式;而对于频繁进行资源回收的资源,则使用不可见的资源回收方式来提高性能。
2.2.3 中止协议
从未及时回应的库操作系统强制回收资源。最简单的做法就是杀死库操作系统以及与之关联的应用程序。但是作者拒绝这种方法,因为作者相信大多数程序员很难推理硬实时界限。正确的方式是使库操作系统遵守回收协议。如果库操作系统没有遵守,外内核则简单的阻断已有的安全绑定,并且通知库操作系统。为了记录这些被强制回收的资源,作者使用了回收向量。如果发生了强制回收,则通过这个向量来向库操作系统发送回收异常。
此外,外内核也不可以随意强制回收资源。库操作系统可能使用一些物理内存来存储重要的引导信息,例如异常处理程序和页表。处理这个问题最简单的方法就是保证每个图书馆操作系统有少量的资源不会被收回。如果非要回收,则需要告知库操作系统,将其自己提交到交换服务器上。
2.3 时间片和处理器环境
时间片。时间片基于时钟粒度进行划分,这样就可以类似于物理内存的方式进行分配。时间片通过轮询的方式进行调度。不同应用类型,采取不同的时间片分配方式。对于持续运行的科学应用程序可以分配连续的时间片,以最大限度地减少上下文切换的开销,而交互式应用程序可以分配多个等距的时间片,以最大限度地提高响应能力。
定时器中断表示时间片的开始和结束,在上下文切换中,应用程序只负责通用的上下文切换:保存和恢复活动寄存器并释放锁等。这样实现的目的是给应用程序极大的上下文切换控制。
公平性是通过限制应用程序的上下文切换时间来实现的,每一个时钟中断都会被记录在一个超时寄存器中。应用程序通过预支后面的时间片来完成超时操作,如果超时的时间超过了设置的阈值,则销毁环境。
处理器环境。Aegis 的处理器环境是一个数据结构体,存储着应用事务传递所需要的必要信息。在 Aegis 中,事务传递有四种类型,分别是:异常、中断、保护控制传输和地址翻译。四种事务的上下文内容如下。
- 异常上下文:1)程序计数器;2)保存寄存器的物理地址的指针。
- 中断上下文:1)程序计数器;2)寄存器的保存区间;3)时间切片开始和结束的程序计数器;4)协处理器控制以及中断使能信号的状态寄存器。
- 保护人口上下文:1)同步保护控制传输和异步保护控制传输的程序计数器。
- 地址上下文:1)保护映射集合;2)地址空间标识符;3)状态寄存器;4)用于散列到 Aegis 软件 TLB 中的标签。
地址上下文依赖于异常上下文来决定需要执行的操作。
3 实验
基于前两节所描述的内容,作者实现了 Aegis 和 ExOS。前者是外内核,后者是库操作系统。在 Aegis 外内核中,导出处理器、物理内存、TLB、异常以及中断。在 ExOS 库操作系统中,实现了处理器、虚拟内存、用户级异常以及各种各样的中断抽象;此外,还实现了几个网络协议(ARP/RARP, IP, UDP, NFS)。这里的实验有以下四个猜测:
- 外内核非常的高效。
- 低层次的安全的硬件资源的多路复用可以被高效地实现。
- 传统的操作系统抽象也可以在应用级上高效地被实现。
- 应用可以在已有的抽象上定制化实现其他功能。
所有的数据对比也是和 Ultrix 操作系统进行对比的,Ultrix 就是现有的大型成熟的操作系统,基于传统的观点实现的。
4 Aegis
Aegis 是外内核的实现。Aegis 的一部分系统调用接口以及操作原语如下两张图所示。
4.1 基本开销
Aegis 有两条基本的系统调用路径。一条是不需要栈的系统调用,一条是需要栈的系统调用。除了受保护的控制传输之外,所有 Aegis 系统调用都沿着这两条路径之一进行引导。
4.2 异常
Aegis 的异常分发的流程如下:
异常处理之后,程序可以立即恢复执行,而不需要内核参与,这一点得益于异常状态对应用程序来说是可获得的。这就要求所有的寄存器必须保存在用户进程可以看到的地方。
4.3 地址翻译
地址翻译需要解决支持应用级虚拟内存的两个问题:引导和效率。Aegis 通过使用少量有保护的映射提供了简单的引导机制。保护映射的缺失将由 Aegis 自动处理。为了高效地实现保护映射,应用的虚拟地址空间需要分成两段,一段持有常规的应用数据和代码。段中的虚拟地址可以使用有保护的映射固定,并且通常保存异常处理代码和页表。
一旦发生了 TLB 缺失,会执行以下的操作:
为了支持应用级虚拟内存高效地执行,TLB 重新填充的速度必须很快。为了达到这一点,Aegis 实现了一个大型的软件缓存。当发生 TLB 缺失的时候,先检查 STLB 是否有缓存,如果有的话,直接返回。正如之前所说的外内核需要暴露一切,所以 STLB 也会暴露给应用程序,使应用程序更好地利用该资源以提升性能。
4.4 保护控制传输
Aegis 提供受保护的控制传输机制作为有效实现进程间通信的基础。保护控制传输将被调用者的程序计数器设置为达成共识的值,并将当前的时间片让给被调用者,然后提供被调用者处理器上下文信息。
Aegis 提供两中类型的保护控制传输,一种同步的,一种异步的。两者的区别在于,异步调用只将当前的时间片的剩余部分转让给被调用者;而同步调用则转让当前的时间片以及后面的所有实例化的时间片。如果被调用者想返回时间片,只可以通过同步调用的方式。不管是哪一种保护控制调用,都必须是原子操作,而且不可以修改应用可见的寄存器。
4.5 动态包过滤
Aegis 的网络子系统大量使用了动态代码生成技术以提供高效地信息解多路复用以及提交。Aegis 主要利用的动态代码生成技术的两种方式:1)通过在将数据包过滤器安装到内核中时将其编译为可执行代码,使用它来消除解释开销。2)通过使用过滤器常量来积极优化此可执行代码。
动态代码生成技术
一项广泛用于及时(JIT)编译以及动态二进制翻译(DBT)的技术,在运行时生成和修改代码,以致提高性能和安全性。
参考文献
Exokernel: An Operating System Architecture for Application-Level Resource Management