操作系统概述
# 概述
现代计算机系统由一个或多个处理器、主存、磁盘、打印机、键盘、鼠标、显示器、网络接口以及各种其他输入/输出设备组成。一般而言,现代计算机系统是一个复杂的系统。如果每位应用程序员都不得不掌握系统所有的细节,那就不可能再编写代码了。而且,管理所有这些部件并加以优化使用,是一件挑战性极强的工作。所以,计算机安装了一层软件,称为操作系统,它的任务是为用户程序提供一个更好、更简单、更清晰的计算机模型,并管理刚才提到的所有这些设备。本书的主题就是操作系统。
多数读者都会对诸如Windows、Linux、FreeBSD或Mac OS X等某个操作系统有些体验,但表面现象是会骗人的。用户与之交互的程序,基于文本的通常称为shell,而基于图标的则称为图形用户界面(Graphical User Interface,GUI),它们实际上并不是操作系统的一部分,尽管这些程序使用操作系统来完成工作。
图1-1给出了在这里所讨论主要部件的一个简化视图。图的底部是硬件。硬件包括芯片、电路板、磁盘、键盘、显示器以及类似的设备。在硬件的顶部是软件。多数计算机有两种运行模式:内核态和用户态。软件中最基础的部分是操作系统,它运行在内核态(也称为管态、核心态)。在这个模式中,操作系统具有对所有硬件的完全访问权,可以执行机器能够运行的任何指令。软件的其余部分运行在用户态下。在用户态下,只使用了机器指令中的一个子集。特别地,那些会影响机器的控制或可进行I/O(输入/输出)操作的指令,在用户态中的程序里是禁止的。在本书中,我们会不断地讨论内核态和用户态之间的差别。
用户接口程序,shell或者GUI,处于用户态程序中的最低层次,允许用户运行其他程序,诸如Web浏览器、电子邮件阅读器或音乐播放器等。这些程序也大量使用操作系统。
操作系统所在的位置如图1-1所示。它运行在裸机之上,为所有其他软件提供基础的运行环境。

图 1-1 操作系统所处的位置
操作系统和普通软件(用户态)之间的主要区别是,如果用户不喜欢某个特定的电子邮件阅读器,他可以自由选择另一个,或者选择自己写一个,但是他不能自行写一个属于操作系统一部分的时钟中断处理程序。这个程序由硬件保护,防止用户试图对其进行修改。
然而,有时在嵌入式系统(该系统没有内核态)或解释系统(如基于Java的操作系统,它采用解释方式而非硬件方式区分组件)中,上述区别是模糊的。
另外,在许多系统中,一些在用户态下运行的程序协助操作系统完成特权功能。例如,经常有一个程序供用户修改其口令之用。但是这个程序不是操作系统的一部分,也不在内核态下运行,不过它明显地带有敏感的功能,并且必须以某种方式给予保护。在某些系统中,这种想法被推向了极致,一些传统上被认为是操作系统的部分(诸如文件系统)在用户空间中运行。在这类系统中,很难划分出一条明显的界限。在内核态中运行的当然是操作系统的一部分,但是一些在内核外运行的程序也有争议地被认为是操作系统的一部分,或者至少与操作系统密切相关。
操作系统与用户(即应用)程序的差异并不在于它们所处的地位。特别地,操作系统是大型、复杂和长寿命的程序。Linux或Windows操作系统的源代码有5百万行数量级。要理解这个数量的含义,请考虑具有5百万行的一套书,每页50行,每卷1000页(比本书厚)。为了以书的大小列出一个操作系统,需要有100卷书------基本上需要一整个书架来摆放。请设想一下有个维护操作系统的工作,第一天老板带你到装有代码的书架旁,说:"去读吧。"而这仅仅是运行在内核中的部分代码。用户程序,如GUI、库以及基本应用软件(类似于Windows Explorer)等,很容易就能达到这个代码数量的10倍或20倍之多。
至于为什么操作系统的寿命较长,读者现在应该清楚了------操作系统是很难编写的。一旦编写完成,操作系统的所有者当然不愿意把它扔掉,再写一个。相反,操作系统会在长时间内进行演化。基本上可以把Windows 95/98/Me看成是一个操作系统,而Windows NT/2000/XP/Vista则是另外一个操作系统。对于用户而言,它们看上去很相像,因为微软公司努力使Windows 2000/XP与被替代的系统,如Windows 98,两者的用户界面看起来十分相似。无论如何,微软公司要舍弃Windows 98是有非常正当的原因的,我们将在第11章涉及Windows细节时具体讨论这一内容。
贯穿本书的其他主要例子(除了Windows)还有UNIX,以及它的变体和克隆版。UNIX,当然也演化了多年,如System V版、Solaris以及FreeBSD等都是来源于UNIX的原始版;不过尽管Linux非常像依照UNIX模式而仿制,并且与UNIX高度兼容,但是Linux具有全新的代码基础。本书将采用来自UNIX中的示例,并在第10章中具体讨论Linux。
本章将简要叙述操作系统的若干重要部分,内容包括其含义、历史、分类、一些基本概念及其结构。在后面的章节中,我们将具体地讨论这些重要内容。
1.1 什么是操作系统
很难给出操作系统的准确定义。操作系统是一种运行在内核态的软件------尽管这个说法并不总是符合事实。部分原因是操作系统执行两个基本上独立的任务,为应用程序员(实际上是应用程序)提供一个资源集的清晰抽象,并管理这些硬件资源,而不仅仅是一堆硬件。另外,还取决于从什么角度看待操作系统。读者多半听说过其中一个或另一个的功能。下面我们逐项进行讨论。
# 1.1.1 作为扩展机器的操作系统
在机器语言一级上,多数计算机的体系结构(指令集、存储组织、I/O和总线结构)是很原始的,而且编程是很困难的,尤其是对输入/输出操作而言。要更细致地考察这一点,可以考虑如何用NEC PD765控制器芯片来进行软盘I/O操作,多数基于Intel的个人计算机中使用了该控制器兼容芯片。(在本书中,术语"软盘"和"磁盘"是可互换的。)我们之所以使用软盘作为例子,是因为它虽然已经很少见,但是与现代硬盘相比则简单得多。PD765有16条命令,每一条命令向一个设备寄存器装入长度从1字节到9字节的特定数据。这些命令用于读写数据、移动磁头臂、格式化磁道,以及初始化、检测状态、复位、校准控制器及设备等。
最基本的命令是read和write。它们均需要13个参数,所有这些参数封装在9个字节中。这些参数所指定的信息有:欲读取的磁盘块地址、磁道的扇区数、物理介质的记录格式、扇区间隙以及对已删除数据地址标识的处理方法等。如果读者不懂这些"故弄玄虚"的语言,请不要担心,因为这正是关键所在------它们太玄秘了。当操作结束时,控制器芯片在7个字节中返回23个状态及出错字段。这样似乎还不够,软盘程序员还要注意保持步进电机的开关状态。如果电机关闭着,则在读写数据前要先启动它(有一段较长的启动延迟时间)。而电机又不能长时间处于开启状态,否则软盘片就会被磨坏。程序员必须在较长的启动延迟和可能对软盘造成损坏(和丢失数据)之间做出权衡。
现在不用再叙述读操作的具体过程了,很清楚,一般程序员并不想涉足软盘(或硬盘,更复杂)编程的这些具体细节。相反,程序员需要的是一种简单的、高度抽象的处理。在磁盘的情况下,典型的抽象是包含了一组已命名文件的一个磁盘。每个文件可以打开进行读写操作,然后进行读写,最后关闭文件。诸如记录是否应该使用修正的调频记录方式,以及当前电机的状态等细节,不应该出现在提供给应用程序员的抽象描述中。
抽象是管理复杂性的一个关键。好的抽象可以把一个几乎不可能管理的任务划分为两个可管理的部分。其第一部分是有关抽象的定义和实现,第二部分是随时用这些抽象解决问题。几乎每个计算机用户都理解的一个抽象是文件。文件是一种有效的信息片段,诸如数码照片、保存的电子邮件信息或Web页面等。处理数码照片、电子邮件以及Web页面等,要比处理磁盘的细节容易,这些磁盘的具体细节与前面叙述过的软盘一样。操作系统的任务是创建好的抽象,并实现和管理它所创建的抽象对象。本书中,我们将研究许多关于抽象的内容,因为这是理解操作系统的关键。
上述观点是非常重要的,所以值得用不同的表述语句来再次叙述。怀着对设计Macintosh机器的工业设计师的尊重,作者这里不得不说,硬件是丑陋的。真实的处理器、内存条、磁盘和其他装置都是非常复杂的,对于那些为使用某个硬件而不得不编写软件的人们而言,他们使用的是困难、可怕、特殊和不一致的接口。有时这是由于需要兼容旧的硬件,有时是为了节省成本,但是,有时硬件设计师们并没有意识到(或在意)他们给软件设计带来了多大的麻烦。操作系统的一个主要任务是隐藏硬件,呈现给程序(以及程序员)良好、清晰、优雅、一致的抽象。如图1-2所示,操作系统将丑陋转变为美丽。

图 1-2 操作系统将丑陋的硬件转变为美丽的抽象
需要指出,操作系统的实际客户是应用程序(当然是通过应用程序员)。它们直接与操作系统及其抽象打交道。相反,最终用户与用户接口所提供的抽象打交道,或者是命令行shell或者是图形接口。而用户接口的抽象可以与操作系统提供的抽象类似,但也不总是这样。为了更清晰地说明这一点,请读者考虑普通的Windows桌面以及面向行的命令提示符。两者都是运行在Windows操作系统上的程序,并使用了Windows提供的抽象,但是它们提供了非常不同的用户接口。类似地,运行Gnome或者KDE的Linux用户与直接在X Window系统(面向文本)顶部工作的Linux用户看到的是非常不同的界面,但是在这两种情形中,操作系统下面的抽象是相同的。
在本书中,我们将具体讨论提供给应用程序的抽象,不过很少涉及用户界面。尽管用户界面是一个巨大和重要的课题,但是它们毕竟只和操作系统的外围相关。
# 1.1.2 作为资源管理者的操作系统
把操作系统看作是向应用程序提供基本抽象的概念,是一种自顶向下的观点。按照另一种自底向上的观点,操作系统则用来管理一个复杂系统的各个部分。现代计算机包含处理器、存储器、时钟、磁盘、鼠标、网络接口、打印机以及许多其他设备。从这个角度看,操作系统的任务是在相互竞争的程序之间有序地控制对处理器、存储器以及其他I/O接口设备的分配。
现代操作系统允许同时运行多道程序。假设在一台计算机上运行的三个程序试图同时在同一台打印机上输出计算结果,那么开始的几行可能是程序1的输出,接着几行是程序2的输出,然后又是程序3的输出等,最终结果将是一团糟。采用将打印结果送到磁盘上缓冲区的方法,操作系统可以把潜在的混乱有序化。在一个程序结束后,操作系统可以将暂存在磁盘上的文件送到打印机输出,同时其他程序可以继续产生更多的输出结果,很明显,这些程序的输出还没有真正送至打印机。
当一个计算机(或网络)有多个用户时,管理和保护存储器、I/O设备以及其他资源的需求变得强烈起来,因为用户间可能会互相干扰。另外,用户通常不仅共享硬件,还要共享信息(文件、数据库等)。简而言之,操作系统的这一种观点认为,操作系统的主要任务是记录哪个程序在使用什么资源,对资源请求进行分配,评估使用代价,并且为不同的程序和用户调解互相冲突的资源请求。
资源管理包括用以下两种不同方式实现多路复用(共享)资源:在时间上复用和在空间上复用。当一种资源在时间上复用时,不同的程序或用户轮流使用它。先是第一个获得资源的使用,然后下一个,以此类推。例如,若在系统中只有一个CPU,而多个程序需要在该CPU上运行,操作系统则首先把该CPU分配给某一个程序,在它运行了足够长的时间之后,另一个程序得到CPU,然后是下一个,如此进行下去,最终,轮到第一个程序再次运行。至于资源是如何实现时间复用的------谁应该是下一个以及运行多长时间等------则是操作系统的任务。还有一个有关时间复用的例子是打印机的共享。当多个打印作业在一台打印机上排队等待打印时,必须决定将轮到打印的是哪个作业。
另一类复用是空间复用。每个客户都得到资源的一部分,从而取代了客户排队。例如,通常在若干运行程序之间分割内存,这样每一个运行程序都可同时入住内存(例如,为了轮流使用CPU)。假设有足够的内存可以存放多个程序,那么在内存中同时存放若干个程序的效率,比把所有内存都分给一个程序的效率要高得多,特别是,如果一个程序只需要整个内存的一小部分时,结果更是这样。当然,如此的做法会引起公平、保护等问题,这有赖于操作系统解决它们。有关空间复用的其他资源还有磁盘。在许多系统中,一个磁盘同时为许多用户保存文件。分配磁盘空间并记录谁正在使用哪个磁盘块,是操作系统资源管理的典型任务。
1.2 操作系统的历史
操作系统已经存在许多年了。在下面的小节中,我们将简要地分析一些操作系统历史上的重要之处。操作系统与其所运行的计算机体系结构的联系非常密切。我们将分析连续几代的计算机,看看它们的操作系统是什么样的。把操作系统的分代映射到计算机的分代上有些粗糙,但是这样做确实有某些作用,否则还没有其他好办法能够说清楚操作系统的历史。
下面给出的有关操作系统的发展主要是按照时间线索叙述的,且在时间上是有重叠的。每个发展并不是等到先前一种发展完成后才开始。存在着大量的重叠,不用说还存在有不少虚假的开始和终结时间。请读者把这里的文字叙述看成是一种指引,而不是盖棺论定。