前 言
尽管OS X已经诞生了十几年,但讨论OS X架构的书籍却少之又少,讨论iOS架构的书更是几乎没有。尽管关于Objective-C、框架和OS X的Cocoa API的文档非常多,但是这些文档的讨论往往不够深入,缺少系统调用层次和实现细节。尽管也有一些关于内核的文档(大部分都是Apple公司提供的),但也同样只关注驱动程序的构建(利用I/O Kit),只展示了一些优美的部分,而对于XNU的基础Mach核心却几乎没有任何涉及。XNU是开放源代码的,但是尽管如此,却有着一百多万行的源代码(和注释),有一些源代码甚至可以追溯到1987年,读起来绝对不是一件轻松愉快的事情。
而对于其他操作系统却并非如此。Linux也完全是一个开源的操作系统,但是从来不缺乏相关的书籍,O’Reilly就有很多非常棒的系列。Windows尽管是闭源的,但是Microsoft却提供了非常好的文档(其源码也在一些场合开放了)。本书对于XNU的意义,就好像Bovet和Cesati的Understanding the Linux Kernel对于Linux的意义,以及 Russinovich的Windows Internals对于Windows的意义。这两本书都是很棒的书,非常清晰地阐述了这些异常复杂的操作系统的架构。幸运的是,您正在读的这本书会用同样的方式讲解Apple的操作系统的内部工作原理。
其实之前有过一本关于Mac OS的书,这就是Amit Singh的优秀著作MAC OS X Internals: A Systems Approach,这是一本很棒的参考书,提供了大量有价值的信息。遗憾的是,该书针对的是PowerPC架构,而且在Tiger之后(2006年左右)就没有任何更新了。从那时到现在,6年过去了。在这漫长的6年里,OS X抛弃了PowerPC架构,完全移植到了Intel平台,而且已经经过了4个大版本的迭代。经历了Leopard(美洲豹)、Snow Leopard(雪豹)、Lion(狮子)和最新的Mountain Lion(山狮),野生猫科动物的家族正在扩大,越来越多的新特性被加入操作系统中。不仅如此,OS X还经历了一次全新的移植。这一次移植的目标是ARM架构,改头换面成为了iOS(根据某些统计资料,它是全世界领先的移动环境操作系统)。因此本书在前辈的基础上狗尾续貂,讨论Apple生态系统中新加入的猫科动物,还讨论了一些版本的iOS。
Apple的操作系统被认为是在不断地演进。本书最早是针对iOS 5和Lion编写的,但是这两个操作系统都在持续进化。在本书英文版即将付印的时候,iOS的版本已经是5.1.1了,而且已经出现了iOS 6即将发布的迹象。而OS X版本依然在Lion(10.7.4),但是Mountain Lion(10.8)已经推出开发者预览了,在本书英文版上架的时候应该已经发布正式版了。本书尽可能介绍最新的信息,覆盖所有的版本,并且持续地不断前进。
0.1 概述和阅读建议
这本书的规模很庞大。刚开始的时候,本来没有想把这本书设计得如此庞大细致,但是随着我对OS X的深入了解,我发现了更多的深奥难解的地方,而对于这些难题我找不到详细的解释或文档。因此我发现自己在编写这本书的过程中开始涉及越来越多的方面。一个操作系统是一个完整的生态系统,有着自己的地理形态(硬件)、大气(虚拟内存)、植被和动物(进程)。这本书尝试着尽可能有条理地记录这些内容,同时不牺牲细节的清晰性(或反之亦然)。这不仅仅是一个壮举。
0.1.1 架构一瞥
OS X和iOS有着复杂的架构,这个架构混杂了多项迥异的技术:NeXTSTEP的Cocoa中遗留的OS 9对于OS X的UI和API、BSD的系统调用和内核层、源于NeXTSTEP的内核结构。尽管是一个融合体,但是各个组件之间的界线还是比较清晰的。图0-1给出了这个架构的鸟瞰图,并且标出了每一个组件对应本书的章节。
图0-1 OS X的架构以及每一个组件对应本书的章节
本书还另外包含了一些章节,介绍了和架构无关,但是非常重要的内容,例如调试(第5章)、固件(第6章)和用户态启动过程(第7章)、内核态启动过程(第9章)和内核模块(第18章)。最后还有两个附录,一个附录为POSIX系统调用和Mach陷阱提供了一个快速参考手册(这个附录在本书的支持网站上),另一个附录对Intel架构和ARM架构的汇编语言做了一个简单的介绍。
0.1.2 目标读者
有4类读者可能会对这本书的全部或部分内容感兴趣:
● 想更深入了解OS X工作原理的高级用户和系统管理员。Mac OS的占有率正在稳步增长,争夺了多年来被PC霸占的市场份额。Mac在企业环境中越来越流行,在学术界已经盖过了PC的风头。
● 不满足于Objective-C层面的用户态开发人员,这些人想要了解他们编写的程序是如何真正在系统层次执行的。
● 想要探寻内核态底层驱动程序设计、内核增强或者文件系统和网络层的内核态开发人员。
● 不满足于使用现有工具或补丁进行越狱的黑客和越狱者,这些人想要理解补丁的工作机理和修补的内容,以及如何进一步对系统进行调整使其能满足自己的需求。注意,在这个上下文中,这些目标读者指的是为了兴趣、快乐和挑战而深入了解内部工作原理的人,而不是那些为了任何违法邪恶目的的人。
0.1.3 选择自己的学习路径
尽管这本书可以从头读到尾,不过不要忘了这毕竟是一本技术书籍。书中的章节设计为可以单独阅读,既可以作为详细的解释也可以作为快速参考。既可以按顺序阅读所有章节,也可以随机阅读感兴趣的章节,略读甚至跳过某些章节,以后可以跳回来进行更深入的阅读。如果一章内引用了之前章节讨论的概念或函数,那么这些内容会清晰地标注出来。
您还可以根据自己所属的读者类型选择自己的阅读策略。例如,本书第Ⅰ部分中的几章可以分解为图0-2所示的流程:
图0-2 本书第Ⅰ部分的阅读建议,这一部分关注于用户态的架构
在图0-2中,完整长度的条表示这一章的内容符合目标读者的兴趣,部分长度的条表示至少有部分符合目标读者的兴趣。当然,每一个读者的兴趣都不同。这也是为什么每一章开头都要对本章讨论的内容做一个简单介绍的原因。同样地,只要看一下目录中每一章的小节标题就可以判断这一章是值得仔细阅读还是快速浏览即可。
本书的第Ⅱ部分实际上可以自成一卷。这一部分关注于XNU内核的架构,因此比第Ⅰ部分复杂得多。这是不可能避免的,内核本身就是一个更加复杂、实时且硬件受限的环境。这一部分包含更多的代码清单,甚至包含了少量用汇编实现的代码片段。本书第Ⅱ部分的阅读建议如图0-3所示。
图0-3 本书第Ⅱ部分的阅读建议,这一部分关注于内核
0.2 实验
本书中很多章节都包含了“实验”,实验通常都需要运行一些shell命令,有时候需要运行一些示例程序。它们称为“实验”的原因是这些内容演示了操作系统中可能会变化的方面,这些方面可能会根据操作系统版本或配置发生变化。通常情况下,书中详细演示了这些实验的结果,但是鼓励读者在自己的系统上尝试这些实验,然后自己观察结果。和UNIX一样(Mac OS X实现的就是一个UNIX),Mac OS X也需要通过动手实践才能切身体会并且真正理解,而通过道听途说是不够的。
在有些情况下,试验中有一些部分被留作练习让读者自己完成。尽管本书的支持网站上会有解答(练习的完整可用版本),但是鼓励读者尽可能地自己独立完成这些部分。仔细地阅读本书,只要有少量的常识,您应该可以收获所有应该收获的内容。
0.3 工具
这本书还应用了一些工具,这些工具都是作者为了支撑这本书而开发的。这些工具都继承了UNIX的优良传统,都是命令行工具,而且输出结果很容易被grep(1)处理,因此这些工具不仅适合手工使用,还适合在脚本中调用。
0.3.1 filemon
第3章介绍了一个名为“filemon”的工具,这个工具可以实时显示OS X和iOS上文件系统的活动。这个工具的命名是为了向Russinovich的同名工具致敬。这个简单的工具基于OS X和iOS 5上的FSEvents设备,能够跟踪文件系统相关的事件,例如文件的创建和删除。
0.3.2 psx
第4章介绍了一个名为psx的工具,这是一个类似ps的增强版工具,可以显示OS X中进程和线程的相关信息。这个工具在第4章中特别重要,该章讲解了进程的内部结构,并且演示了一个没有文档的系统调用proc_info。如果查看的是自己创建的线程,这个工具不需要有特别的权限,但是查看其他进程则需要root权限。这个工具可以从本书的支持网站免费下载,带有完整的源代码。
0.3.3 jtool
对于大部分和二进制文件相关的功能,可以使用OS X自带的otool(1),这个工具能够很全面地分析数据区域(data section),而且由于ARM架构具有两种模式的汇编,所以显示ARM二进制文件的时候会让人感到混淆。jtool的目标是增强otool,不仅解决了otool的这些缺陷,还提供了静态二进制分析的新特性。这个工具对第4章特别有用,该章详细分析了Mach-O文件格式,而且由于这个工具有很多有用的特性,例如寻找文件中的引用以及一些有限的反汇编能力,所以在后面几章也挺有用。这个工具可以从本书的支持网站免费下载,但是是闭源的。
0.3.4 dEFI
这个简单的程序可以导出Intel Mac上的固件(EFI)变量,还可以显示注册的EFI提供商。这个工具演示了基本的EFI编程技术——如何与引导服务和运行时服务进行接口。这个工具可以免费下载,带有完整的源代码。第6章介绍这个工具。
0.3.5 joker
第8章介绍的joker工具是一个简单的工具,这个工具可以用来和内核交互(特别是在iOS上)。这个工具可以查找并显示iOS和OS X内核的系统调用和Mach陷阱表,显示sysctl结构,并且在二进制文件中查找特定的模式。这个工具非常适合于逆向工程和黑客一类的活动,因为Apple已经不再导出陷阱和系统调用符号了。
0.3.6 corerupt
第11章讨论Mach虚拟内存管理器的底层API。为了演示这些API是多么强大(和危险),本书提供了corerupt工具。通过这个工具可以将任何进程的虚拟内存映射以核心转储兼容的格式导出到一个文件,类似于Windows的Create Dump File功能,也很像Mac OS X Internals: A Systems Approach一书中使用的gcore工具。corerupt在其前身的基础上有了增强,提供了对ARM的支持,还支持对虚拟内存映射进行入侵式的操作,例如修改内存页面。
0.3.7 HFSleuth
HFSleuth是本书中的一个重要工具,这是一个用于查看OS X原生的文件系统HFS+支撑结构的多合一命令行工具。之所以开发这个工具,是因为确实没有什么其他方式能够展示这个非常复杂的文件系统的内部工作原理。Singh的书Mac OS X Internals: A Systems Approach 中也包含了一个类似的工具,称为hfsdebug,这个工具的功能少一些,而且只支持PowerPC,后来被一个商业工具fileXRay所替代。
为了在真实的文件系统上使用HFSleuth,必须能够读这个文件系统。一种方法就是引导这个文件系统。HFSleuth的功能几乎都是只读的,所以可以保证这个工具非常安全。但是访问文件系统所在的底层块设备(有时候是字符设备)的访问权限通常是rw-r-----,意味着设备不能被其他用户读访问。如果您通常情况下不信任root,并且坚持要使用较低的权限(这是一个明智的决定!),那么一个同等有效的替代方案是通过chmod(1)修改HFS+分区所在设备的权限,使得自己的用户有权限读(通常使用o+r参数)。一些高级功能(例如修复和HFS+/HFSX转换)要求写访问权限。HFSleuth可以从本书的支持网站免费下载,而且会一直免费。不过和其前任一样,这个工具也不是开源的。
0.3.8 lsock
netstat -o是一个大家都非常需要的功能,能够显示进程对系统中socket的所有权,但是这个功能在OS X中就没有了。在OS X中,这个功能要通过lsof(1)实现,但是后者需要剔除其他打开的文件将socket滤出来,所以比较麻烦。另一个缺失的功能是显示正在创建的socket连接,就像Windows的TCPMon提供的功能一样。第17章介绍的这个工具使用了一个没有正式文档的内核控制协议com.apple.network.statistics,这个协议可以获得socket创建时候的实时通知。这个工具特别容易集成到脚本中,因此可以很方便地用作连接事件处理程序。
0.3.9 jkextstat
本书中使用的最后一个工具是jkextstat,这是一个kextstat(8)兼容的工具,能够列出内核扩展。和最初的版本不一样,这个工具支持详细输出(verbose)模式,而且可以用于iOS。因此,这个工具对于iOS内核的探讨来说价值重大,而这件事在这本书出现之前是很困难的,因为iOS的二进制kextstat使用了不再被支持的API。这个工具在最初的版本上有了改进,允许更详细的输出信息,能关注于特定的内核扩展,而且还支持输出到XML格式。
0.4 本书使用的约定
为了使本书更容易阅读,避免经常反复强调示例代码和程序的具体背景,本书使用了一些约定,通过这些约定可以巧妙地提醒您给定代码清单的背景。
0.4.1 出场演员表
本书中的演示和代码清单自然是在各种不同版本的Apple电脑和i-设备上产生和测试的。根据系统管理员给设备命名的习惯,每一台主机都有自己的“个性”和名字。为了避免重复说明每一个演示是在哪一台设备和操作系统上运行的,书中保留了shell的命令行提示符,通过其中的主机名可以找到这个演示所在平台对应的OS X或iOS版本(详见表0-1)。
表0-1 本书演示程序所用的主机名和版本信息
主 机 名 设 备 类 型 操作系统版本 使 用 目 的
Ergo MacBook Air, 2010 Snow Leopard, 10.6.8 OS X的一般性特性演示。在Snow Leopard及更新的版本上测试
iPhonoclast iPhone 4S iOS 5.1.1 iOS 5及更新的版本在A5处理器(ARM的多核处理器)上的特性测试
Minion Mac Mini, 2010 Lion, 10.7.4 Lion相关的特性演示
Simulacrum VMWare镜像 Mountain Lion, 10.8.0 DP3 Mountain Lion(开发者预览版)相关的特性演示
Padishah iPad 2 iOS 4.3.3 iOS 4及更新的版本的特性
Podicum iPod Touch, 4G iOS 5.0.1 iOS 5相关的特性,在A4或A5处理器上
此外,带有root@的命令行提示符表示只能通过root用户运行的命令。通过这个标志很容易区分哪些示例能运行在哪些系统上,以及需要什么权限。
0.4.2 代码节选和示例
本书包含了大量代码示例,分为以下两类:
● 示例程序:主要出现在第Ⅰ部分。这些程序通常用于演示在用户态有效的简单概念和原理,或者演示一些特殊的API和库。这些示例程序都是由作者本人编写的,而且注释良好,您可以随意尝试这些程序,可以按照自己希望的方式修改,或者也可以不去碰这些程序。如果想要偷懒的话,还可以从本书的支持网站上下载这些程序的源代码和二进制程序。
● Darwin的代码节选:主要出现在第Ⅱ部分。这些代码几乎都是XNU代码的完整片段,截取自最新开源版本1699.26.8(对应于Lion 10.7.4)。所有代码都是开源的,但是要遵循Apple的Public Source License。本书提供代码节选的目的是展示XNU架构中的相关部分。由于自然语言容易产生一些歧义,而代码既不用依赖上下文又准确(遗憾的是,有时候可读性不是那么好),因此最准确的解释往往来自于对代码的阅读。当书中引用代码的时候,有可能指的是/usr/include目录下的头文件(通过标准的C语言< >记号表示,例如<mach/mach-o.h>)。有时候,也有可能指的是来自于XNU或相关软件包的Darwin的源代码。在这种情况下会使用相对路径(例如osfmk/kern/spl.c,指的是相对于XNU内核源代码解压的路径)。相关软件包总是会标注出来,不过书中第部Ⅱ分引用的代码几乎都是XNU内核的代码。
XNU和Darwin组件的代码都有很好的文档,不过本书尝试更进一步,在必要的时候在代码中以注释的形式添加额外的解释。为了区分,这种不属于原始代码的注释都是用C++风格的注释清晰地标注出来,而不是Darwin中使用的C风格的注释,例如下面这个代码清单示例:
代码清单0-1:示例代码清单
/* This is a Darwin comment, as it appears in the original source */
// This is an annotation provided by the author, elaborating or explaining
// something which the documentation may or may not leave wanting
// Where the source code is long and tedious, or just obvious, some parts may
// be omitted, and this is denoted by a comment marking ellipsis (...), i.e:
// ...
important parts of a listing or output may be shown in bold
本书区分“输出清单”和“代码清单”。代码清单是直接从程序源代码文件或系统配置文件中摘抄出来的。而输出清单则是用户命令运行捕获的文本,用于演示在OS X、iOS或二者上运行的结果。本书旨在比较和对比两个系统,因此常常会见到同样的命令序列在两个系统上的执行结果。在输出清单中,可以看到用户命令用粗体表示,我们鼓励读者仿照书中在自己的系统上实验这些命令。
通常情况下,书中提供代码清单的目的是为了阐述清楚问题,而不是让人感到更迷惑。自然语言带有一定的二义性,但是代码则只有一种解释方式(即使有时候这样的方式并不完全清晰明了)。只要有可能,书中会用清晰的描述辅以详尽的图示,期望能够帮助您快速理解代码。对C语言(有时候要求一点汇编语言)当然能够帮助阅读书中的代码示例,但是并不是必要的。代码中的注释——特别是额外的注解——可以帮助您理解代码中的要点。书中使用更多的是框图和流程图,把函数当做黑盒子。读者可以自己选择是停留在大致了解的层次,还是深入研究实现中具体的变量和函数。不过值得注意的是,代码本身非常复杂,是很多人和很多编码风格的产物,在整个XNU中可以看到各种风格迥异的代码。
在iOS方面,XNU仍然保持闭源。iOS版本使用的XNU版本实际上比公开发布的版本领先很多版本。因而,书中也无法展示相应的示例源代码,但有时候会给出一些反汇编的代码(主要来自于iOS 5.x)。这里使用的汇编是ARM汇编,代码中有一些帮助解释内部工作原理的注释(这些注释全部是由本书作者提供的)。对于和汇编相关的知识,可以参考本书的附录快速了解。
0.4.3 排版约定
本书约定命令后面括号中的数字表示这条命令所在man手册的节数(如果有的话)。例如:ls(1)表示一条用户命令,write(2)表示一个系统调用,printf(3)表示一个库函数调用,ipfw(8)表示一条系统管理命令。本书中描述的大部分命令和系统调用都在man手册中有很好的文档,本书也不打算替代精美手册的地位(因此,有问题请首先参阅手册)。然而,文档偶尔会遗漏一些内容——甚至根本没有听过文档记载——这时本书就会给出更多的信息。
0.5 支持网站
OS X和iOS都在高速地演进,而且会一直持续下去。我尽可能跟上演进的步伐,并且为本书更新维护了一个支持网站,地址是http://newosxbook.com。我的公司(http://technologeeks.com)也在LinkedIn上维护了OS X和iOS内核开发者小组(与那些Windows和Android的小组并列),它包含一个问答论坛,这个论坛有希望成为一个热门的OS X和iOS相关问题的讨论场所。
本书支持网站的内容包括:
● 一个列出了各种POSIX和Mach系统调用的附录。
● 本书中所有实验中包含的示例程序——让有热情但是懒得敲代码的读者尝试。这些程序不仅提供了源代码的形式,还提供了二进制文件(给那些甚至懒得编译或不想使用Xcode的读者)。
● 本书中介绍的(也就是在这个前言中讨论的)工具,可以免费下载使用OS X平台和iOS平台的二进制文件,有些还提供了源代码。
● 其他网络资源的更新引用和链接。
● 随着时间的推移,会有一些关于新特性和改进的更新文章。
● 勘误表——人都会犯错误——尤其是iOS中的错误,因为大部分和iOS有关的细节都是通过反汇编挖掘出来的,这些内容中可能有一些不准确的地方或版本的差异需要修正。
本书是一段不可思议的旅程,您将带着(玩小猫的时候用的)护目镜,慢慢地揭开支持用户态应用程序的真实脉络。我真切地希望读者能像我一样得到启示,了解的不仅是关于OS X和iOS的思想,还有整个操作系统架构和软件设计的基本思想。
翻开这本书,开始这段奇妙的旅程吧!
