预计阅读本页时间:-
1.3 计算机硬件介绍
操作系统与运行该操作系统的计算机硬件联系密切。操作系统扩展了计算机指令集并管理计算机的资源。为了能够工作,操作系统必须了解大量的硬件,至少需要了解硬件如何面对程序员。出于这个原因,这里我们先简要地介绍现代个人计算机中的计算机硬件,然后开始讨论操作系统的具体工作细节。
从概念上讲,一台简单的个人计算机可以抽象为类似于图1-6中的模型。CPU、内存以及I/O设备都由一条系统总线连接起来并通过总线与其他设备通信。现代个人计算机结构更加复杂,包含多重总线,我们将在后面讨论之。目前,这一模式还是够用的。在下面各小节中,我们将简要地介绍这些部件,并且讨论一些操作系统设计师们所考虑的硬件问题。毫无疑问,这是一个非常简要的概括介绍。现在有不少讨论计算机硬件和计算机组织的书籍。其中两本有名的书的作者分别是Tanenbaum(2006)和Patterson与Hennessy(2004)。

1.3.1 处理器
计算机的“大脑”是CPU,它从内存中取出指令并执行之。在每个CPU基本周期中,首先从内存中取出指令,解码以确定其类型和操作数,接着执行之,然后取指、解码并执行下一条指令。按照这一方式,程序被执行完成。
广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元
每个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中是一个有着三个阶段的流水线示意图。更长的流水线也是常见的。在多数的流水线设计中,一旦一条指令被取进流水线中,它就必须被执行完毕,即便前一条取出的指令是条件转移,它也必须被执行完毕。流水线使得编译器和操作系统的编写者很头疼,因为它造成了在机器中实现这些软件的复杂性问题。

比流水线更先进的设计是一种超标量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。(后面将解释缓存。)要使用这类多核芯片肯定需要多处理器操作系统。
