第1章 引 论 现代计算机系统由一个或多个处理器、主存、磁盘、打印机、键盘、鼠标、显示器、网络接口以及各种其他输入/输出设备组成。一般而言,现代计算机系统是一个复杂的系统。如果每位应用程序员都不得不掌握系统所有的细节,那就不可能再编写代码了。而且,管理所有这些部件并加以优化使用,是一件挑战性极强的工作。所以,计算机安装了一层软件,称为操作系统,它的任务是为用户程序提供一个更好、更简单、更清晰的计算机模型,并管理刚才提到的所有这些设备。本书的主题就是操作系统。 多数读者都会对诸如Windows、Linux、FreeBSD或Mac OS X等某个操作系统有些体验,但表面现象是会骗人的。用户与之交互的程序,基于文本的通常称为shell,而基于图标的则称为图形用户界面(Graphical User Interface,GUI),它们实际上并不是操作系统的一部分,尽管这些程序使用操作系统来完成工作。 图1-1给出了在这里所讨论主要部件的一个简化视图。图的底部是硬件。硬件包括芯片、电路板、磁盘、键盘、显示器以及类似的设备。在硬件的顶部是软件。多数计算机有两种运行模式:内核态和用户态。软件中最基础的部分是操作系统,它运行在内核态(也称为管态、核心态)。在这个模式中,操作系统具有对所有硬件的完全访问权,可以执行机器能够运行的任何指令。软件的其余部分运行在用户态下。在用户态下,只使用了机器指令中的一个子集。特别地,那些会影响机器的控制或可进行I/O(输入/输出)操作的指令,在用户态中的程序里是禁止的。在本书中,我们会不断地讨论内核态和用户态之间的差别。 用户接口程序,shell或者GUI,处于用户态程序中的最低层次,允许用户运行其他程序,诸如Web浏览器、电子邮件阅读器或音乐播放器等。这些程序也大量使用操作系统。 操作系统所在的位置如图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所示,操作系统将丑陋转变为美丽。 需要指出,操作系统的实际客户是应用程序(当然是通过应用程序员)。它们直接与操作系统及其抽象打交道。相反,最终用户与用户接口所提供的抽象打交道,或者是命令行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 操作系统的历史 操作系统已经存在许多年了。在下面的小节中,我们将简要地分析一些操作系统历史上的重要之处。操作系统与其所运行的计算机体系结构的联系非常密切。我们将分析连续几代的计算机,看看它们的操作系统是什么样的。把操作系统的分代映射到计算机的分代上有些粗糙,但是这样做确实有某些作用,否则还没有其他好办法能够说清楚操作系统的历史。 下面给出的有关操作系统的发展主要是按照时间线索叙述的,且在时间上是有重叠的。每个发展并不是等到先前一种发展完成后才开始。存在着大量的重叠,不用说还存在有不少虚假的开始和终结时间。请读者把这里的文字叙述看成是一种指引,而不是盖棺论定。 第一台真正的数字计算机是英国数学家Charles Babbage (1792—1871)设计的。尽管Babbage花费了他几乎一生的时间和财产,试图建造他的“分析机”,但是他始终未能让机器正常的运转,因为它是一台纯机械的数字计算机,他所在时代的技术不能生产出他所需要的高精度轮子、齿轮和轮牙。毫无疑问,这台分析机没有操作系统。 有一段有趣的历史花絮,Babbage认识到他的分析机需要软件,所以他雇佣了一个名为Ada Lovelace的年轻妇女,作为世界上第一个程序员,而她是着名的英国诗人Lord Byron的女儿。程序设计语言Ada则是以她命名的。 1.2.1 第一代(1945~1955):真空管和穿孔卡片 从Babbage 失败之后一直到第二次世界大战,数字计算机的建造几乎没有什么进展,第二次世界大战刺激了有关计算机研究的爆炸性开展。Iowa州立大学的John Atanasoff教授和他的学生Clifford Berry 建造了据认为是第一台可工作的数字计算机。该机器使用了300个真空管。大约在同时,Konrad Zuse在柏林用继电器构建了Z3计算机,英格兰布莱切利园的一个小组在1944年构建了Colossus,Howard Aiken在哈佛大学建造了Mark I,宾夕法尼亚大学的William Mauchley和他的学生J.Presper Eckert建造了ENIAC。这些机器有的是二进制的,有的使用真空管,有的是可编程的,但是都非常原始,甚至需要花费数秒时间才能完成最简单的运算。 在那个早期年代里,同一个小组的人(通常是工程师们)设计、建造、编程、操作并维护一台机器。所有的程序设计是用纯粹的机器语言编写的,甚至更糟糕,需要通过将上千根电缆接到插件板上连接成电路,以便控制机器的基本功能。没有程序设计语言(甚至汇编语言也没有),操作系统则从来没有听说过。使用机器的一般方式是,程序员在墙上的机时表上预约一段时间,然后到机房中将他的插件板接到计算机里,在接下来的几小时里,期盼正在运行中的两万多个真空管不会烧坏。那时,所有的计算问题实际都只是简单的数字运算,如制作正弦、余弦以及对数表等。 到了20世纪50年代早期有了改进,出现了穿孔卡片,这时就可以将程序写在卡片上,然后读入计算机而不用插件板,但其他过程则依然如旧。 1.2.2 第二代(1955~1965):晶体管和批处理系统 20世纪50年代晶体管的发明极大地改变了整个状况。计算机已经很可靠,厂商可以成批地生产并销售计算机给用户,用户可以指望计算机长时间运行,完成一些有用的工作。此时,设计人员、生产人员、操作人员、程序人员和维护人员之间第一次有了明确的分工。 这些机器,现在被称作大型机(mainframe),锁在有专用空调的房间中,由专业操作人员运行。只有少数大公司、重要的政府部门或大学才接受数百万美元的标价。要运行一个作业(job,即一个或一组程序),程序员首先将程序写在纸上(用FORTRAN语言或汇编语言),然后穿孔成卡片,再将卡片盒带到输入室,交给操作员,接着就喝咖啡直到输出完成。 计算机运行完当前的任务后,其计算结果从打印机上输出,操作员到打印机上撕下运算结果并送到输出室,程序员稍后就可取到结果。然后,操作员从已送到输入室的卡片盒中读入另一个任务。如果需要FORTRAN编译器,操作员还要从文件柜把它取来读入计算机。当操作员在机房里走来走去时许多机时被浪费掉了。 由于当时的计算机非常昂贵,人们很自然地要想办法减少机时的浪费。通常采用的解决方法就是批处理系统(batch system)。其思想是:在输入室收集全部的作业,然后用一台相对便宜的计算机,如IBM 1401计算机,将它们读到磁带上。IBM 1401计算机适用于读卡片、复制磁带和输出打印,但不适用于数值运算。另外用较昂贵的计算机,如IBM 7094来完成真正的计算。这些情况如图1-3所示。 图1-3 一种早期的批处理系统:a) 程序员将卡片拿到1401机处;b) 1401机将批处理作业读到磁带上;c) 操作员将输入带送至7094机;d) 7094机进行计算;e) 操作员将输出磁带送到1401机;f) 1401机打印输出 在收集了大约一个小时的批量作业之后,这些卡片被读进磁带,然后磁带被送到机房里并装到磁带机上。随后,操作员装入一个特殊的程序(现代操作系统的前身),它从磁带上读入第一个作业并运行,其输出写到第二盘磁带上,而不打印。每个作业结束后,操作系统自动地从磁带上读入下一个作业并运行。当一批作业完全结束后,操作员取下输入和输出磁带,将输入磁带换成下一批作业,并把输出磁带拿到一台1401机器上进行脱机(不与主计算机联机)打印。 典型的输入作业结构如图1-4所示。一开始是张$JOB卡片,它标识出所需的最大运行时间(以分钟为单位)、计费账号以及程序员的名字。接着是$FORTRAN卡片,通知操作系统从系统磁带上装入FORTRAN语言编译器。之后就是待编译的源程序,然后是$LOAD卡片,通知操作系统装入编译好的目标程序。接着是$RUN卡片,告诉操作系统运行该程序并使用随后的数据。最后,$END卡片标识作业结束。这些基本的控制卡片是现代shell和命令解释器的先驱。 图1-4 典型的FMS作业结构 第二代大型计算机主要用于科学与工程计算,例如,解偏微分方程。这些题目大多用FORTRAN语言和汇编语言编写。典型的操作系统是FMS(FORTRAN Monitor System,FORTRAN监控系统)和IBSYS(IBM为7094机配备的操作系统)。 1.2.3 第三代(1965~1980):集成电路芯片和多道程序设计 20世纪60年代初期,大多数计算机厂商都有两条不同并且完全不兼容的生产线。一条是面向字的、大型的科学用计算机,诸如IBM 7094,主要用于科学和工程计算。另一条是面向字符的、商用计算机,诸如IBM 1401,银行和保险公司主要用它从事磁带归档和打印服务。 开发和维护两种完全不同的产品,对厂商来说是昂贵的。另外,许多新的计算机用户一开始时只需要一台小计算机,后来可能又需要一台较大的计算机,而且希望能够更快地执行原有的程序。 IBM公司试图通过引入System/360来一次性地解决这两个问题。360是一个软件兼容的计算机系列,其低档机与1401相当,高档机则比7094功能强很多。这些计算机只在价格和性能(最大存储器容量、处理器速度、允许的I/O设备数量等)上有差异。由于所有的计算机都有相同的体系结构和指令集,因此,在理论上,为一种型号机器编写的程序可以在其他所有型号的机器上运行。而且360被设计成既可用于科学计算,又可用于商业计算,这样,一个系列的计算机便可以满足所有用户的要求。在随后的几年里,IBM使用更现代的技术陆续推出了360的后续机型,如着名的370、4300、3080和3090系列。zSeries是这个系列的最新机型,不过它与早期的机型相比变化非常之大。 360是第一个采用(小规模)芯片(集成电路)的主流机型,与采用分立晶体管制造的第二代计算机相比,其性能/价格比有很大提高。360很快就获得了成功,其他主要厂商也很快采纳了系列兼容机的思想。这些计算机的后代仍在大型的计算中心里使用。现在,这些计算机的后代经常用来管理大型数据库(如航班定票系统)或作为web 站点的服务器,这些服务器每秒必须处理数千次的请求。 “单一家族”思想的最大优点同时也是其最大的缺点。原因在于所有的软件,包括操作系统OS/360,要能够在所有机器上运行。从小的代替1401把卡片复制到磁带上的机器,到用于代替7094进行气象预报及其他繁重计算的大型机;从只能带很少外部设备的机器到有很多外设的机器;从商业领域到科学计算领域等。总之,它要有效地适用于所有这些不同的用途。 IBM(或其他公司)无法写出同时满足这些相互冲突需要的软件。其结果是一个庞大的又极其复杂的操作系统,它比FMS大了约2~3个数量级规模。其中包含数千名程序员写的数百万行汇编语言代码,也包含成千上万处错误,这就导致IBM不断地发行新的版本试图更正这些错误。每个新版本在修正老错误的同时又引入了新错误,所以随着时间的流逝,错误的数量可能大致保持不变。 OS/360的设计者之一Fred Brooks 后来写过一本既诙谐又尖锐的书(Brooks, 1996),描述他在开发OS/360过程中的经验。我们不可能在这里复述该书的全部内容,不过其封面已经充分表述了Fred Brooks的观点,一群史前动物陷入泥潭而不能自拔。Silberschatz等人着作(2005)的封面也表达了操作系统如同恐龙一般的类似观点。 抛开OS/360的庞大和存在的问题,OS/360和其他公司类似的第三代操作系统的确合理地满足了大多数用户的要求。同时,它们也使第二代操作系统所缺乏的几项关键技术得到了广泛应用。其中最重要的应该是多道程序设计(multiprogramming)。在7094机上,若当前作业因等待磁带或其他I/O操作而暂停时,CPU就只能简单地踏步直至该I/O完成。对于CPU操作密集的科学计算问题,I/O操作较少,因此浪费的时间很少。然而,对于商业数据处理,I/O操作等待的时间通常占到80%~90%,所以必须采取某种措施减少(昂贵的)CPU空闲时间的浪费。 解决方案是将内存分几个部分,每一部分存放不同的作业,如图1-5所示。当一个作业等待I/O操作完成时,另一个作业可以使用CPU。如果内存中可以同时存放足够多的作业,则CPU利用率可以接近100%。在内存中同时驻留多个作业需要特殊的硬件来对其进行保护,以避免作业的信息被窃取或受到攻击。360及其他第三代计算机都配有此类硬件。 第三代计算机的另一个特性是,卡片被拿到机房后能够很快地将作业从卡片读入磁盘。于是,任何时刻当一个作业运行结束时,操作系统就能将一个新作业从磁盘读出,装进空出来的内存区域运行。这种技术叫做同时的外部设备联机操作(Simultaneous Peripheral Operation On Line,SPOOLing ),该技术同时也用于输出。当采用了SPOOLing技术后,就不再需要IBM 1401机,也不必再将磁带搬来搬去了。 第三代操作系统很适于大型科学计算和繁忙的商务数据处理,但其实质上仍旧是批处理系统。许多程序员很怀念第一代计算机的使用方式。那时,他们可以几个小时地独占一台机器,可以即时地调试他们的程序。而对第三代计算机而言,从一个作业提交到运算结果取回往往长达数小时,更有甚者,一个逗号的误用就会导致编译失败,而可能浪费了程序员半天的时间。 程序员们的希望很快得到了响应,这种需求导致了分时系统(timesharing)的出现。它实际上是多道程序的一个变体,每个用户都有一个联机终端。在分时系统中,假设有20个用户登录,其中17个在思考、谈论或喝咖啡,则CPU可分配给其他三个需要的作业轮流执行。由于调试程序的用户常常只发出简短的命令(如编译一个五页的源文件),而很少有长的费时命令(如上百万条记录的文件排序),所以计算机能够为许多用户提供快速的交互式服务,同时在CPU空闲时还可能在后台运行一个大作业。第一个通用的分时系统,兼容分时系统(Compatible Time Sharing System,CTSS)是MIT(麻省理工学院)在一台改装过的7094机上开发成功的(Corbat?等人,1962年)。但直到第三代计算机广泛采用了必需的保护硬件之后,分时系统才逐渐流行开来。 在CTSS成功研制之后,MIT、贝尔实验室和通用电气公司(GE,当时一个主要的计算机制造厂商)决定开发一种“公用计算服务系统”,能够同时支持数百名分时用户的一种机器。它的模型借鉴了供电系统—当需要电能时,只需将电气设备接到墙上的插座即可,于是,在合理范围内,所需要的电能随时可提供。该系统称作MULTICS (MULTiplexed Information and Computing Service ),其设计者着眼于建造满足波士顿地区所有用户计算需求的一台机器。在当时看来,仅仅40年之后,就能成百万台地销售(价值不到1千美元)速度是GE-645主机10 000倍的计算机,完全是科学幻想。这种想法同现在关于穿越大西洋的超音速海底列车的想法一样,是幻想。 MULTICS得到一种混合式的成功。尽管这台机器具有较强的I/O能力,却要在一台仅仅比Intel 386 PC性能强一点的机器上支持数百个用户。可是这个想法并不像表面上那么荒唐,因为那时的人们已经知道如何编写精练的高效程序,而这种技巧随后逐渐丢失了。有许多原因造成MULTICS没有能够普及到全世界,至少它不应该采用PL/1编写,因为PL/1编译器推迟了好几年才完成,好不容易完成的编译器又极少能够成功运行。另外,当时的MULTICS有太大的野心,犹如19世纪中期Charles Babbage的分析机。 简要地说,MULTICS在计算机文献中播撒了许多原创的概念,但要将其造成一台真正的机器并想实现商业上的巨大成功的研制难度超出了所有人的预料。贝尔实验室退出了,通用电气公司也退出了计算机领域。但是M.I.T.坚持下来并且最终使MULTICS成功运行。MULTICS最后成为商业产品,由购买了通用电气公司计算机业务的公司(Honeywell)销售,并安装在世界各地80多个大型公司和大学中。尽管MULTICS的数量很小,但是MULTICS的用户们却非常忠诚,例如,通用汽车、福特和美国国家安全局直到20世纪90年代后期,在试图让Honeywell更新其硬件多年之后,才关闭了他们的MULTICS系统,而这已经是在MULTICS推出之后30年了。 目前,计算服务的概念已经被遗弃,但是这个概念是可以回归的,以大量的、附有相对简单用户机器的、集中式Internet服务器形式回归。在这种形式中,主要工作在大型服务器上完成。而回归的动机可能是多数人不愿意管理日益过分复杂的计算机系统,宁可让那些运行服务器公司的专业团队去做。电子商务已经向这个方向演化了,各种公司在多处理器的服务器上经营各自的电子商场,简单的客户端连接着多处理器服务器,这同MULTICS的设计精神非常类似。 尽管MULTICS在商业上失败了,但MULTICS对随后的操作系统却有着巨大的影响,详情请参阅有关文献和书籍(Corbat椀热耍?972;Corbat椇蚔yssotsky,1965;Daley和Dennis,1968;Organick,1972;Saltzer,1974)。还有一个曾经(现在仍然)活跃的Web站点www.multicians.org,上面有大量关于系统、设计人员以及其用户的信息资料。 另一个第三代计算机的主要进展是小型机的崛起,以1961年DEC的PDP-1作为起点。PDP-1计算机只有4K个18位的内存,每台售价120 000美元(不到IBM 7094的5%),该机型非常热销。对于某些非数值的计算,它和7094几乎一样快。PDP-1开辟了一个全新的产业。很快有了一系列PDP机型(与IBM系列机不同,它们互不兼容),其顶峰为PDP-11。 一位曾参加过MULTICS研制的贝尔实验室计算机科学家Ken Thompson,后来找到一台无人使用的PDP-7机器,并开始开发一个简化的、单用户版MULTICS。他的工作后来导致了UNIX操作系统的诞生。接着,UNIX在学术界,政府部门以及许多公司中流行。 有关UNIX的历史到处可以找到(例如Salus,1994)。这段故事的部分放在第10章中介绍。现在,有充分理由认为,由于到处可以得到源代码,各种机构发展了自己的(不兼容)版本,从而导致了混乱。UNIX有两个主要的版本,源自AT&T的System V,以及源自加州伯克利大学的BSD (Berkeley Software Distribution)。当然还有一些小的变种。为了使编写的程序能够在任何版本的UNIX上运行,IEEE提出了一个UNIX的标准,称作POSIX,目前大多数UNIX版本都支持它。POSIX定义了一个凡是UNIX必须支持的小型系统调用接口。事实上,某些其他操作系统也支持POSIX接口。 顺便值得一提的是,在1987年,本书作者发布了一个UNIX的小型克隆,称为MINIX,用于教学目的。在功能上,MINIX非常类似于UNIX,包括对POSIX的支持。从那时以后,MINIX的原始版本已经演化为MINIX 3,该系统是高度模块化的,并专注于高可靠性。它具有快速检测和替代有故障甚至已崩溃模块(如I/O设备驱动器)的能力,不用重启也不会干扰运行着的程序。有一本叙述其内部操作,并在附录中列出源代码的书(Tanenbaum和Woodhull,2006),该书现在仍然有售。在因特网的地址www. minix3.org 上,MINIX3是免费使用的(包括了所有源代码)。 对UNIX 版本免费产品(不同于教育目的)的愿望,导致芬兰学生Linus Torvalds编写了Linux。这个系统直接受到在MINIX开发的启示,而且原本支持各种MINIX的功能(例如MINIX文件系统)。尽管它已经通过多种方式扩展,但是该系统仍然保留了某些与MINIX和UNIX共同的低层结构。对Linux和开放源码运动具体历史感兴趣的读者可以阅读Glyn Moody的书籍(2001)。本书所叙述的有关UNIX的多数内容,也适用于System V、MINIX、Linux以及UNIX 的其他版本和克隆。 1.2.4 第四代(1980年至今):个人计算机 随着LSI (大规模集成电路) 的发展,在每平方厘米的硅片芯片上可以集成数千个晶体管,个人计算机时代到来了。从体系结构上看,个人计算机(最早称为微型计算机)与PDP-11并无二致,但就价格而言却相去甚远。以往,公司的一个部门或大学里的一个院系才配备一台小型机,而微处理器却使每个人都能拥有自己的计算机。 1974年,当Intel 8080,第一代通用8位CPU出现时,Intel希望有一个用于8080的操作系统,部分是为了测试目的。Intel请求其顾问Gary Kildall编写。Kildall和一位朋友首先为新推出的Shugart Associates 8英寸软盘构造了一个控制器,并把这个软磁盘同8080 相连,从而制造了第一个配有磁盘的微型计算机。然后Kildall为它写了一个基于磁盘的操作系统,称为CP/M(Control Program for Microcomputer)。由于Intel不认为基于磁盘的微型计算机有什么未来前景,所以当Kildall要求CP/M的版权时,Intel同意了他的要求。Kildall于是组建了一家公司Digital Research,进一步开发和销售CP/M。 1977年,Digital Research重写了CP/M,使其可以在使用8080、Zilog Z80以及其他CPU芯片的多种微型计算机上运行,从而使得CP/M完全控制了微型计算机世界达5年之久。 在20世纪80年代的早期,IBM设计了IBM PC 并寻找可在上面运行的软件。来自IBM的人员同Bill Gates联系有关他的BASIC解释器的许可证事宜,他们也询问是否他知道可在PC 机上运行的操作系统。Gates建议IBM同Digital Research联系,即当时世界上主宰操作系统的公司。在做出毫无疑问是近代历史上最糟的商业决策后,Kildall拒绝与IBM会见,代替他的是一位次要人员。为了使事情更糟糕,他的律师甚至拒绝签署IBM的有关尚未公开的PC 的保密协议。结果,IBM回头询问Gates可否提供他们一个操作系统。 在IBM返回时,Gates了解到一家本地计算机制造商,Seattle Computer Products,有合适的操作系统DOS(Disk Operating System)。他联系对方并提出购买(宣称75 000美元),对方接受了。然后Gates提供给IBM 成套的DOS/BASIC,IBM也接受了。IBM希望做某些修改,于是Gates雇佣了那个写DOS的作者,Tim Paterson,作为Gates的微软公司早期的一个雇员,并开展工作。修改版称为MS-DOS(MicroSoft Disk Operating System),并且很快主导了IBM PC市场。同Kildall试图将CP/M每次卖给用户一个产品相比(至少开始是这样),这里一个关键因素是Gates(回顾起来,极其聪明)的决策,将MS-DOS与计算机公司的硬件捆绑在一起出售。在所有这一切烟消云散之后,Kildall突然不幸去世,其原因从来没有公布过。 1983年,IBM PC后续机型IBM PC/AT推出,配有Intel 80286 CPU。此时,MS-DOS已经确立了地位,而CP/M只剩下最后的支撑。MS-DOS后来在80386和80486 中得到广泛的应用。尽管MS-DOS的早期版本是相当原始的,但是后期的版本提供了更多的先进功能,包括许多源自UNIX的功能。(微软对UNIX是如此娴熟,甚至在公司的早期销售过一个微型计算机版本,称为XENIX)。 用于早期微型计算机的CP/M、MS-DOS和其他操作系统,都是通过键盘输入命令的。由于Doug Engelbart于20世纪60年代在斯坦福研究院(Stanford Research Institute)工作,这种情况最终有了改变。Doug Engelbart发明了图形用户界面,包括窗口、图标、菜单以及鼠标。这些思想被Xerox PARC的研究人员采用,并用在了他们所研制的机器中。 一天, Steve Jobs(和其他人一起在汽车库里发明了苹果计算机)访问PARC, Jobs一看到GUI,立即意识到它的潜在价值,而Xerox管理层恰好没有认识到。这种战略失误的庞大比例,导致名为《摸索未来》一书的出版(Smith 与Alexander,1988年)。Jobs随后着手设计了带有GUI的苹果计算机。这个项目导致了Lisa的推出,但是Lisa过于昂贵,所以它在商业上失败了。Jobs的第二次尝试,即苹果Macintosh,取得了巨大的成功,这不仅是因为它比Lisa便宜得多,而且它还是对用户友好的(user friendly),也就是说,它是为那些不仅没有计算机知识,而且也根本不打算学习计算机的用户们准备的。在图像设计、专业数码摄影,以及专业数字视频生产的创意世界里,Macintosh得到广泛的应用,这些用户对苹果公司及Macintosh有着极大的热情。 在微软决定构建MS-DOS的后继产品时,受到了Macintosh成功的巨大影响。微软开发了名为Windows的基于GUI的系统,早期它运行在MS-DOS上层(它更像 shell而不像真正的操作系统)。在从1985年至1995年的10年之间,Windows只是在MS-DOS上层的一个图形环境。然而,到了1995年,一个独立的Windows版本,具有许多操作系统功能的Windows 95发布了。Windows 95仅仅把底层的MS-DOS作为启动和运行老的MS-DOS程序之用。1998年,一个稍做修改的系统,Windows 98发布。不过Windows 95和Windows 98仍然使用了大量16位Intel 汇编语言。 另一个微软操作系统是Windows NT(NT表示新技术),它在一定的范围内同Windows 95兼容,但是内部是完全新编写的。它是一个32位系统。Windows NT的首席设计师是David Cutler,他也是VAX VMS 操作系统的设计师之一,所以有些VMS的概念用在了NT上。事实上,NT中有太多的来自VMS的思想,所以VMS的所有者DEC公司控告了微软公司。法院对该案件判决的结果引出了一大笔需要用多位数字表达的金钱。微软公司期待NT的第一个版本可以消灭MS-DOS和其他的Windows版本,因为NT 是一个巨大的超级系统,但是这个想法失败了。只有Windows NT 4.0踏上了成功之路,特别在企业网络方面取得了成功。1999年初,Windows NT 5.0改名为Windows 2000。微软期望它成为Windows 98和Windows NT 4.0的接替者。 不过这两个方面都不太成功,于是微软公司发布了Windows 98的另一个版本,名为Windows Me (千年版)。2001年,发布了Windows 2000的一个稍加升级的版本,称为Windows XP。这个版本的寿命比较长(6年),基本上替代了Windows所有原先版本。在2007年1月,微软公司发布了Windows XP的后继版,名为Vista。它有一个新的图形接口Aero,以及许多其他新的或升级的用户程序。微软公司希望Vista能够完全替代XP,但是这个过程可能需要将近十年的时间。 在个人计算机世界中,另一个主要竞争者是UNIX(和它的各种变体)。UNIX在网络和企业服务器等领域强大,在台式计算机上,特别是在诸如印度和中国这些发展中国家里,UNIX的使用也在增加。在基于Pentium的计算机上,Linux 成为学生和不断增加的企业用户们代替Windows的通行选择。顺便提及,在本书中,我们使用“Pentium”这个名词代表Pentium I,II,III和4,以及它们的后继者,诸如Core 2 Duo等。术语x86有时仍旧用来表示Intel公司的包括8086的CPU,而“Pentium”则用于表示从Pentium I开始的所有CPU。很显然,这个术语并不完美,但是没有更好的方案。人们很奇怪,是Intel公司的哪个天才把半个世界都知晓和尊重的品牌名(Pentium)扔掉,并替代以“ Core 2 Duo”这样一个几乎没有人立即理解的术语—“2”是什么意思,而“Duo”又是什么意思?也许“Pentium 5”(或者“Pentium 5 dual core”)太难于记忆吧。至于FreeBSD,一个源自于Berkeley的 BSD项目,也是一个流行的UNIX变体。所有现代Macintosh计算机都运行着FreeBSD的一个修改版。在使用高性能RISC芯片的工作站上,诸如Hewlett-Packard 公司和Sun Microsystems公司销售的那些机器上,UNIX系统也是一种标准配置。 尽管许多UNIX用户,特别是富有经验的程序员们更偏好基于命令的界面而不是GUI,但是几乎所有的UNIX系统都支持由MIT开发的称为X Windows 的视窗系统(如众所周知的X11)。这个系统处理基本的视窗管理功能,允许用户通过鼠标创建、删除、移动和变比视窗。对于那些希望有图形系统的UNIX用户,通常在X 11之上还提供一个完整的GUI,诸如Gnome或KDE,从而使得UNIX在外观和感觉上类似于Macintosh或Microsoft Windows。 另一个开始于20世纪80年代中期的有趣发展是,那些运行网络操作系统和分布式操作系统(Tanenbaum和Van Steen,2007)的个人计算机网络的增长。在网络操作系统中,用户知道多台计算机的存在,用户能够登录到一台远地机器上并将文件从一台机器复制到另一台机器,每台计算机都运行自己本地的操作系统,并有自己的本地用户(或多个用户)。 网络操作系统与单处理器的操作系统没有本质区别。很明显,它们需要一个网络接口控制器以及一些低层软件来驱动它,同时还需要一些程序来进行远程登录和远程文件访问,但这些附加成分并未改变操作系统的本质结构。 相反,分布式操作系统是以一种传统单处理器操作系统的形式出现在用户面前的,尽管它实际上是由多处理器组成的。用户应该不知晓他们的程序在何处运行或者他们的文件存放于何处,这些应该由操作系统自动和有效地处理。 真正的分布式操作系统不仅仅是在单机操作系统上增添一小段代码,因为分布式系统与集中式系统有本质的区别。例如,分布式系统通常允许一个应用在多台处理器上同时运行,因此,需要更复杂的处理器调度算法来获得最大的并行度优化。 网络中的通信延迟往往导致分布式算法必须能适应信息不完备、信息过时甚至信息不正确的环境。这与单机系统完全不同,对于后者,操作系统掌握着整个系统的完备信息。 1.3 计算机硬件介绍 操作系统与运行该操作系统的计算机硬件联系密切。操作系统扩展了计算机指令集并管理计算机的资源。为了能够工作,操作系统必须了解大量的硬件,至少需要了解硬件如何面对程序员。出于这个原因,这里我们先简要地介绍现代个人计算机中的计算机硬件,然后开始讨论操作系统的具体工作细节。 从概念上讲,一台简单的个人计算机可以抽象为类似于图1-6中的模型。CPU、内存以及I/O设备都由一条系统总线连接起来并通过总线与其他设备通信。现代个人计算机结构更加复杂,包含多重总线,我们将在后面讨论之。目前,这一模式还是够用的。在下面各小节中,我们将简要地介绍这些部件,并且讨论一些操作系统设计师们所考虑的硬件问题。毫无疑问,这是一个非常简要的概括介绍。现在有不少讨论计算机硬件和计算机组织的书籍。其中两本有名的书的作者分别是Tanenbaum(2006)和Patterson与Hennessy(2004)。 图1-6 简单个人计算机中的一些部件 1.3.1 处理器 计算机的“大脑”是CPU,它从内存中取出指令并执行之。在每个CPU基本周期中,首先从内存中取出指令,解码以确定其类型和操作数,接着执行之,然后取指、解码并执行下一条指令。按照这一方式,程序被执行完成。 每个CPU都有其一套可执行的专门指令集。所以,Pentium不能执行SPARC程序,而SPARC也不能执行Pentium程序。由于用来访问内存以得到指令或数据的时间要比执行指令花费的时间长得多,因此,所有的CPU内都有一些用来保存关键变量和临时数据的寄存器。这样,通常在指令集中提供一些指令,用以将一个字从内存调入寄存器,以及将一个字从寄存器存入内存。其他的指令可以把来自寄存器、内存的操作数组合,或者用两者产生一个结果,诸如将两个字相加并把结果存在寄存器或内存中。 除了用来保存变量和临时结果的通用寄存器之外,多数计算机还有一些对程序员可见的专门寄存器。其中之一是程序计数器,它保存了将要取出的下一条指令的内存地址。在指令取出之后,程序计数器就被更新以便指向后继的指令。 另一个寄存器是堆栈指针,它指向内存中当前栈的顶端。该栈含有已经进入但是还没有退出的每个过程的一个框架。在一个过程的堆栈框架中保存了有关的输入参数、局部变量以及那些没有保存在寄存器中的临时变量。 当然还有程序状态字(Program Status Word,PSW)寄存器。这个寄存器包含了条件码位(由比较指令设置)、CPU优先级、模式(用户态或内核态),以及各种其他控制位。用户程序通常读入整个PSW,但是,只对其中的少量字段写入。在系统调用和I/O中,PSW的作用很重要。 操作系统必须知晓所有的寄存器。在时间多路复用(time multiplexing)CPU中,操作系统经常会中止正在运行的某个程序并启动(或再启动)另一个程序。每次停止一个运行着的程序时,操作系统必须保存所有的寄存器,这样在稍后该程序被再次运行时,可以把这些寄存器重新装入。 为了改善性能,CPU设计师早就放弃了同时读取、解码和执行一条指令的简单模型。许多现代CPU具有同时取出多条指令的机制。例如,一个CPU可以有分开的取指单元、解码单元和执行单元,于是当它执行指令n时,它还可以对指令n + 1解码,并且读取指令n + 2。这样一种机制称为流水线(pipeline),在图1-7a中是一个有着三个阶段的流水线示意图。更长的流水线也是常见的。在多数的流水线设计中,一旦一条指令被取进流水线中,它就必须被执行完毕,即便前一条取出的指令是条件转移,它也必须被执行完毕。流水线使得编译器和操作系统的编写者很头疼,因为它造成了在机器中实现这些软件的复杂性问题。 图1-7 a) 有三个阶段的流水线;b) 一个超标量CPU 比流水线更先进的设计是一种超标量CPU,如图1-7b所示。在这种设计中,有多个执行单元,例如,一个CPU用于整数算术运算,一个CPU用于浮点算术运算,而另一个用于布尔运算。两个或更多的指令被同时取出、解码并装入一个保持缓冲区中,直至它们执行完毕。只要有一个执行单元空闲,就检查保持缓冲区中是否还有可处理的指令,如果有,就把指令从缓冲区中移出并执行之。这种设计存在一种隐含的作用,即程序的指令经常不按顺序执行。在多数情况下,硬件负责保证这种运算的结果与顺序执行指令时的结果相同,但是,仍然有部分令人烦恼的复杂情形被强加给操作系统处理,我们在后面会讨论这种情况。 除了用在嵌入式系统中的非常简单的CPU之外,多数CPU都有两种模式,即前面已经提及的内核态和用户态。通常,在PSW中有一个二进制位控制这两种模式。当在内核态运行时,CPU可以执行指令集中的每一条指令,并且使用硬件的每种功能。操作系统在内核态下运行,从而可以访问整个硬件。 相反,用户程序在用户态下运行,仅允许执行整个指令集的一个子集和访问所有功能的一个子集。一般而言,在用户态中有关I/O和内存保护的所有指令是禁止的。当然,将PSW中的模式位设置成内核态也是禁止的。 为了从操作系统中获得服务,用户程序必须使用系统调用(system call)系统调用陷入内核并调用操作系统。TRAP指令把用户态切换成内核态,并启用操作系统。当有关工作完成之后,在系统调用后面的指令把控制权返回给用户程序。在本章的后面我们将具体解释系统调用过程,但是在这里,请读者把它看成是一个特别的过程调用指令,该指令具有从用户态切换到内核态的特别能力。作为排印上的说明,我们在行文中使用小写的Helvetica字体,表示系统调用,比如read。 有必要指出,计算机使用陷阱而不是一条指令来执行系统调用。其他的多数陷阱是由硬件引起的,用于警告有异常情况发生,诸如试图被零除或浮点下溢等。在所有的情况下,操作系统都得到控制权并决定如何处理异常情况。有时,由于出错的原因程序不得不停止。在其他情况下可以忽略出错(如下溢数可以被置为零)。最后,若程序已经提前宣布它希望处理某类条件时,那么控制权还必须返回给该程序,让其处理相关的问题。 多线程和多核芯片 Moore 定律指出,芯片中晶体管的数量每18个月翻一番。这个“定律”并不是物理学上的某种规律,诸如动量守恒定律等,它是 Intel公司的共同创始人Gordon Moore对半导体公司如何能快速缩小晶体管能力上的一个观察结果。Moore 定律已经保持了30年,有希望至少再保持10年。 使用大量的晶体管引发了一个问题:如何处理它们呢?这里我们可以看到一种处理方式:具有多个功能部件的超标量体系结构。但是,随着晶体管数量的增加,再多晶体管也是可能的。一件由此而来的必然结果是,在CPU 芯片中加入了更大的缓存,人们肯定会这样做,然而,原先获得的有用效果将最终消失掉。 显然,下一步不仅是有多个功能部件,某些控制逻辑也会出现多个。Pentium 4和其他一些CPU芯片就是这样做的,称为多线程(multithreading)或超线程(hyperthreading,这是Intel公司给出的名称)。近似地说,多线程允许CPU保持两个不同的线程状态,然后在纳秒级的时间尺度内来回切换。(线程是一种轻量级进程,也即一个运行中的程序。我们将在第2章中具体讨论)。例如,如果某个进程需要从内存中读出一个字(需要花费多个时钟周期),多线程CPU则可以切换至另一个线程。多线程不提供真正的并行处理。在一个时刻只有一个进程在运行,但是线程的切换时间则减少到纳秒数量级。 多线程对操作系统而言是有意义的,因为每个线程在操作系统看来就像是单个的CPU。考虑一个实际有两个CPU的系统,每个CPU有两个线程。这样操作系统将把它看成是4个CPU。如果在某个时间的特定点上,只有能够维持两个CPU忙碌的工作量,那么在同一个CPU上调度两个线程,而让另一个CPU完全空转,就没有优势了。这种选择远远不如在每个CPU上运行一个线程的效率高。Pentium 4的后继者,Core(还有Core 2)的体系结构并不支持超线程,但是Intel公司已经宣布,Core的后继者会具有超线程能力。 除了多线程,还出现了包含2个或4个完整处理器或内核的CPU芯片。图1-8中的多核芯片上有效地装有4个小芯片,每个小芯片都是一个独立的CPU。(后面将解释缓存。)要使用这类多核芯片肯定需要多处理器操作系统。 1.3.2 存储器 在任何一种计算机中的第二种主要部件都是存储器。在理想情形下,存储器应该极为迅速(快于执行一条指令,这样CPU不会受到存储器的限制),充分大,并且非常便宜。但是目前的技术无法同时满足这三个目标,于是出现了不同的处理方式。存储器系统采用一种分层次的结构,如图1-9所示。顶层的存储器速度较高,容量较小,与底层的存储器相比每位成本较高,其差别往往是十亿数量级。 存储器系统的顶层是CPU中的寄存器。它们用与CPU相同的材料制成,所以和CPU一样快。显然,访问它们是没有时延的。其典型的存储容量是,在32位CPU中为32×32 位,而在64位CPU中为64×64 位。在这两种情形下,其存储容量都小于1 KB。程序必须在软件中自行管理这些寄存器(即决定如何使用它们)。 下一层是高速缓存,它多数由硬件控制。主存被分割成高速缓存行(cache line),其典型大小为64个字节,地址0至63对应高速缓存行0,地址64至127对应高速缓存行1,以此类推。最常用的高速缓存行放置在CPU内部或者非常接近CPU的高速缓存中。当某个程序需要读一个存储字时,高速缓存硬件检查所需要的高速缓存行是否在高速缓存中。如果是,称为高速缓存命中,缓存满足了请求,就不需要通过总线把访问请求送往主存。高速缓存命中通常需要两个时钟周期。高速缓存未命中就必须访问内存,这要付出大量的时间代价。由于高速缓存的价格昂贵,所以其大小有限。有些机器具有两级甚至三级高速缓存,每一级高速缓存比前一级慢且容量更大。 缓存在计算机科学的许多领域中起着重要的作用,并不仅仅只是RAM的缓存行。只要存在大量的资源可以划分为小的部分,那么,这些资源中的某些部分就会比其他部分更频繁地得到使用,通常缓存的使用会带来性能上的改善。操作系统一直在使用缓存。例如,多数操作系统在内存中保留频繁使用的文件(的一部分),以避免从磁盘中重复地调取这些文件。相似地,类似于 /home/ast/projects/minix3/src/kernel/clock.c 的长路径名转换成文件所在的磁盘地址的结果,也可以放入缓存,以避免重复寻找地址。还有,当一个Web 页面(URL)的地址转换为网络地址(IP地址)后,这个转换结果也可以缓存起来以供将来使用。还有许多其他的类似的应用。 在任何缓存系统中,都有若干需要尽快考虑的问题,包括: 1) 何时把一个新的内容放入缓存。 2) 把新内容放在缓存的哪一行上。 3) 在需要时,应该把哪个内容从缓存中移走。 4) 应该把新移走的内容放在某个较大存储器的何处。 并不是每个问题的解决方案都符合每种缓存处理。对于CPU缓存中的主存缓存行,每当有缓存未命中时,就会调入新的内容。通常通过所引用内存地址的高位计算应该使用的缓存行。例如,对于64字节的4096缓存行,以及32位地址,其中6~17位用来定位缓存行,而0~5位则用来确定缓存行中的字节。在这个例子中,被移走内容的位置就是新数据要进入的位置,但是在有的系统中未必是这样。最后,当将一个缓存行的内容重写进主存时(该内容被缓存后,可能会被修改),通过该地址来惟一确定需重写的主存位置。 缓存是一种好方法,所以现代CPU 中设计了两个缓存。第一级或称为L1缓存总是在CPU中,通常用来将已解码的指令调入CPU的执行引擎。对于那些频繁使用的数据字,多数芯片安排有第二个L1缓存。典型的L1缓存大小为16KB。另外,往往还设计有二级缓存,称为L2缓存,用来存放近来所使用过若干兆字节的内存字。L1和L2缓存之间的差别在于时序。对L1缓存的访问,不存在任何延时;而对L2缓存的访问,则会延时1或2个时钟周期。 在多核芯片中。设计师必须确定缓存的位置。在图1-8a中,一个L2缓存被所有的核共享。Intel多核芯片采用了这个方法。相反,在图1-8b中,每个核有其自己的L2缓存。AMD采用这个方法。不过每种策略都有自己的优缺点。例如,Intel的共享L2缓存需要有一种更复杂的缓存控制器,而AMD的方式在设法保持L2缓存一致性上存在困难。 在图1-9的层次结构中,再往下一层是主存。这是存储器系统的主力。主存通常称为随机访问存储器(Random Access Memory,RAM)。过去有时称之为磁芯存储器,因为在20世纪50年代和60年代,使用很小的可磁化的铁磁体制作主存。目前,存储器的容量在几百兆字节到若干吉字节之间,并且其容量正在迅速增长。所有不能在高速缓存中得到满足的访问请求都会转往主存。 除了主存之外,许多计算机已经在使用少量的非易失性随机访问存储器。它们与RAM不同,在电源切断之后,非易失性随机访问存储器并不丢失其内容。只读存储器(Read Only Memory,ROM)在工厂中就被编程完毕,然后再也不能被修改。ROM速度快且便宜。在有些计算机中,用于启动计算机的引导加载模块就存放在ROM中。另外,一些I/O卡也采用ROM处理底层设备控制。 EEPROM(Electrically Erasable PROM,电可擦除可编程ROM)和闪存(flash memory)也是非易失性的,但是与ROM相反,它们可以擦除和重写。不过重写它们需要比写入RAM更高数量级的时间,所以它们的使用方式与ROM相同,而其与众不同的特点使它们有可能通过字段重写的方式纠正所保存程序中的错误。 在便携式电子设备中,闪存通常作为存储媒介。闪存是数码相机中的胶卷,是便携式音乐播放器的磁盘,这仅仅是闪存用途中的两项。闪存在速度上介于RAM和磁盘之间。另外,与磁盘存储器不同,如果闪存擦除的次数过多,就被磨损了。 还有一类存储器是CMOS,它是易失性的。许多计算机利用CMOS存储器保持当前时间和日期。CMOS存储器和递增时间的时钟电路由一块小电池驱动,所以,即使计算机没有上电,时间也仍然可以正确地更新。CMOS存储器还可以保存配置参数,诸如,哪一个是启动磁盘等。之所以采用CMOS是因为它消耗的电能非常少,一块工厂原装的电池往往就能使用若干年。但是,当电池开始失效时,计算机就会出现“Alzheimer病症”计算机会忘记掉记忆多年的事物,比如应该由哪个磁盘启动等。
现代操作系统(第3版)——第1章 引论
书名: 现代操作系统(第3版)
作者: [美] Andrew S·Tanenbaum
出版社: 机械工业出版社
原作名: Modern Operating Systems
译者: 马洪兵 | 陈向群
出版年: 2009-7
页数: 582
定价: 75.00元
装帧: 平装
丛书: 计算机科学丛书
ISBN: 9787111255444