过时的微内核注定 Mac OS X 性能落后于 Linux
关于微内核是否优秀存在争议。人们很容易产生微内核是优秀的印象,因为它们是在单内核之后作为一种改进方案被提出的。然而,微内核现在大多已被否定,因为它们存在性能问题,并且最初承诺的优点只是一种幻想。
微内核狂热者认为,几个协同工作的系统进程应该接管单内核的传统工作。这些系统进程彼此之间通过内存保护隔离,这就是所谓的优点。
单内核将内核的定义和实现限定为“系统中不会从内存保护中受益的部分”。
当我以这种方式陈述单内核设计的动机时,很明显我相信谁是对的。我认为微内核狂热者是过度概括的受害者:他们从 Windows 3.1 和 Mac OS 6 等旧系统来到 UNIX,这让他们误以为在任何地方进行内存保护都是一种抽象的、毋庸置疑的优点。这有点像常见的错误,即相信那些据称能提供更多安全性的仪式,仿佛安全性是一个一维概念。
内存保护是一种工具,它有三个常见的动机
在开发过程中帮助调试程序,且性能成本低于检测工具。(检测工具是 Java 或 Purify 使用的工具。)内存保护有望使程序崩溃的位置更接近错误发生的位置,而不是没有内存保护的情况,而检测工具则应该使程序直接在错误发生的位置崩溃。
最大限度地减少程序崩溃带来的不便。
即使程序崩溃,也要保持安全承诺。
“因为 MS-DOS 没有它,而且 MS-DOS 很烂”不是内存保护的动机。
动机 #1 在某种程度上是微内核系统中额外内存保护的合理论据。例如,QNX 开发人员可以使用相同的调试器调试设备驱动程序和常规程序,从而使 QNX 驱动程序更易于编写。QNX 程序员很厉害,因为他们可以如此轻松地编写驱动程序,以至于他们似乎与我们对驱动程序的理解不同;他们认为任何对硬件进行抽象的操作都是驱动程序。我认为用于设备驱动程序的良好调试工具使 QNX 成为一家在商业上可行的加拿大微内核公司。任何真正开始使用 QNX 的开发人员都会对其关于成品稳定性的声明感到怀疑;微内核的优点完全在于易于开发和调试。
动机 #2 很愚蠢。当 SCSI 驱动程序进程或文件系统进程崩溃时,现场的实际微内核不会自行恢复。诚然,如果有一个开发人员在场,可以用一些特殊的调试工具给它一个推动,它可能会恢复,但这种优势实际上更像是动机 #1 中所述的优势,而不是 #2。
由于微内核进程协同工作以实现安全承诺,因此当其中一个进程崩溃时,这些承诺不一定能得到兑现。因此,动机 #3 也很愚蠢。
这三个因素共同表明,内存保护在内核内部用途不大,除非可能对内核开发人员有用。这就是为什么我认为微内核承诺的优点只是一种幻想。
在我们继续之前,我应该指出,Mach 和 QNX 这两个微内核系统对于什么程度的“微”可以放入微内核有不同的看法。在 QNX 中,只有消息传递、上下文切换和一些进程调度钩子进入微内核。QNX 用于磁盘、控制台、网卡和所有硬件设备的驱动程序都是普通进程,它们与用户的程序一起显示在 sin 或 ps 中。它们服从 kill 命令,所以如果你想,你可以杀死它们并使系统崩溃。
苹果公司为 Mac OS X 采用了 Mach,它将任何访问硬件的东西都放入微内核中。根据 Mach 的理念,XFree86 仍然不应该是用户进程。在像 mkLinux 这样的微内核的单服务器滥用中,Linux 进程每当需要访问任何 Apple 硬件时,都会向 Mach 发出系统调用(而不是消息传递),因此文件系统的实现在 Linux 进程内部,但磁盘驱动程序在 Mach 微内核内部。这种安排对于苹果公司资助 mkLinux 来说是一个很好的商业理由:他们专有硬件的所有驱动程序,以及他们资助的大部分代码,都保留在 Mach 内部,在那里它们受到对他们更有利的许可协议的保护。
然而,将 Mach 设备驱动程序放入微内核中,大大扼杀了 QNX 的动机 #1,因为 Mach 设备驱动程序现在与单内核的设备驱动程序一样难以调试。我不确定 Darwin 的驱动程序是如何工作的,但重要的是要承认关于实际微内核系统组织的这种争议。
性能问题呢?简而言之,现代 CPU 针对单内核进行了优化。单内核将其自身映射到每个用户进程的虚拟内存空间中,但这些内核页面以某种方式标记,以便只有当 CPU 的主管位被设置时才能访问它们。当进程发出系统调用时,CPU 在调用进入和返回时隐式地设置和取消设置主管位,因此内核页面通过翻转单个位来适当地亮起和隔离。由于虚拟内存映射在系统调用中不会改变,因此处理器可以保留它缓存在 TLB 中的所有映射片段。
对于微内核,几乎所有曾经是系统调用的东西现在都属于“向另一个进程传递消息”的范畴。在这种情况下,翻转主管位已不足以实现内存保护,因为单个用户进程的系统调用涉及 1 个用户进程 + 1 个微内核 + n 个系统进程的单独内存映射,但单个位只有足够的两种映射状态。微内核必须为每个等效于系统调用的操作至少切换两次虚拟内存映射,而不是使用主管位技巧;一次是从用户进程切换到系统进程,另一次是从系统进程切换回用户进程。这比翻转主管位需要更多的开销;管理映射需要更多的开销,并且还有两次 TLB 刷新。
一个实际的例子可能涉及更多的开销,因为两个进程只是单个等效于系统调用的操作中涉及的最小值。例如,从 QNX 上的文件中读取数据涉及用户进程、文件系统进程和磁盘驱动程序进程。
TLB 刷新开销是多少?TLB 存储虚拟到物理映射的小片段,以便大多数内存访问最终查询 TLB 而不是存储在物理内存中的权威映射。由于 TLB 位于 CPU 内部,因此 CPU 设计人员安排 TLB 查询应该是免费的。
TLB 中的所有信息都是存储在物理内存中的真实虚拟到物理映射的派生物。TLB 可以表示一个虚拟到物理映射,但内存保护的全部意义在于为每个进程提供不同的虚拟到物理映射,从而为每个进程保留某些物理内存块。存储在物理内存中的虚拟到物理映射可以表示映射的这种多样性,但高速硬件 TLB 中表示的映射片段只能表示一个映射。这就是为什么切换进程涉及 TLB 刷新的原因。
一旦 TLB 被刷新,它就会在新进程执行时从物理内存中的权威映射中逐渐重新加载。TLB 的逐渐重新加载,分摊到每个新唤醒的进程的执行中,就是开销。因此,尽可能少地在进程之间切换,并最大限度地利用主管位技巧是有意义的。
微内核还通过使当前趋向于零拷贝设计的趋势复杂化来损害性能。零拷贝美学表明,系统应尽可能少地复制内存块。假设应用程序想要将文件读入内存。一个在美学上完美的零拷贝系统可能会让应用程序 mmap(..) 文件,而不是使用 read(..)。磁盘控制器的 DMA 引擎会将文件内容直接写入映射到应用程序虚拟地址空间的同一物理内存中。显然,安排好这一点需要一些技巧,但内存保护是主要障碍之一。内核中显眼地散布着关于某些东西必须复制到用户空间的注释。微内核使消除块复制变得更加困难,因为有更多的内存保护屏障需要跨越复制,并且数据必须复制到微内核系统传递的格式化消息中和从中复制出来。
单内核中现有的零拷贝项目获得了回报。NetBSD 的 UVM 是 Chuck Cranor 根据 零拷贝美学 重写的虚拟内存。UVM 发明了 NetBSD 早期 VM 缺乏的页面借出和页面传输功能。这些功能体现了零拷贝美学,因为它们有时消除了内核复制到用户空间的需要,但仅当要复制的块足够大以跨越整个 VM 页面时才有效。他的速度提升无疑部分来自更简洁的代码,但他博士论文中最引人注目的部分讨论了通过减少批量复制来节省处理器周期。
VxWorks 是最早吹嘘零拷贝设计的内核之一,其 TCP 协议栈就是如此。他们可能是出于减少内存占用的动机,但他们的零拷贝协议栈也应该比传统的 TCP 协议栈更快。应用程序必须使用 zbuf API 才能体验到好处,而不是通常的 Berkeley 套接字 API。相比之下,VxWorks 没有内存保护,甚至在内核和用户应用程序之间也没有。
BeOS 在用户进程中实现了其 TCP 协议栈,采用微内核风格,QNX 也是如此。两者都以 TCP 协议栈速度慢而臭名昭著。
零拷贝是一种美学,而不是一个复选框新闻稿功能,因此它不像系统可以拥有或缺乏的东西那样简单。我怀疑 VxWorks 和 QNX 的 TCP 协议栈之间的区别在于零拷贝与过度复制。
微内核的诞生和消亡不是一蹴而就的,重要的是要理解,即使在微内核最初被提出时,这些性能障碍也可能是显而易见的。否定微内核需要实际实现它们,优化消息传递原语等等。
也不要过分嘲笑 QNX,这一点很重要。考虑到他们的整个环境都是严格的闭源,人们竟然可以编写 QNX 驱动程序,更不用说以非同寻常的轻松程度来编写,这有点令人惊讶。
但是,我认为我们已经到了记录说明一切的地步,微内核项目已经失败了。然而,这仍然不能完全证明 Linux 的正确性,仅仅因为它有一个单内核。当然,Linux 不再需要羡慕 Darwin 的微内核,但微内核实验更普遍地说明了内存保护和某些类型的 IPC 的成本。
如果用户进程和系统进程之间过度的内存保护切换是浪费的,那么两个用户进程之间过度的切换是否也是浪费的?事实上,这个问题解释了为什么专有 UNIX 系统使用两级线程架构,该架构在每个内核线程内部调度许多用户线程。Linux 顽固地保留了像 Windows NT 那样的单级内核调度的线程。通过采用专有 UNIX 的 调度器激活 或 Masuda 和 Inohara 的 不稳定线程,Linux 可以获得更好的性能。这种性能问题与 IBM JDK 的本地线程和 Blackdown JDK 的可选绿色线程之间的争议交织在一起。
鉴于微内核实验的结果,我对苹果公司在新设计中采用微内核的古怪选择感到惊讶。至少,它为 Linux 在 macppc 平台上建立和保持性能领先地位创造了机会。但是,我认为失败的微内核实验最有趣的意义在于它对数据如何在整个系统中流动的观察,而不仅仅是回答关于内核应该有多大的显而易见的问题。
Miles Nordin 是一位饱经风霜的 FidoNet 老兵,也是 Boulder 2600 (720) 的活动家,目前流亡在臭名昭著的 Waynesboro 乡村俱乐部附近的广阔的宾夕法尼亚州东部地区。
电子邮件:carton@Ivy.NET