操作系统的作用是为用户提供应用软件的运行环境。用户运行应用程序时所执行的任务依赖于操作系统提供的服务,然而,操作系统执行任务时,许多情况下都无需用户甚至程序员的干预。例如,应用程序从硬盘读取文件时,程序员只需调用操作系统提供的函数即可,执行读取的特定步骤完全交由操作系统处理。程序员不用了解从计算机硬盘中读取文件与从外部USB闪存中读取文件有什么不同,这是操作系统关心的事情。 绝大多数程序员都开发过很多用户使用的代码,他们可能还会使用框架(如Cocoa)提供图形化用户界面与用户进行交互。Mac或iPhone应用商店提供的所有应用程序都属于这一类。本书讲述的不是应用软件开发,而是内核扩展开发,即编写为应用程序提供服务的代码。下列两种情况非常适合进行内核扩展:操作系统与自定义的硬件设备协同工作时,以及添加新文件系统的支持时。例如,经过内核扩展后,iTunes就可以使用新的USB音频设备,以太网卡就可以为网络应用程序提供接口,如图1-1所示。对文件系统进行内核扩展,就可以将在Windows计算机上格式化后的硬盘装载在Mac机上,使其看起来像一个标准的Mac驱动。 操作系统的一项重要功能就是管理计算机硬件资源(如内存与CPU)以及外部设备(如磁盘存储器与键盘)。操作系统所支持的硬件设备的集合在不同的机器上有很大差别。尽管MacBook Air与Mac Pro运行相同的操作系统,但它们的硬件配置却有很大不同。为了让操作系统支持多种硬件配置而又不变得膨胀,支持每个硬件组件的代码被分别打包成一种特殊类型的内核扩展程序(称为驱动程序)。这种模块化方式使操作系统可以根据系统上现有的硬件按需加载驱动程序。厂商因而也可以在系统中安装相应的驱动程序以支持他们自己的硬件。安装标准的Mac OS X系统需要装100多个驱动程序,但其中只有一个子集用于运行特定的系统。 内核扩展程序开发与应用程序开发有很大不同。应用程序一般由用户事件驱动执行。应用程序在用户启动它时开始运行,然后等待用户点击按钮或选择菜单项,再处理相应的请求。相反,内核扩展没有用户界面,无需与用户交互。它们由操作系统加载,并由操作系统调用去执行自身无法独立完成的任务(如访问由内核扩展驱动的硬件设备)。 为了提高系统的安全性和稳定性,现代操作系统(如Mac OS X)将操作系统核心代码(内核)与应用程序、用户运行的服务程序分离。任何作为内核的一部分运行的代码(如驱动代码),都要在“内核空间”中运行。运行在内核空间中的代码被授予特权,如可以直接读写连接到计算机上的硬件设备,但标准的用户应用程序不享有该特权。 相反,用户处理的标准应用程序代码要在“用户空间”中运行。用户空间中运行的软件无法直接访问硬件。因此,为了访问硬件,用户代码必须向内核发送请求(如读取磁盘的请求),让内核代表应用程序执行任务。 用户空间与内核空间中运行的代码之间具有明确的界限。应用程序只能通过调用操作系统发布给用户空间代码的函数访问内核。类似地,内核空间代码独立运行于用户空间代码环境之外。内核不使用与用户空间代码相同的富编程API,它有自己的API集,供内核扩展开发者使用。如果你熟悉用户空间编程,那么这些API最初看起来可能有一定的局限性。因为通常情况下,用户交互和访问文件系统等操作不能在开发内核扩展时使用。图1-2说明了用户空间代码和内核空间代码的划分,以及各层之间的交互关系。 强制应用程序向内核发送访问硬件的请求有一大优点,即内核(及内核驱动程序)成为硬件设备的中央仲裁器。以声卡为例,系统上可能有多个应用程序在同一时刻播放音频,但由于所有的请求只通过一个音频驱动传送,所以该驱动程序能将来自所有应用程序的音频流混合,然后将产生的混合流提供给声卡。 本章的剩余部分将概述操作系统内核的功能,着重讲述其为用户应用程序提供硬件访问的重要性。我们将从最上层的应用软件着手,然后深入到操作系统内核层,最后介绍底层的硬件驱动程序。如果你已经熟悉这些概念,可以直接跳到第2章继续阅读。