18.7 L4 Linux性能分析

你应该问自己:通过论文可以学到有关微内核的什么内容呢?

对于我们来说,论文中有很多有趣的有关微内核是如何运行,有关Linux是如何运行的小的知识点,以及你该如何设计这么一个系统。但是论文并没有回答这个问题:微内核是不是一个好的设计?论文只是讨论了微内核是否有足够的性能以值得使用。

论文之所以讨论这个内容的原因是,在论文发表的前5-10年,有一场著名的测试针对一种更早的叫做MACH的微内核。它也运行了与L4类似的结构,但是内部的设计完全不一样。通过测试发现,当按照前一节的架构运行时,MACH明显慢于普通的Unix。这里有很多原因,比如IPC系统并没有如你期望的一样被优化,这样会有更多的用户空间和内核空间的转换,cache-miss等等。有很多原因使得MACH很慢。但是很多人并不关心原因,只是看到了这个测试结果,发现MACH慢于原生的操作系统,并坚信微内核是无可救药的低效,几乎不可能足够快且足够有竞争力。很多人相信应该都使用monolithic kernel。

今天的论文像是对于这种观点的一个反驳,论文中的观点是,你可以构建类似上一节的架构,如果你花费足够的精力去优化性能,你可以获取与原生操作系统相比差不多的性能。因此,你不能只是因为性能就忽视微内核。今天的论文要说明的是,你可以因为其他原因不喜欢微内核,但是你不能使用性能作为拒绝微内核的原因。

达成这一点的一个重要部分是,IPC被优化的快得多了,相应的技术在18.5中提到过。

论文的表二做了性能对比,运行在硬件上的原生Linux执行一个简单的系统调用getpid花费1.7us,对于上一节的实现,需要发送一个IPC request,并获取一个IPC response,以实现getpid系统调用,这需要花费接近4us,这是原生Linux的两倍多。主要是因为这里有两倍的工作,这里涉及到两次用户空间到内核空间的切换,而不是一个简单的系统调用。这也说明L4已经将这种基于IPC的系统调用的代价降到了最低,也就是2倍于一个原生Linux的系统调用。因此,它可以做的大概与你期望的一样好。

当然这里的系统调用仍然只有原生Linux一半的速度。现在还不清楚这是否是一个灾难,还是说并没有问题。如果你执行大量的系统调用或许就是个问题;如果你执行了相对较少的系统调用,或者系统调用本身就有很多工作,或者你的系统调用比getpid要复杂的多,这又或许不是个问题。论文中通过使用AIM做的测试结果,给出了答案。测试结果在论文的图8。

AIM会执行各种系统调用,例如read/write文件,创建进程等等。从图8可以看出,在AIM设置的一个更完整的应用中,基于L4的Linux之比原生Linux慢几个百分点。因此,理想情况下你可以期望你想要运行在计算机上行的应用,如果在L4+Linux上运行可以与运行在原生操作系统上一样快。因为可以以近似原生Linux的速度运行,所以你们现在应该认真对待微内核。图8是一个非常不错的结果,有点超出预期。

让时间快进20年,如果之前所说,现在人们实际上在一些嵌入式系统中使用L4,尤其在智能手机里有很多L4实例在运行,它们与Unix并没有兼容性。在一些更通用的场景下,像是工作站和服务器, 微内核从来没有真正的流行过,并不是因为这里的设计有什么问题,只是为了能够吸引一些软件,微内核需要做的更好,这样人们才会有动力切换到微内核。对于人们来说很难决定微内核是否足够好,这样才值得让他们经历从现在正在运行的Linux或者其他系统迁移到微内核的所需要的各种麻烦事。所以,微内核从来没有真正流行过,因为它们并没有明显的更好。

另一方面来看,微内核的很多思想都有持久的影响。

  • 人们实现了更加灵活和有趣的方法来在微内核上使用虚拟内存。这些复杂的多的接口导致了mmap这样的系统调用合并到了例如Linux的主流操作系统中。

  • 论文中将一个操作系统作为一个用户程序运行另一个操作系统之上,今天以另一种方式非常流行的存在:在Virtual Machine Monitor上运行虚拟机。这种方式在各种场景,例如云主机上,都有使用。

  • 为了让内核能够具有一个用户空间服务一样的可扩展性,在Linux中演进成了可加载的内核模块,这使得你可以在线修改Linux的工作方式。

  • 当然,这里基于IPC的Client-Server支持,也在macOS有所体现,macOS中也有好用的IPC。

以上就是这节课的所有内容,有什么问题吗?

学生提问:论文4.3 Dual-Space Mistake能介绍一下吗?

Robert教授:这里稍微有点复杂。这里的一部分背景是,论文发表时的Linux,甚至直到最近,当你运行在x86上,且运行在用户空间时,使用的Page Table同时会有用户空间的内存Page,以及所有的内核内存Page。所以当你执行系统调用,并跳转到内核中,内核已经映射到了Page Table中,因此不需要切换Page Table。所以当你执行一个系统调用时,代价要小得多,因为这里没有切换Page Table。如果你回想我们之前介绍的内容,trampoline代码会切换Page Table(注,也就是更新SATP寄存器,详见6.5)。这是个代价很高的操 作,因为这会涉及到清除TLB。所以出于性能的考虑,Linux将内核和用户进程映射到同一个Page Table,进而导致更快的系统调用。

论文中期望的是,当用户空间进程向Linux发送一个系统调用,并且Linux的内核线程在处理系统调用,Page Table也包含发送系统调用的进程的所有虚拟内存映射,这会使得作为系统调用参数传入的虚拟内存地址查找更加的简单。但是为什么这里不能很好工作?

首先,L4并不知道这里的任何具体实现,在L4的眼里这就是两个进程。当你从一个进程发送IPC到另一个进程,L4只是会切换Page Table。由于现在Linux的系统调用是基于L4实现的,没有办法在系统调用的过程中保持Page Table,因为L4在两个进程间切换时总是会切换Page Table。所以这里不能得到系统调用时不切换Page Table带来的性能优势。

我认为这里希望得到可以在内核中直接使用用户空间的虚拟内存地址的便利,但是这意味着在Linux内核中需要知道是在执行哪个进程的系统调用,并使用那个进程的Page Table。当然L4并不知道这里的细节,它只是给每个进程关联了一个Page Table。所以L4只会给Linux关联一个Page Table,Linux并没有办法在处理不同进程的系统调用时使用不同的Page Table。

为了解决这个问题,论文中为每个进程都做了共享内存拷贝,每一个共享内存拷贝都有内核的所有内存,所以都有相同的数据结构。因为每个进程都有一个kernel task与之关联,因此可以使得L4切换到合适的Page Table同时包含了进程和内核的内存。我认为这里可以工作,但是忘记了这里是否会很慢之类的,因为这里有大量的任务。这是个复杂的故事,我不知道解释清楚了没有。

学生提问:看起来一些任务更适合在内核中,但是内核的方案中,要么所有东西都在内核要么都不在。所以要么你有一个monolithic kernel可以完成所有的事情,要么有个micro kernel什么也不做。我认为虚拟内存、文件系统和一些其他的事情在内核中做会非常的有效。不能有些系统具备有一些功能,然后你又可以选择用不用这些功能吗?

Robert教授:所有你说的都有非常有道理。实际上有很多微内核相关的项目都构建了各种hybrid内核,MACH有一些不同的版本,其中一些就是hybrid内核,这些内核的核心是包括了IPC的微内核,同时在内核中又是一个完整的Unix系统,比如MACH 2.5就是这样一个hybrid内核,其中一些东西是按照微内核的方式构建,一些东西又是按照Unix方式构建。现代的macOS也以与你描述类似的方式构建,macOS本身是个完整的操作系统,包含了文件系统,同时也很好的支持了IPC和其他用来构建微内核的东西。Google Fuchsia也实现了一些这里的想法。

Last updated