从鸿蒙操作系统的发布,经Minix认识微内核

浙江温州皮鞋湿,下雨进水不会胖!


来自《有关微内核OS史上最透彻一篇 – 写于华为鸿蒙发布一周之际》https://mp.weixin.qq.com/s/MLCR7qqGFWyyP0KcZqW3Kw

华为鸿蒙OS发布已经一周了,在这一周中发生了很多事情,有人对华为路转粉,也有人对华为粉转黑,在时下,只要是华为的任何动作,背后都早已预备好某种正确,当然,所有事先备好的正确,必然不是客观的,所以为了不浪费时间和精力,避开那些争端即可。

所谓的“正确”便是,只要是华为的,就是好的,谁哪怕说一句不好,那就是谁不好。
然而,事情在悄悄地起变化,当舆论清一色一边倒向利好华为时,反对的声音便形成了新的“正确”,这个时候,只要谁说一句好,那就是谁不好。
有意思,无意义。

这一周,借助鸿蒙操作系统把一个计算机领域内非常专业的词带到了几乎所有人的视线内,这就是 微内核 ,一瞬间, 上铺天盖地的关于鸿蒙操作系统微内核,全场景,分布式的文章,携带着纷至沓来数以千百的评论,把所有人淹没,搞得好像是所有人早就知道微内核这个概念了似的,全民科普非常之优秀。



如果说华为鸿蒙发布会真的在吹水(是不是吹水我不多评论,我只是说如果),那么还必须承认两点:

  1. 围绕吹水的鸿蒙PPT而写的评论性软文也是在吹水,并且连个PPT都没有。

不懂就不要跟风。

几乎所有讲微内核的文章都采用以上这个图,但是除此之外,基本就是空洞的总结了,无外乎微内核更加容易扩展,更稳定,而宏内核则性能优越之类云云。但这些并不能让读者体会微内核的实质,因为这些总结性的理论通常是给已经懂的人看的,它无法给初学者带来感官上的感受。

下面这几个资源还不错,可以一读:
微内核IPC/RPC等 :https://pdfs.semanticscholar.org/1cd7/edefcdd4cec5babb6b5b2d9e6572aa27a046.pdf
微内核 络相关 :https://ieeexplore.ieee.org/stamp/stamp.jsprnumber=275670
微内核IO相关 :https://pdfs.semanticscholar.org/8559/e6c101b14511002dd5178097f7d2d2acb247.pdf
QNX体系结构 :https://cseweb.ucsd.edu/~voelker/cse221/papers/qnx-paper92.pdf

除了这些空洞的操作系统理论概念,我们还有一个活生生的Minix。我之所以以Minix来介绍微内核而不是QNX,L4这些,基于以下几点:

  1. Minix源代码是唾手可得的。
  2. Minix的原理有大部头的教材跟讲。
  3. Minix系统是精简直接的,没有任何针对性的特性,它通篇就是在讲什么是微内核。
  4. QNX,L4等是难以获得的,且复杂,不适合从根本上理解什么是微内核,容易陷入细节。

Minix介绍
Minix是Andrew S. Tanenbaum所著《操作系统:设计与实现》教材的示例代码。喜欢操作系统的都应该去读一下这本书。这本书是非常少见的以一个完整的操作系统实现来讲操作系统原理的教材,风格非常不一般。

Linus的Linux内核本身就参考了Minix,如果你去看Linux 0.1/1.0等早期的代码,就会发现它和Minix是多么的相似,很多函数名字都是一样的。

更多详情,参见:
Minix的Wiki页面:https://zh.wikipedia.org/wiki/MINIX
一个介绍Minix3的ppt:http://www.minix3.org/docs/jorrit-herder/disi08-talk.pdf

按照理解一个操作系统的常规步骤,首先,你要先让一个系统跑起来再说其它,至少你要知道它长什么样子吧。

Minix目前有三个主要的版本:

  • Minix1 https://github.com/gdevic/minix1
    也就是《操作系统:设计与实现》教材的演示代码,侧重于教学和学习。年代久远,很难编译安装。
  • Minix 2.0.4 http://download.minix3.org/previous-versions/Intel-2.0.4/
    侧重于自学的一个版本,安装稍微麻烦,需要自己做软盘。不过这里有个超详细的安装教程,可以参考:https://mmmyddd.github.io/wiki/debian/minixonbochs.html
    读它的源码感觉也是不错的。
  • Minix 3.2.1 http://download.minix3.org/iso/minix_R3.2.1-972156d.iso.bz2
    这是个实用版本,也就是说,它是实际可用的,有iso映像可供下载,安装非常方便。

我基本上三个版本都试了,对于Minix1,我只是通读了源码,而对于后面两个版本,都均进行了安装测试。

下面是Minix2.0.4版本的 ps ax 命令的结果界面:

好了,我们已经看过了一个真实的微内核操作系统到底长什么样子了,试着折腾个把小时,把你觉得好奇的地方都摸摸清楚,基础的第一步已经完成,接下来就要去看看冰山下面了。

要想快速理解什么是微内核,了解一个常用的场景在微内核中是如何表现的,便是最有效率的方法了,加以和宏内核同样的场景进行对比,基本就了解微内核行为的大概了。是为情景分析。

我就以普通的读取文件的read调用,以Minix为例,展示一下其行为:

同样,在FF服务进程中,系统进程的快照保存在fproc数组中,见fs/fproc.h:

其实,可以这样理解,在微内核中,FS,MM这些服务进程的逻辑以及快照数据在宏内核中就是对应内核本身的,只不过它们的访问方式不同:

  • 宏内核通过函数调用访问特定的逻辑和数据。
  • 微内核通过IPC(进程间通信)访问特定的逻辑和数据。

作为对比,我们看一下宏内核Linux是如何完成与上面的情景IPC等价的操作步骤的:

极简主义的典范,不是吗让我们可以联想到RISC处理器,看它们的汇编指令时,访存寻址只有load/store两个指令,也是极简主义的典范。

极简归极简,但是请看看上面那繁琐的流程,在宏内核中一个read系统调用的事,在Minix微内核中竟然要整整12个系统调用才能完成。 回到上面的话题, 性能何在率何在义何在/strong>

这是微内核饱受诟病的核心。

确实,纯传统IPC的方案,如此多的系统调用,开销是非常可观的。然而,通过以太 的发展史,我们或许可以看到曙光。

我们看看早期的共享总线式以太 :

宏内核缺乏访问共享资源的有序仲裁机制,因此同步开销会非常大,最直接的后果就是宏内核随着处理器核心的增加而不可扩展。这个现状和早期以太 是多么相似, “以太 随着共享总线上接入的计算机数量的增加而性能陡降”

所有使用内核服务的进程都在各自 隔离的上下文(现代操作系统之所以现代的原因) 中访问底层的共享资源,这是无法仲裁的根源。

说回以太 ,当事情发展到交换式以太 的时候,问题解决了,因为冲突域坍缩到了交换机的背板总线,作为具备二层逻辑处理能力的交换机,它便可以进行必要的资源仲裁,比如排队以及队列的调度,实现数据包的存储转发:

我们回看并思考一下以太 进化到交换式以太 时的情形。

面对交换式以太 ,有人肯定会质疑, 原来数据包可以直接通过一根线飞到目的地,现在还要去交换机里走一遭,还有经由交换机逻辑的处理,多了这么多的步骤,如何能和曾经共享总线时一根线相比呢/strong>

质疑者明显是忽略了CSMA/CD的开销。相比CSMA/CD的开销,交换机的处理开销与之做减法,最终化作了收益。
【交换式以太 从此飞起,如今万兆以太 都已经是标配了】

类比以太 的发展历史,如果我们考虑多核处理器上宏内核的无序同步开销,在微内核中加入带有仲裁功能的服务进程后竟然大大降低甚至消失了,是不是有一丝的欣慰呢/p>

有了交换机之后,人们忘记了CSMA/CD(虽然还是要考试),那么若干年后,是不是也能忘掉spinlock呢/p>


其实,在我们使用宏内核时也不是不明白专门进程处理专门事情的重要性,只是这种感觉来得比较自发罢了,并没有形成书面上的方法论。典型的例子就是最近几年非常风靡的Linux用户态协议栈。

为什么要构建用户态协议栈,那明显是因为内核协议栈性能低。用户态协议栈可以做到专核专用,进程上下文可以完全掌握数据包收包逻辑的全过程,更便于仲裁和协作。

如果用户态协议栈抽象成一个服务,这是不是就和微内核思想一致呢/p>

此外,除了协议栈,很多别的逻辑也纷纷往用户态搬迁,并且,在内核态本身,中断也越来越线程化了,以便统一参与内核的调度。微内核的思想一直在背后起着作用。


本来微内核的性能是软肋,结果被我说的好像成了它的优势,有点不太雅。但其实,我表达的更多的是微内核表现出来的一种潜力。 站在多核心可扩展性角度,多核平台,无疑微内核的设计会更好,服务进程的仲裁可以让应用在多核平台无锁运行。

不过无论如何,我承认性能确实是微内核的软肋,特别是Minix的性能确实不咋地,但这也同样意味着微内核可以针对性能这个软肋集中地进行优化。业界公认的性能比较好的微内核当属QNX了,它被广泛部署在黑莓手机和汽车上,时间和存在可以证明,它守住了它承诺的。

在UNIX历史的长河中,一开始的系统就是奔着微内核的思想去的,比如最初的UNIX系统中会拥有一个专门负责调度和交换的swapper进程,直到现在,我们依然可以在UNIX/Linux中找到一个0 的swapper/idle进程,虽然它早就被淹没在宏内核之中,再也不行调度以及交换之事务了。

说到微内核的性能问题,我认为在经过了30多年的发展后,微内核,宏内核之间的性能差距已经大大缩小了,这个关于两种由于设计理念的不同导致的性能差异话题在计算机的发展史上,从来就没有停止过:

  • 微内核和宏内核。
  • 跨平台的Java语言和C语言。
  • 跨平台的语言Java和跨语言的平台.NET。
  • 解释型脚本Python和C语言。
  • CISC体系结构和RISC体系结构。

但是无一例外,最终谁也吊打不了谁,收益必有代价,大多数的纷争最终走向了融合。

硬件技术的发展往往落后于软件技术的发展,但是硬件技术却是始终不断发展的,在此期间,软件技术往往陷入了两种理念的纷争而稍有停滞,最终硬件技术跟上,弥补软件性能的缺陷。甚至硬件可以针对软件的需求作出特殊的支持。

软件提出需求,硬件实现。如果微内核在理论上证明确实好,仅仅IPC是个瓶颈的话,直接从硬件上着手优化,岂不是更好吗想QNX,鸿蒙的设计应该也是这么考虑的吧。

软件硬件统一支持,我想微内核和宏内核之间最终也会是某种融合,我说的并非Windows作为混合内核的那种马赛克式的融合,而是熔炉式的融合。

具体到微内核性能差的根因,我们来聊一下IPC的优化。

如果实现一个最基本最简单的IPC,那么内存拷贝就够了。这一般是0.1到1.0的做法。先让系统跑起来是最重要的, 完成比完美更重要 。【GNU Hurd内核的失败就是因为斯托曼太过理想主义,追求完美…反例则是李纳斯,追求完成。】

随着硬件技术的发展,随着系统对性能要求不断提高,IPC必然要不断优化,内存拷贝不再适用,共享内存则更好,最终,可能直接使用硬件的某种机制,比如寄存器,DMA等机制来帮助实现IPC。如果有硬件机制直接提供IPC的支持,问题就解决了。

这个过程非常类似sendfile/splice系统调用的设计过程。

sendfile/splice的设计

起初,Web服务器需要先将文件拷贝到Web服务器内部buffer,然后再将buffer拷贝到用户的socket。这很类似传统的IPC方案。

然而随着HTTP逐渐主宰互联 ,几乎每一个Linux服务器上均部署有Web服务器,谁还能忍受两次拷贝的瓶颈,于是sendfile就呼之欲出了。sendfile仅仅提供一个外部把手,真正的数据并不需要拷贝到Web服务器的buffer,通过这个把手,数据可以从文件直通到socket。

用的人多了,需求在了,问题自然也就解决了,而且是从底层根本上解决。微内核的IPC也会是这样的路子。

不过,目前还没有一个通用的使用在微内核上的IPC机制,相信QNX是有优化过的IPC的,但是不够通用,而Android系统的Binder够通用也还不错,但是它并不针对微内核。倒是非常希望能看看鸿蒙是怎么去优化的。


好了,现在我在想,我们是不是可以去看看Minix的源码了。好吧,Let’s go!

还是情景分析的方式,我们先看read。在Linux的glibc上,read的实现都是 SYSENTER,SYSCALL或者int 0x80 来直接陷入系统调用的。而在Minix中, 以下以Minix1为例 ,我们看看其库函数的实现:

我们展开callm1:

// lib/call.cPUBLIC int callm1(proc, syscallnr, int1, int2, int3, ptr1, ptr2, ptr3)int proc;			/* FS or MM */int syscallnr;			/* which system call */int int1;			/* first integer parameter */int int2;			/* second integer parameter */int int3;			/* third integer parameter */char *ptr1;			/* pointer parame

                                                        

声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2019年7月18日
下一篇 2019年7月19日

相关推荐