1975年,美国罗彻斯特大学纽约分校,一组研究员正在做一个名为RIG(Rochester’s Intelligent Gateway)的项目,它由Jerry Feldman主持设计。RIG的目标是给所有本地以及远端的计算设备(比如磁盘、列印机、磁带、绘图机等)提供一组统一的访问方式,其作业系统称为Aleph。为了实现所需要的功能,Aleph的内核主要构建了一个进程交互(Interprocess Communication,IPC)的机制。RIG的各进程,只要设置了目标端口,就可以彼此间发送信息。RIG项目没过几年就被判了死刑,主要是缺少很多有用的功能,比如端口没有保护机制,一次最多只能发送2KB大小的信息(受硬件限制),也没有很好的网络支持等。不过在20世纪70年代,这个系统依然代表着当时作业系统设计的先进水平,比如除了进程交互外,每个进程还有内存保护的功能,这足以让20世纪90年代末都没有做出内存保护技术的Apple公司汗颜。
该项目后来失败了,随后在1979年,RIG的Richard Rashid博士毕业到卡内基-梅隆大学当教授,开始做Accent项目。它是一个网络作业系统,于1981年4月开始活跃开发。受RIG的影响,Accent系统的亮点也在于可以使用IPC,而且解决了很多RIG的不足。比如每个进程有4GB的虚拟内存空间,而且甚至连内核自已都可以被存入缓存页面,内存有先进的更新前拷贝(Copy-on-Write)功能,可以实现进程间大信息的传送等。读者可以把Accent理解为支持虚拟内存技术,并且具有网络透明IPC功能的RIG内核。
但过了几年,开发者们越来越对Accent失去兴趣。在1980年初,很多人觉得多核计算是计算机未来发展的潮流,但Accent内核在设计时并没有考虑到这些问题。而且,随着许多实验室纷纷购置性能更强劲的计算机,这就意味着Accent需要移植到新的目标架构上。此外,Unix正大行其道,不管是在作业系统理论上还是在用户程序上,都成为最为流行的作业系统模式,而Accent并不是一个Unix系统,所以无法享受Unix世界的诸多美好。为了解决这个问题,研究人员决定把所有设计推翻重来,于是就有了一个全新的系统。
在匹兹堡的一个雨天,卡内基-梅隆大学的Avie Tevanian,此系统的最主要开发者,正打着伞和同学们在去吃午饭的路上。他们一边绕着无数的泥塘,一边构思给这个新系统取什么名字好。灵感突来,Avadis Tevanian建议把这个系统叫作Muck,引得同学们哈哈大笑。后来,Richard Rashid和一位意大利同事Dario Giuse说起这玩笑,结果这位同事不经意地把Muck发为Mach,遂把Richard Rashid笑翻,伟大的Mach系统因此得名。
Mach是一个受Accent启发而搞出的Unix兼容系统。那年,Unix已经十六岁,而且依然是作业系统理论与实践开发的主要阵地。Unix内核由于新加入的功能越来越多,变得越来越复杂。而Mach的一个主要目标就是尽量缩减Unix的各项服务,以使内核变得简单可维护。此项目从1984年开始,目标主要是包含完整的多任务支援、良好的硬件移植性,并要把大量服务移出内核作为跑在内核上的服务,以及提供与Unix的兼容性。
Mach使用纯C编写,所以在一定程度上保证了可移植性,这事实上为后面的NeXT向PowerPC移植以及2005年的向Intel移植提供了很重要的前提。而为了缩减内核该管的任务,Mach做得很绝,只提供内存和处理器管理。类似于档案系统、网络、输入输出等功能都作为单个的系统进程,独立执行于内核之上。Mach的开发过程以4.3BSD作为起点,以RIG的Accent作为参考,采纳DEC的虚拟内存设计思路,逐步开发,以新写的代码代替BSD的代码。两年后的1986年,虽然没能把系统服务完全分离于内核之外,但已颇见成效。Mach第一版大功告成,组员发表会议论文,成为操作系统史上里程碑式的经典,引发操作系统业界的“微内核”学潮,如今学习作业系统设计的皆需学习此文,二十五年来被引用一千二百余次。
这篇文章主要讲了两方面内容:IPC和虚拟内存。在IPC方面,Mach把复杂的消息传送机制分为四个独立的清晰概念—任务、线程、端口、信息。任务是拥有一组系统资源的对象,允许线程在其中执行;线程是执行的基本单位,拥有一个任务的上下文,并且共享任务中的资源。
由于该论文的影响力,所以项目得到了OSF(Open Software Foundation)在内的很多投资。当然了,学术和工程永远存在差距,所以即使是最受欢迎的Mach 2.5其实仍然是一个包括大多数BSD服务层的单内核。但包括NeXTSTEP、OSF/1在内的很多操作系统都采用Mach作为其内核技术,原因是广大研究人员依然相信微内核代表着未来。虽然Mach 2.5的效率比传统的Unix系统稍低一些,但研究者们表示情绪淡定,因为Mach支持多处理器系统,可以利用多线程把任务处理得飞快,相比之下其他Unix内核并没有多处理器的完善支援,因此Mach效率稍低完全可以接受。但随着真正把Mach和BSD服务完全脱离的Mach 3微内核面世,研究人员们的情绪就再也淡定不起来了。因为服务和内核分离后,任务间的IPC数量暴涨,一个简单的Unix系统调用要涉及到十多个开端口、设权限、发送、收取消息的操作,哪怕是使用数年后的1997年的硬件,跑一个系统调用密集的程序,Mach的效率要比一般的Unix系统慢50%,而且根本没有什么好方法来解决这个问题。
所以Mach 3出来后,虽有少数微内核信徒继续执著地改进Mach,或者开始其他微内核比如L4的研究。但学术界对Mach的兴趣大减,因而Mach 3也成为最后一版。项目解散后,Richard Rashid去了微软研究院。
再说我们的主角Avie Tevanian,他1987年博士毕业去了NeXT。这家公司刚刚由Steve Jobs成立两年,这两年Steve Jobs啥正经事都没干,只是花了十万美元雇Paul Rand设计了一个公司商标。直到Avie Tevanian加入后,这个公司才开始干实事。1987年公司确认要开发一个面向研究人员使用的计算机工作站,于是软硬件的开发工作紧锣密鼓地展开。硬件组由领导过Apple Lisa的Rich Page原班人马负责,而软件则由Avie Tevanian负责,计划开发一个有图形界面的操作系统NeXTSTEP。由于Avie Tevanian是Mach主要的开发者,自然NeXTSTEP就基于Mach了。1988年10月12日,NeXT发布预览版(0.8版),并于1989年9月18日发布1.0版(注:http://en.wikipedia.org/wiki/NeXTSTEP)。
作为NeXTSTEP系统的内核,NeXT分支的Mach经历了不少变化。NeXTSTEP 0.8主要使用Mach 2.0版,而稍后的NeXTSTEP 1.0版主要基于Mach 2.5版,包含一个自己定制的当时最新的4.3BSD服务层。从3.1版开始,NeXT分支的Mach还包括一个全新的设备驱动框架, 名为Driver Kit,仅供x86系列的硬件使用。和Mach以及BSD代码不同,Driver Kit是使用Objective-C写的。为什么是一个面向对象的语言呢?看NeXTSTEP 3.3的DriverKit文档。读者大概就会发现,NeXTSTEP把所有硬件设备理解为对象,而我们知道,对象之间有继承关系,比如,磁盘(IODisk物件)属于输入输出设备(IODevice物件)的子物件,而磁盘(IODisk)本身又是逻辑磁盘(IOLogicalDisk)的父物件。硬件的初始化对应于每个物件的初始化(init方法),硬件又有读、写,所以可以用getter/setter的方法。因此,DriverKit是一个非常有特色的实现。而且由于Objective-C的效率很高,依赖很少(Objective-C程序可以直接被编译器翻译成等价的C语言程序并编译,而Objective-C的运行库libobjc也以高效著称),所以也是编写驱动的良好选择。几年后的IOKit其实就是个DriverKit的翻版。
这时,NeXTSTEP操作系统大获成功,风险投资商们纷纷购买,但硬件却始终卖不出去(注:Aaron Hillegass《Cocoa Programming for Mac OS X》前言),所以NeXT砍掉了硬件部门专做软件,更是使NeXTSTEP发展到了巅峰时期,同时支持68K、x86、PA-RISC和SPARC等硬件,但颇有意味的是它就是不支持PowerPC架构。它可以同时产生一个包含所有架构可执行码的二进制文件,来使开发的程序在所有平台上执行。这个功能也影响了后来Mac OS X的技术。Mac OS X 10.4时代有两件跨时代意义的事情,一件是Apple搞出了64位的Power Mac,开发者可以发布一个包含64位和32位程序的单一可执行文件,而无需让用户去区分;另一件是和Intel合作。Apple正式发表了Universal Binary技术,可以一个Mach-O文件同时包含Intel和PowerPC的指令。这非常贴心的设计(要知道,大多数电脑用户根本不知道Intel、PowerPC、64位、32位等技术)就是来自于Mach的技术。
NeXTSTEP 3.3后,NeXTSTEP因为NeXT和Sun的合作改名为OPENSTEP,1996年发布4.0版,到1997年2月4日,NeXT被Apple收购之前,期间内核改进除源码同步到Mach 3.0版外不明,而且出于不知道的原因,我手头的OPENSTEP正式版光盘中,居然找不到DriverKit的发布说明和编程文档,故不作详述。不过这段时间,Apple的活动值得好好一说。之前在《Linus Torvalds的短视》中,我们曾提到,1996年,Apple和OSF曾经合作,把Mach移到PowerPC Mac上,再把Linux作为单一的服务跑在Mach上,这个项目叫做MkLinux。在1996年发布基于Mach 3.0和Linux 1.3的预览版,并更新到2002年结束其历史使命,对Mach在PowerPC的移植性上做出了重要贡献。这个PowerPC版的Mach被叫作osfmk分支,也正是现在Mac OS X中用的分支。当然了,NeXT被合并后做了大量修改。
Apple收购NeXT后,Mach被确定作为未来的操作系统核心。Avie Tevanian被选为软件开发部的总裁。合并所有项目的号角吹响后,上层的OpenStep API和老版Mac OS的部件开始合并,而Mach也经历重大变化。主要是一方面,Mach使用了osfmk分支,但依然包含4.3BSD服务;另一方面,DriverKit被IOKit取代。这是Apple走得很被动的一步。因为当时外界普遍对Objective-C不看好,逼着Apple走老版Mac OS API的老路。而Apple自己对Objective-C也很不自信,甚至想索性换用Java了事(我们以后会谈及这段不自信的历史)。所以IOKit是一个C++的驱动架构,来符合大众口味。这些改变最早在Rhapsody中出现(我们以后也会有一期Rhapsody的专题)。但由于C++是门很恐怖的语言,所以Apple又把C++给阉割了,去掉了多重继承、模板、运行时动态以及异常,让开发者使用这种对于Objective-C来说换汤不换药的Clean C++来做驱动。但公正地说,IOKit对于Driver Kit是有不少改进的,比如IOKit可以写在用户空间跑的驱动(虽然大多仍是跑在内核空间上的),因而驱动挂了而系统不会挂。另外IOKit考虑到了计算机发展的趋势,所以在电源管理、即插即用、动态加载上做得更好。
但各位也知道,C++程序得用专门的运行库才能跑,所以Mach中又加入了一个叫作libkern的库负责C++相关的功能,同时,还有一个libsa的库提供一些类似二分查找、排序等基本算法之类的功能。最后和硬件相关的还有一个叫作pexpert(Platform Expert)的库,负责收集硬件设备列表、检测机器种类(比如处理器速度等)、解析启动参数等杂活。
至此,Mac OS X的内核完全形成,形成BSD、IOKit、Mach osfmk三足鼎立的态势,并有pexpert、libkern、libsa作为基础。Apple称它的内核杰作为XNU。其代码开源,请读者移步http://www.opensource.apple.com/source/xnu/xnu-123.5/,每个部分的代码都独立存放在一个文件夹中,条理清晰,不妨一读。
由于4.3BSD已是过眼烟云,Apple后来投入大量资源扶持FreeBSD开发。2001年,Apple将FreeBSD的发起者、领军人物Jordan Hubbard收入麾下,并在Mac OS X 10.3时基本同步到FreeBSD 5的代码(注:http://osxbook.com/book/bonus/ancient/whatismacosx/arch_xnu.html)。
另外,Apple 的开发也同时反馈到FreeBSD小组,包括FreeBSD 6.2 内核引入的 AUDIT (man audit 或参见http://manpages.unixforum.co.uk/man-pages/unix/freebsd-6.2/4/audit-man-page.html),后来 FreeBSD 8引入的 libdispatch (http://wiki.freebsd.org/GCD, 在Apple这项技术叫Grand Central Dispatch,是Mac OS X 10.6 主推的新功能,FreeBSD基本在 Mac OS X 10.6 上市的同时就拥有这项最新技术),以及 FreeBSD-CURRENT 中的 LLVM-Clang,全是Apple的手笔。从 1999 年开始,FreeBSD 源码仓库可以搜索到 Apple 提供的大量的补丁以及新功能。
Mac OS X早期版本不太稳定,所以会内核崩溃。10.0版本会直接像Linux或者BSD那样打出回溯信息,很不美观,所以Apple在10.2版本开始设计了一个多国语言的图片告诉用户你的内核崩溃了,以让内核崩得看起来更优雅一点。由于包含四国语言,被国内用户戏称为“四国”(注:优雅的图片见http://support.apple.com/kb/ht1392),这是XNU的Mach osfmk部分的功能。但从10.3~10.4版本开始,系统越发稳定,正常使用已很少见到内核崩溃。而且,内核提供的服务也越来越多,使得Mac OS X成为一个完善的系统。
21世纪XNU架构方面的最重大改动是支持了PPC64(10.4版本时代)、x86架构(其实本来也一直支持的,以后讲Apple的Intel迁移时详谈)、x86_64(64位支持是苹果长年努力逐步展开的。10.4时代32位内核支持载入64位的用户程序,10.5系统提供64位的Cocoa框架,但系统 大部分程序都是32位的,10.6时代内核支持以64位模式启动,但在不少硬件上这是非默认的方式,但系统大量程序已被改写并编译为64位的二进制程序,10.7时代内核默认以64位模式启动。)和ARM架构(iPhone和iPad使用XNU内核)等多个新架构。
而其中ARM架构的支持别具意义。但2006年5月31日,功成名就的Avie Tevanian离开Apple另谋发展,此时,离Apple的iPhone奇迹发生,只有不到一年时间。