Fushsia:一次对操作系统的重构

Fushsia是对Windows操作系统的一次重构。

Object的组织方式已经深深的刻进了Windows的骨髓里,同为微内核设计的Fushsia毫不避讳的继承了这个设计。

操作系统的发展过程就是Windows和Linux不断的从不同的角度发现自己的局限性,并且从各自的角度出发来试图解决这些局限性的过程。在这个发展的过程,无数的弥补措施和新机制被发明,但是Windows和Linux都要兼容老的东西。Windows演进的过程积累下来的最重要的经验就是微内核应该怎么设计,Linux演进的过程积累下来的主要是稳定性和隔离行等容器化的要求,这一方面Windows也在不断的跟进。Android在内核之外设计了一个大型的操作系统组件,Android与Linux的配合是如此的丑陋,因为Android相当于想在应用层做掉一个内核不该做的事情。

每一个操作系统都在痛苦的迭代,各自面临着自己的痛点。Linux的世界对于权限和隔离性非常重视,但是apparmor,selinux,cgroup等后来逐渐添加的机制又与最初的设计那么的不相符。安全方面既然发现ACL的优势,为何还要用户的权限概念?隔离性记性不同的资源可以分组隔离,为什么还要统一的fork继承?Windows以图形见长,窗口和游戏性是Windows的公认优势,那他的优势又是如何塑造的?又如何打造一个类似的呢?简单的说是因为驱动闭源,但是深层原因还是比较复杂。

Android像是一个试验性系统,他希望达到Windows在图形方面(宽泛的说是硬件支持)上的优势,同时又羡慕Linux的富功能集。他从一开始就知道自己不会长期依赖Linux,否则java这种架构就不会被采纳。如果我要评价Android,就是Android更多的是一个架构层面的DEMO,验证的是操作系统的架构设计思想。至于实现,差不多能支撑这个架构设计就好了。验证这个过程,他的内核只能采用Linux,没有更好的选择。但是谷歌一直认为Linux做了太多不该他做的事情了(很多人都这么认为),其实根本原因还是认为过度的开源协议妨碍其他人赚钱了。

Fushsia一出手就是一个Android的下层替换。但是这个替换并不是代码上的,而是架构层面的。谷歌在战略方向比较少走废棋,他希望能在各个领域进行推进的同时,大家都尽可能的朝向同一个大目标前进。这个目标,就是一个开源形式的垄断Windows。在整个开源世界里,谷歌希望上下游通吃。这种做法必须要各个细分领域都要有谷歌的拳头开源产品,互相依促,互相借鉴,互相帮扶,共同前进。

首选第一个选择是微内核还是宏内核。这个基本没有任何争议。作为一个公司之间的玩具,互相解耦是第一位的。想要快速产业化,允许参与者独立变更,微内核几乎是唯一选择。宏内核在Linus之前,没有人敢想到能成功。直到今天,这个宏内核能走多远,也没有人能把握。宏内核需要一个独裁者,他提纲挈领,乾纲独断。在你走偏的时候,能直接把你的路封掉。整个Linux世界,离开Linus本身,没有任何人有这种威信(想象一下皇帝驾崩三个儿子怎么治理国家)。

部门之间,公司之间,都是微内核更合理。微内核是一种社会性的内核,宏内核是一个技术性的内核。要说性能,微内核无论如何也不可能击败宏内核,因为宏内核相当于任何一个细分变更都需要整合测试。任何一个细分都在整体的框架内变更,包括架构风格甚至编码风格。

所以很容易想到的理想操作系统的样子是Android的设计架构+微内核。这个微内核就直接借鉴Windows的成功经验就好,Windows的设计代表目前商业操作系统的最高水平,没有产品层面经久不衰的成功经验的任何团队,都不会敢在微内核的设计上抛弃Windows的设计,最多是先学习,后想办法超越。包括现在的鸿蒙,其微内核的架构也一定是类似的。

Windows的典型微内核架构是Object设计。内核管理的资源都是一个一个的Object,打开每个Object都有对应的Handle。微内核中还必须要组织进程线程之类的调度单元,这种操作系统一路发展下来沉淀出来的架构不可能被一个新系统直接抛弃,除非他想像IBM 360一样的下场。安全性是附加在Object上的。微内核包括的要保证是必须要包括并且只能被包括的,典型的是ring 3的特权代码。为了达到所有ring3代码在微内核运行的目标,很多周边架构就必须要设计进内核。比如进程线程等是CPU的直接资源使用单元,调度这个事情到底是设计到用户空间还是内核空间?这个并不一定有确定的答案,但是Fushsia选择了放在内核,那么就代表了进程间的通信机制也必须要在内核里面。Windows已经增加用户空间的调度接口,但是也并不意味着微内核就撒手不管了。操作系统是一个在安全性,易用性和符合社会组织方式的发展过程中一起动态变化的一个过程。

Windows下一个非常精彩的设计是Event,Linux下类似的设计是signal。两个可以说完全不一样,但是从哲学层面又有很多相似的地方。谷歌经过深入的思考和实践,认为两者是可以结合的。状态,事件,信号这三个东西,本身可以抽象为Object和Signal,Event三种互相结合的模型。每个Object都有32个信号集,Event也是一个Object,可以说是最简单的Object,他里面只有32个信号集。所以的Object的信号集的变化就代表他们状态的变化。也就是说,信号与状态协调成一个概念,就是一个Object所持有的信号集,信号的发生就可以是事件的发生,几个不同的设计被谷歌经过精简和联动设计,提取他们的核心本质,保留他们的各自优点,就形成了Fushsia的Object和Signal模型。

Windows下的安全也是Object的属性,Fushsia在微内核层次完全抛弃了Linux的用户权限概念,改为了Windows的Object权限。甚至更激进的让整个虚拟化建立在Object权限之上(更确切的说是Object对应的HANDLE)。Linux下的最大抽象是一切皆文件,微内核的经验是一切皆Object,包括进程这个单位。很多Fushsia的系统调用都要传入一个进程的HANDLE,这个HANDLE就决定了这个系统调用有没有权限继续下去。Windows最新的成果是虚拟化和沙箱技术的重度迭代,Fushsia从设计层面就直接做到了。所以说Fushsia更像是一个没有包袱的Windows系统的重构,保留了Windows的大部分优点。

Fushsia在很多地方有不同的想法。例如Windows中饱受诟病的进程间互写内核成为很多安全问题的摇篮。但是进程间互相传递HANDLE又是Windows下一个很好用的资源传递的功能。在Linux下一个被打开的资源想要传递给另外一个进程,早期除了fork是没有方法的,后面出现了SCM技术,就是通过Unix Domain Socket来直接传递fd。因为你不能默认需要传递资源的环境都是父子进程关系。Linux一个很大的设计问题就是太过依赖父子关系,Windows的问题又是因为太过不依赖父子关系,导致进程间缺乏有效的组织。近期的Windows版本已经很注重进程间关系的组织,或多或少的引入Linux下的进程关系树模型。双方在靠拢的过程中,都有一些逐渐的对原生设计的一些违背。导致整个操作系统看起来不太和谐。这个问题在Android系统中被谷歌刻意的放大了。因为Android系统重度的依赖Binder,Binder是一个试图让各个进程可以很方便的通信交换资源的设计,这个设计对Linux来说是强人所难。对Linux的要求有点过于激烈。Linux对于资源的进程隔离一直是一个很重要的发展方向,Android反其道而行,要求进程之间大门敞开。阅读Binder的代码就能很容易的知道这个系统做的是多么的艰难。反而Binder在Windows上就很容易实现,LPC是如此的高效,进程间可以方便的把自己的HANDLE写入到另外一个进程的内存,资源的传递几乎是没有什么成本的。但是直接写对面进程的内核对于权限和并发又带来了复杂性。所以Fushsia既然是Android架构的落地,自然重度的依赖这个特点。于是Fushsia直接将Golang中成功实践的Channel的思想在Fushsia中落地。传递HANDLE,只需要把HANDLE放进Channel中,原进程自动失去该资源,Channel对面的进程自动获得该资源。

这里面就有一个问题,就是内核块本身也是资源,也是可以有HANDLE的。Linux下这种哲学几乎没有体现,Android为了落实他的架构,非常艰难的设计了ashmem,用内存文件系统生硬的设计了带有handle的内核块。Windows的底层内存管理是带有HANDLE的,叫做Section,一个Section是64KB的大块。但是Windows在往上层暴露的时候,仍然没有选择直接把section直接给用户用,而是进行了封装。但是微内核中就是section和其对应的HANDLE的方式。该方式被证明了可以同时管理内存块和文件映射,还可以向上提供更高层的内存分配机制。对于Android对HANDLE资源转移的渴求,没有理由不去学习Windows的Section机制。Fushsia比Windows走的还远,并没有直接采用Windows的section,而是创造性的设计了pager和VMO,VMAR这几种对象。同样是基于对象的,将连续内存和页更灵活的进行了对象的抽象,克服了Windows下的granulary固定的问题(默认64KB)。正是因为更机制化的对象化,整个设计中没有添加策略属性,所以就可以把策略上升到微内核之外。也就是说,操作系统的服务部分应该去负责整个的内存管理,但是内存的颗粒度对应的是内核中的硬件Object。这又是一个对于Windows下内存问题的一个巨大的改善设计。

Windows下对任务的组织采用job,进程,线程,纤程四个维度。这一点也是完胜Linux下绕口的Session组,进程组,线程对应内核进程等一大堆看起来非常别扭的添砖加瓦。Fushsia同样是在Windows的基础上进行增强。进程组织成job,进程下有线程。

Windows下还有一个让人印象深刻的设计,就是Completion Port。当有大量的事件发生的时候,Completion Port将其抽象为对一个HANDLE的发送数据包消息。对比Linux,类似的事情使用epoll,ppoll等事件集合机制,Completion Port在设计上非常的干净。同样的思想直接被Fushsia采用,并且进行了“本土化”实现。

在锁这件事情上,从Linux环境工作久了再切换到Windows,会明显的感觉Windows的设计非常差。Linux作为一个服务端市场占有率第一的操作系统,其对并发问题的处理是非常好的。这里是指技术层面,而不是架构层面。一个最大的优势是Linux把所有的锁实现都抽象成一个对futex系统调用的依赖。Futex的实际意义其实跟锁没有任何关系,只是一个等待条件并且唤醒对应的线程的机制,也就是一个单纯的线程的同步机制。Linux成功的做到让各种各样的锁都依赖一个简单的同步机制来达到实现目的。如此好的抽象正式Fushsia要学习和借鉴的。而Windows下,Mutex,CriticalSection,RWLOCK等使用过程,跨进程还是进程内,不同的性能表现,全部是黑盒并且各自独立的。这显然不是一个微内核该有的样子。但是Windows的微内核是否提供类似futex的机制支撑上层锁的实现,这个我不清楚,没有逆向看过。反正我对Windows下的锁设计是不太喜欢的。

调度算法上,操作系统产业界都逐渐的往Fair Scheduling过度,尤其是Linux。Fushsia不能参考Windows的,因为细节没人知道,Linux长期的工程实践已经证明可行性。要知道,早期的Linux调度是动不动就被挨骂的。走到现在积累下来的经验不容易。

隔离,是现在Windows和Linux都在面临的一个问题,服务端先对隔离性发起重度需求,需求来的猛烈程度,可以用革命性需求来形容。一时间,Linux虚拟化技术雨后春笋,蓬勃发展。因为Linux先于Windows几年支持了这件事情。一切竟然归因为机缘巧合下Linux一个一度被认为没有什么意义的LXC虚拟化技术的产品化创意。你可以说出一大堆的Linux发展虚拟化的优势,但是没有LXC这种被嫌弃的技术作为铺垫,很可能第一个Docker都没有勇气发布,也就没有之后的春天。Windows近年来奋起直追,试图追赶并超越Linux在虚拟化上的优势,同时伴随自己的UWP技术对隔离性的强烈需求,Windows创造了属于自己的隔离化,并且试图侵蚀Linux的阵地。WSL技术的发展也不遗余力。Fushsia看到了虚拟化的威力,Android的经验已经告诉了谷歌,不但是服务端,客户端对隔离性的要求指挥更强,因为Fushsia的隔离性是一个深入骨髓的设计,体现的非常彻底的整个架构的隔离性重构。典型的,连我们熟知的操作系统级别的文件系统都没有,没有根目录,每个进程为单位只能看到自己的私有目录。完整的去掉了Fork,这一点连Windows都要对之敬畏。Windows还是允许Object在进程创造之间进行继承的,所以说Windows是对fork概念的选择性支持,但是远远没有Linux的全量继承那么过分。Linux下的子进程要对父进程进行减法,一些变动会导致减法经常的没有减到位,就会有很多的安全问题。Fork的设计就不是为了隔离性而设计的,相反,他是完全共享数据甚至逻辑通道的。是一个完全的反隔离设计。Fushsia对此进行了完全的重新设计,新建的进程是干干静静的容器。类似Windows的新技术UWP,完全的沙箱,从底层杜绝了逃逸的可能。Windows也会非常羡慕Fushsia可以没有历史包袱直接进行非常激进纯粹的沙箱设计。

其他的类似的领域还有TLS,DMA,日志,中断等。都在在以Linux和Windows,Android使用过程中的一系列需求和他们蹩脚的满足方式找到一个使用和架构的最佳平衡进行的重新设计。可以说Fushsia的Zircon微内核是一个最能匹配当前所有场景应用的架构设计。系统调用数量非常少,Linux一直在精简,但是历史包袱严重,Windows的微内核让人无法区别系统调用的层次,导致API非常混乱。Zircon开了一个好头,提供了系统调用集,但是又把大量的工作交给了服务。服务也是Windows上落地非常好的一个概念,是微内核的必备的匹配组件。Android的架构设计了大量的服务,例如SurfaceFlinger,Zygo等,Linux下缺乏对服务的有效表达。因为宏内核的很多本应该是服务的逻辑单位被做成了内核线程,但是又做的不全。Linux的宏内核自己做了大量的服务,但是又做的不够全,也不可能全,还是需要用户空间的服务来补全。例如systemd等试图统一的把Linux的服务组织起来,但是各种target和启动流程,服务管理,依赖关系,把整个系统搞的无比复杂。我个人对Linux的服务面的设计一直是嗤之以鼻,因为一直有个Windows的对比在。不过Windows的服务也不能说没有问题。很多流氓软件利用服务做了很多影响体验的事情,苹果在这一方面就控制的非常好。可以说Windows是一个设计和实现在架构层面都非常优秀的系统,但是反应速度太慢,远不如Linux的适应性。而且微软公司本身对这个系统的控制性不强,导致有时候用户被流氓软件控制了。而苹果就太强,所有人都感觉被苹果控制了。Android的设计是一个平衡,隔离性,控制性都刚刚好。它可以由一个OEM进行强控制,也可以通用性的完全不控制。是一个很灵活的社会化设计。反观Windows类似概念的企业版,组织能控制的东西非常有限,并且基本上应用也只是公司的内部。类似的组织交付能力,微软不愿意给任何人,所以过度开放。不过近期有所收紧。苹果也是不愿意给任何人,所以采取了过度收紧。

谷歌畅想的,是一个能屈能伸,能开源,能赚钱,能让自己赚钱,能让别人赚钱的大社会型架构。谷歌非常深刻的知道开源模式的力量,也知道闭源世界的强大影响力。更多的社会属性是让谷歌脱颖而出的最重要因素。因为他“接地气”。

在驱动层面,Zircon甚至开放了用户程序中断处理的系统调用,内核层面相当于直接对驱动进行应用层委派,但是又不是给应用程序,而是给DDK,一个专门的允许闭源闭源驱动存在的框架。Android给谷歌带来的一个最大的收益,是对驱动世界的深入了解。DDK就是这种提取升华,然后重新落地的设计。我一直认为Windows是Zircon的架构学习对象,Linux是一个很好的验证算法逻辑的对象,也是一个不错的教材。Android则是一次尝试,一次架构设计的验证和社会性的学习积累。目标都是最后的大一统设计。整个Fushsia的核心IPC调用接口是FIDL (or "Fuchsia Interface Definition Language") ,这种描述性的IPC表达方式简直与binder如出一辙。Component,capbility和manifest的概念也正是Android验证的开发模式(capbility这个说不清楚有没有参考Linux)。随着Flutter的逐步推广,Fushsia的模型有效性正在由Android和Windows一步一步的验证。文件系统这个点是Linux和Windows都称不上是做好的。Fushsia有自己的设计,但是是否足够好,我无法评价。恐怕要等实际的落地才能知道。IO这个点,Windows无数败笔,Linux的电梯和缓存设计也称不上是优良,Fushsia一定可以做得更好,毕竟重新设计,完整参考。但是能好到哪里去,我觉得得后面再看。比如Android用到的ashmem,在Windows下同样功能要模拟会痛不欲生,在Linux下也是依赖已有的tmp文件系统来的。Fushsia就直接出现了MemFS这种量身打造的文件系统。Volume Manager这个在Linux上实现的非常好,但是在Windows上又惨不忍睹。Fushsia也进行了重新设计。Mount的挂载概念是Linux上的一个基本功能,这个设计是如此的优秀,现在的Windows也开始学习,Windows在进入虚拟化领域的时候发现把一个文件系统挂载到别的磁盘的目录的必要性,也产生的对应的技术。这种被一个系统发明(也不能说是Linux发明的),被Windows都采纳了的设计,Fushsia自然也不会错过。网络文件系统在Linux和Windows中实现的都不算好,但是又各有千秋,WSL中对9P协议的应用被谷歌注意到了,但是9P的落地又变成了一个延迟很高的纸上谈兵。Fushsia类似的发展路线,用了现在Windows和Android都有的描述性接口和建立连接的概念进行IO设计,效果如何有待评价。我是十分担忧会重走9P的覆辙。Windows和谷歌内部有很多过于重视架构完备性的理想主义工程师,已经做出来很多“神奇的”作品了。其架构水准之高很多时候都可以掩盖实现之丑陋。但是在性能层面,却是掩盖不了的。性能过程,大部分情况下,就是一个不断破坏架构优美的破坏性过程。我是做性能的,这句话我是真的深有体会。

显示是Linux领域的一个最大败笔,好在有Android挽回一些,但是也是付出了巨大的努力。Windows引以为傲的显示是从Directx到驱动,从软件到硬件的深度定制合作产生的。这种通常都有非常大的商业动机。Linux本身不具备这种动机,手机具备。Fushsia对于这种决定生死的领域自然是非常看重,在Android积累的大量渲染经验,让Fushsia充分意识到渲染部分的独立性。Android对SurfaceFlinger的设计和游戏本体渲染的隔离,让传统的单线程渲染非常笨重。这种架构天然是过渡期的产品,Vulkan和Directx12本身就是对这种架构的抛弃。渲染子系统本身就要负责任务的调度和内存的管理,这是现代渲染架构都已经认清的问题了。从架构层面或者操作系统过多的干预渲染过程,坏处远远大于好处,因为渲染是一个性能第一的领域,你的架构再优美,在性能面前也有强烈的放弃动机。在Vulkan和Directx12这种命令队列缓存和任务调度,内存管理高度策略化的发展浪潮下,Fushsia作为一个新时代的操作系统自觉的对渲染作出让步。让渲染子系统能够承担越来越多的自主性工作。不但如此,Direct Compute等显卡计算技术,已经让CPU调度层面认识到计算方面的不足,计算在未来也会可以预见的更多的交给渲染管线。CPU本身和GPU之争,在软件层面出现了第一次的控制权让步。

Fushsia是一个新时代的操作系统,他的诞生几乎是建立在Windows和Linux的痛苦的基础之上的。变化的是需求,架构能不能更好的适应快速变化的需求是一个操作系统长期发展的重要保证。现在Linux和Windows都已经老态龙钟,但却仍然在坚持更新,不断的一次一次的创新和突破满足社会的需求。Windows和Linux像两个兄弟,社会对操作系统的需求Windows扛不住的时候Linux顶,Linux顶不住的地方Windows顶。双方在迭代的过程中终于逐渐的在领域层进行了划分。与其说是Linux更适合服务端,Windows更适合客户端,不如说是Linux在满足需求的过程中选择了去优先满足服务端,甚至把嵌入式都快丢掉了。Windows在选择满足需求的过程中选择了优先去满足客户端。两个都看着对方的地盘不肯放弃希望。

Fushsia建立在两个操作系统发展,定位的过程中,博采两家所长,进行的站在巨人的肩膀上的重新设计。这次,不是DEMO,用的也不是java。这次,谷歌的目标是终极。他想要在此终结操作系统的争论。我相信鸿蒙的设计不可能超脱Fushsia之外,要是天天吼着脱离时代的吊打那显然不是这个时代的人。没有人能超脱时代而存在,技术的演进必须要在已有的技术基础之上进行哲学层面,设计层面,实现层面的创新。如果说Fushsia是对Windows和Linux的创新,我相信鸿蒙应该是在Fushsia的哲学基础之上进行创新。因为没有理由谷歌的总结,实验和发现得到的优质财产鸿蒙不去继承。在这个基础之上进行修改才是合理的选择。凭空而出的不同架构,完全重写。我个人不在赞成也不太相信。等他开源看吧。

Published by

风君子

独自遨游何稽首 揭天掀地慰生平

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注