购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

1.3 主要操作系统与Linux的对比

1.3.1 Linux和Android

Linux操作系统一般指常见的发行版,例如Ubuntu、OpenSuse、Fedora等,另外,Linux操作系统还存在大量的、不常见的发行版,例如Kali等。每一个Linux嵌入式硬件都可以看作携带了一个专用的Linux发行版。

一个Linux操作系统主要包括Linux内核、系统服务和其对应的配置文件、系统目录结构、常用库、程序包、命令工具和图形设施。Linux与Android都有基础的init、libc、常用命令、图形系统、Binder与Ashmem,但是采用了不同的实现,这些共有的部分可以看作一个最小Linux系统,在此之上的系统服务和组件是Linux桌面系统与Android系统的主要区别。

1.init

Linux内核规定要有一个init进程存在,通常init进程承担系统服务管理的职责。继承自System V风格的init系统比较简单,目前仍被应用在嵌入式系统中。后来,大部分的发行版都采用了systemd软件作为系统服务管理程序,而Android采用了自己编写的init工具,通过自定义的一套配置文件管理Android中系统服务的启动。大部分的系统服务都会有配置文件,配置文件被用来改变系统服务的行为,在桌面Linux发行版下的大部分系统服务的配置文件都位于/etc/目录下,而Android的则位于/system/etc/目录下,Android为了与Linux目录结构兼容,将根目录的etc目录链接到/system/etc目录,以此能方便地复用Linux下的一些工具。因此从目录结构上看,Android与其他发行版虽然区别很大,但是有很多相似的地方。

2.libc

所有的Linux系统都会有一个libc库,即运行时的加载器。大部分的桌面Linux发行版的libc库都是glibc,在嵌入式系统中有uClibc、newlib等小型化的libc库,而Android单独开发了一个libc库,叫作Bionic。glibc太过复杂,并且固定了很多操作系统的行为方式,例如DNS的解析缓存也在glibc中实现。Android系统对自定制的需求很强,所以进行了自研,运行加载器ld.so也是重新开发的。Android在ELF的文件格式上做了扩充,并且对应地设计了Android专用的linker程序。在应用层方面,Android可以兼容Linux,但是Linux不能直接兼容Android。

3.常用命令

Linux系统包含系统管理的常用命令,在POSIX中也规定了要包含的这些命令。在桌面Linux发行版中通常包含大量的命令和常用库,而在小型发行版中通常只包含必需的命令和共享库,甚至只包含一个集成必需的命令Busybox。在UNIX中的POSIX约定的命令,在Linux和Android中也会发现类似的结构。

大部分的桌面Linux发行版都会基于yum或者apt的方式进行包管理,一个包就是一个工具,或者说一个软件。在桌面版Linux中的组件大部分都是通过包的形式进行安装后使用的。这种类似的包管理软件有很多,很多发行版都在乐此不疲地推出自己专用的包管理软件。在Android中,由于Android的设计对于使用者来说是封闭的,甚至不允许使用者使用root权限,所以Android不允许动态地安装、卸载系统组件,常用的包管理的方法在Android中被去除了,取而代之的是纯粹的应用市场和对应的应用程序包的管理方式。

4.图形系统

在图形系统上,大部分的桌面Linux发行版都基于X窗口协议(或者Wayland协议),而Android则基于SurfaceFlinger的组件。Android的图形化没有网络传输的需求,但是有跨进程渲染的需求,SurfaceFlinger会完成桌面相关的混合渲染,而在X协议中,所有的渲染都集中传输渲染指令给X服务,由X服务进行渲染。SurfaceFlinger的设计允许应用本身在自己的窗口内调用渲染指令进行内部渲染,而SurfaceFlinger只负责组装渲染后期的呈现效果,这种架构节省了大量的渲染指令传输的开销。

5.Binder与Ashmem

Linux内核还添加了两个比较显著的改动:一个是Binder,另一个是Ashmem。Binder是为了高性能且安全的跨进程通信的需求而设计的,这是因为Android的系统服务层的设计需要大量的性能敏感的跨进程系统调用,而Binder设计了一个SystemServer组件,大量的系统服务存在于SystemServer进程中,对系统服务的需求需要跨进程地去SystemServer中寻求支援,但Linux已有的跨进程通信方案不能满足Android的系统设计需求。高性能且安全的跨进程通信方式几乎是每个系统都要面对的问题。Ashmem是匿名共享内存的实现,其实现方式比较简单,主要作用也是跨进程地共享内存块和提供给Java虚拟机使用。

1.3.2 Windows下Linux运行环境的发展

在Windows上运行Linux程序不是一个最新的需求,从1997年开始,SFU、SUA、Project Astoria、Islandwood、Pico-process、Drawbridge、WSL等项目都持续在做这件事情。

Interix组件或者叫作Windows Services for UNIX(SFU),从1999年开始就提供在Windows上运行Linux程序的能力,后来该组件在Windows Vista和Windows 7版本的时候升级为SUA,在Windows 8.1版本的时候正式被移除。

这类技术的特点是在编译和运行时使用POSIX库的程序的环境,且不提供UNIX二进制文件的运行支持,也就是说,在SUA上运行的程序依然是exe格式的。

在Windows 10版本的时候,有一个需求子项目叫作Project Astoria,该项目能够让Windows 10版本直接运行ARM程序。但是在2016年,这个项目被叫停,只留下了iOS的兼容层Islandwood 项目。Project Astoria项目投入了很多人力,但它内部所产生的技术被一个崭新的项目WSL所使用,这个崭新的项目就是我们现在熟知的Linux子系统。

Islandwood运行iOS的应用需要重新编译,而Project Astoria项目是二进制层面的支持,这与WSL的设计思路一样。除了基于Project Astoria的一些工作,WSL还从其他项目借鉴了另外一个技术:Pico-process。

Pico-process是Drawbridge项目的技术,也是一种新的虚拟化思路,它在用户空间直接运行ELF格式的文件,所用到的系统调用可以在Windows内核里面直接模拟。内核空间实现一个目标平台的API支持,叫作Pico Provider。

Drawbridge代表了一种需求,就是把操作系统当作库来进行虚拟化运行。关于Drawbridge,有一篇论文是描述如何将Windows 7作为Library OS来运行的,其中用到的技术就是pico。Graphene实现了Linux系统调用的Library OS,但遗憾的是Graphene只能运行在Linux系统上。

有一个项目叫LKL,其设计思路是将整个Linux内核变成一个库。但是在使用LKL的时候,需要直接用LKL链接应用程序,这使得原本依赖外部系统调用的程序就不需要系统调用了。这其实是一个消灭系统调用的思路,而不是转换的思路。

目前,Pico-process的兼容性只支持Windows 8.1版本以上的系统。Pico-process对应的WSL技术也继承了同样的问题,并且目前还不支持32位系统,定制性也差,出了问题没办法修补。

Library OS的设计思路与Pico-process是一致的,但是目前没有在Windows上运行Linux的Library OS,只有Graphene能在UNIX系列操作系统上运行Linux。Colinux的设计思路也是类似的。

WSL2代表了微软一个具有战略性高度的项目,其直接从根本上改变了整个Windows的设计方式,使得开源集成虚拟化变得更加彻底。WSL2在本质上是一个完整的Linux内核,用户可以自己编译Linux内核以替换WSL2中使用的内核,这体现出WSL2在兼容性上做得很好。

谷歌的gVisor代表了另外一种思路,即在用户空间实现所有的系统调用,与Windows上的WSL1的方式类似,通过截断系统调用,使得系统调用在用户空间完成,这能保持内核代码的最小化。类似的VBox也有同样的设计思路,在VBox里面有一个Recompiler组件,可以动态地截断系统调用和中断以重新处理,但是性能非常差。

腾讯傲其实就是Pico-process的概念,只是由自己实现Pico Provider,并且自己提供Pico-process的接口。将Linux的系统调用在内核中重新实现一遍,这样的最大好处是可以做到从Window XP到Windows 10的全系统兼容,坏处是内核模块的系统调用的完整实现工作量巨大,且x64的方案需要创新。

网易星云内核是全用户空间的内核模拟实现,其设计思路与WSL1类似,但是不依赖Pico-process,在用户空间实现Linux系统调用和相关文件系统。

1.3.3 Fuchsia OS与Windows、Linux的对比

微内核系统Fuchsia OS的设计大量地参考了Windows的设计,同时还可以看到很多来自Plan 9和Linux的设计思想。

Windows使用了大量的Object的思想,同为微内核设计的Fuchsia OS毫不避讳地继承了这个设计。内核管理的每一个资源都是Object,打开每个Object都有对应的HANDLE。在微内核中必须组织进程、线程之类的调度单元,这种操作系统一路发展下来所沉淀的架构不可能被直接抛弃。安全性是附加在Object上的。Windows已经增加用户空间的调度接口,但是并不意味着微内核就不介入调度了,Windows的用户空间调度接口至今应用都很少。

Android希望拥有Windows在图形方面(也可以说是硬件支持)的优势,又羡慕Linux内核丰富的功能,而且其从一开始就知道自己不会长期依赖Linux内核。Android更像一个操作系统架构层,验证的是操作系统的架构设计思想。在验证过程中,Android的内核早期只能采用Linux,没有更好的选择。Fuchsia OS是Android的下层替换,但是这个替换并不是代码层面上的,而是架构层面的。现在的Android AOSP源代码已经可以直接编译出Fuchsia OS的系统。HarmonyOS操作系统的下层内核也并不限于特定的内核,这种内核与系统解耦和的方式是操作系统发展的一个新趋势。

Windows下表示事件的设计是Event,Linux下类似的设计是Signal。这两个设计完全不一样,但是又有很多相似的地方。谷歌经过深入的思考和实践,认为两者是可以结合的,尤其是Plan 9的Namespace注入的通信方式和Golang语言设计中积累的管道通信方法给两者结合提供了更多的思路。在Fuchsia OS中,状态、事件和信号可以分别抽象为Object、Signal和Event三种互相结合的模型。每个Object都有32个信号集,Event也是一个Object,且可以说是最简单的Object。Object的信号集的变化代表Object状态的变化,也就是说可以将信号与状态协调成一个概念。谷歌提取这些设计的核心本质,保留它们的各自优点,就形成了Fuchsia OS的Object和Signal模型。

Fuchsia OS在微内核层次完全抛弃了Linux的用户权限概念,改为了Windows的Object权限,甚至让整个虚拟化都建立在Object对应的HANDLE的权限上。很多Fuchsia OS的系统调用都要传入一个进程的HANDLE,这个HANDLE决定了这个系统调用有没有权限继续运作下去。虚拟化和沙箱技术是Windows的一个发展趋势,Fuchsia OS从设计层面就直接做到了。所以说Fuchsia OS更像是一个没有包袱的Windows系统的重构,保留了Windows的大部分优点。

Windows中的进程间互写内存,Linux下的process_vm_writev系统调用也能做到类似的事情。进程间互相传递HANDLE是Windows一个很好用的资源传递的功能。在Linux下,想要将一个被打开的资源传递给另外一个进程,早期除了Fork技术,没有别的方法可以完成,后来出现了SCM技术,即通过UNIX Domain Socket来直接传递fd,在Android的设计中重度使用了这个功能。Linux一个很大的设计问题是太过依赖父子关系,Windows的问题是太不依赖父子关系,导致进程间缺乏有效的组织。较新的Windows版本已经很注重进程间关系的组织,或多或少地引入了Linux的进程关系树模型。

Linux和Windows的设计在相互融合的过程中都有一些对原生设计的违背,导致整个操作系统看起来不太和谐,这个问题在Android系统中被放大了。Android系统重度依赖Binder,Binder是一个试图让各个进程都可以很方便地通信、交换资源的设计,这个设计对Linux来说实现起来非常困难。Linux对于资源的进程隔离一直是一个很重要的发展方向,Android反其道而行之,要求进程之间大门敞开。Fuchsia OS既然是Android架构的落地承载,自然重度依赖Binder。于是,在Golang中成功实践的Channel的思想在Fuchsia OS中落地。传递HANDLE,只需要把HANDLE放进Channel中,使得原进程自动失去该资源,Channel对面的进程自动获得该资源。

内存块本身就是资源,也是可以有HANDLE的。Linux倾向于使用纯粹的内核数据结构来组织整个线性地址空间,而并不用对象来表示。Android为了落实其自身的架构设计了ashmem,用内存文件系统生硬地设计了带有HANDLE的内核块。Windows的底层内存管理是带有HANDLE的,叫作Section,一个Section是64KB。但是Windows在往上层暴露的时候,仍然没有选择直接把Section给用户用,而是进行了封装。Android使用ashmem,但Windows没有提供内存文件系统这种概念,只能使用Section内存进行模拟。Fuchsia OS直接具有Mem FS这种量身打造的文件系统。

在内核中使用Section和对应HANDLE的方式,证明可以同时管理内存块和文件映射,还可以向上提供更高层的内存分配机制。Fuchsia OS并没有直接采用Windows的Section机制,而是创造性地设计了Pager、VMO和VMAR这几种对象,这几种对象将连续内存和页进行了面向对象的抽象,克服了Windows下的granulary固定的问题。

Windows下对任务的组织采用job、进程、线程、纤程四个维度。Fuchsia OS同样是在Windows的基础上进行增强的:进程组织成job,进程下有线程。

Windows下还有一个让人印象深刻的设计,就是Completion Port。当有大量的事件发生的时候,Completion Port将这些事件抽象为对一个HANDLE发送的数据包消息。在Linux中,类似的事情使用epoll、ppoll等事件集合机制。Linux作为一个在服务端市场占有率第一的操作系统,其对并发问题的处理是非常好的(这里是指技术层面,而不是架构层面)。

Linux一个显著的优势是把大部分用户空间的锁实现都抽象成对futex系统调用的依赖。futex的实际意义其实跟锁没有任何关系,只是一个等待条件并且唤醒对应的线程的机制,也就是一个单纯的线程的同步机制。Linux成功地做到让各种各样的锁都依赖一个简单的同步机制来实现目的,好的抽象设计正是Fuchsia OS要学习和借鉴的。而Windows下的Mutex、CriticalSection、RWLOCK等系统锁,有的要跨进程使用,有的只支持在进程内运行,它们全部都是黑盒,并且各自独立,这显然不是好的系统锁该有的样子。

在调度算法上,操作系统产业界都逐渐向Fair Scheduling(公平调度)过渡,尤其是Linux,Fuchsia OS也同样使用了公平调度的思路。

隔离性是目前Windows和Linux都在面临的一个问题,服务端先对隔离性发起重度需求。一时间,Linux虚拟化技术如雨后春笋般蓬勃发展。

Windows近年来奋起直追,试图追赶并超越Linux在虚拟化上的优势,同时伴随自己的UWP技术对隔离性的强烈需求,Windows创造了属于自己的隔离性,并且试图侵蚀Linux的阵地。WSL技术的发展不遗余力,Fuchsia OS看到了虚拟化的威力。Fuchsia OS的隔离性是一个“深入骨髓”的经典设计,深刻体现了整个架构的隔离性重构,既没有我们熟知的操作系统级别的文件系统,也没有根目录,以每个进程为单位,只能看到自己的私有目录,其完整地去掉了Fork。Windows还是允许Object在进程间进行继承的,所以Windows对Fork概念选择性支持。Linux下的子进程要对父进程进行减法操作,一些变动会导致减法操作不到位,从而出现很多安全问题。Fork的设计并不是为了隔离性而设计的,相反,它是完全共享数据,也是一个完全的反隔离设计。

Fuchsia OS的Zircon微内核是一个最能匹配当前所有场景应用的架构设计。Zircon提供了系统调用集,但是又把大量的工作交给了服务。服务也是Windows上落地得非常好的一个概念,是微内核必备的匹配组件。Android的架构设计了大量的服务,例如SurfaceFlinger、Zygo等。Linux的宏内核做了大量的服务,但是也不够全面,还需要用户空间的服务来补全。

在架构层面,Windows是一个设计和实现都非常优秀的系统,但是其反应速度太慢,在适应性上远不如Linux。Android系统设计的平衡性、隔离性和控制性都刚刚好,它可以由一个OEM进行强控制,也可以完全不被控制,是一个很灵活的社会化设计。

在驱动层面,Zircon开放了用户程序中断处理的系统调用,相当于内核层面直接对驱动进行了应用层的委派,但又不是委派给应用程序的,而是委派给DDK的,即一个专门的允许闭源驱动存在的框架。Windows是Zircon架构的主要学习对象;Linux是一个很好的验证算法逻辑的对象;Android则是一次尝试,一次架构设计的验证和社会性的学习积累,也是一个独立于内核的操作系统产品。

整个Fuchsia OS的核心IPC调用接口是FIDL(Fuchsia Interface Definition Language,Fuchsia OS接口定义语言),这种描述性的IPC表达方式简直与Binder如出一辙。随着Flutter的逐步推广,Fuchsia OS模型的有效性正在由Android和Windows一步一步地验证。

Volume Manager在Linux上实现得非常好,但是在Windows上的实现非常不成功。Mount(挂载)是Linux上的一个基本功能,这个设计非常优秀。

网络文件系统在Linux和Windows中实现得都不算好,但是又各有千秋,WSL中对9P协议的应用被谷歌注意到了,但是9P又迟迟没有落地,变成了“纸上谈兵”。Fuchsia OS使用了Windows和Android都有的描述性接口,并对建立连接的概念进行了I/O设计。9P在不断的实践过程中暴露了比Windows本身的I/O还多的性能问题。

显示问题是Linux桌面系统的最大问题,Windows从DirectX到驱动的软件结构得到显卡厂商的大力支持。Fuchsia OS从Android积累了大量的渲染经验,这让Fuchsia OS充分意识到渲染部分的重要性。Android对SurfaceFlinger的设计和游戏本体渲染的隔离,让传统的单线程渲染显得非常笨重。渲染子系统本身要负责对任务的调度和内存的管理,这是现代渲染架构已经认清的问题。Fuchsia OS作为一个新时代的操作系统自觉地对渲染做出让步,让渲染子系统能够承担越来越多的自主性工作。Direct Compute等显卡计算技术已经在CPU调度层面认识到计算方面的不足,可以预见的是,在未来,计算技术也会更多地交给渲染管线来执行。

Windows和Linux像两个兄弟,当Windows满足不了社会对操作系统的需求时,就由Linux来满足;在Linux顶不住压力的地方,就由Windows来代替。双方在迭代的过程中终于逐渐对领域层进行了划分。Linux在满足需求的过程中,选择了优先满足服务端和可裁剪的嵌入式需求;Windows在满足需求的过程中,选择了优先满足桌面。

Fuchsia OS是一个新时代的操作系统,它的诞生几乎是建立在充分了解Windows和Linux的弊端和优势基础之上的。变化的是需求,让架构更好地适应快速变化的需求是一个操作系统长期发展的重要保证。现在,Linux和Windows仍然在坚持不断地创新和突破,以满足社会的需求。Fuchsia OS是站在巨人肩膀上的设计,因为脱离时代的跳跃式发展是不存在的,技术的演进必须要在已有的技术基础之上进行哲学层面、设计层面和实现层面的创新。 Bi2R3xVlfuFt70WegHi2Ft8dEwSotnYSbsokdnzgjLXkGJ86zJ2gRjiT7ssWEOPP

点击中间区域
呼出菜单
上一章
目录
下一章
×