第二章 计算机系统结构
Thank lyrh!!!
更新日期:2004年5月24日
在研究计算机系统运行的细节之前,需要对计算机系统结构有一个总体的认识。我们将在本章中学习这个体系结构中的几个完全不同的部分,以此来完善我们的背景知识。本章主要关注计算机体系结构,所以,如果已经掌握了这些概念,你就可以略读或者跳过本章。最初的课题包括了系统启动、I/O和存储器。
操作系统也必须确保计算机系统的正确运行。为确保用户程序不会干扰系统的正常运行,硬件必须要提供合适的机制来保证操作的正确性。在本章的后面,我们将描述基本的计算机体系结构,这是设计可用操作系统的基本知识。最后,我们对网络结构做一个概述。
2.1 计算机系统的运行
一个现代通用计算机系统由一个CPU和多个设备控制器组成,它们通过一条公共总线连接到一起,而这条公共总线提供了对共享存储器的访问能力(图 2.1)。每个设备控制器负责某种特定类型的设备(例如:磁盘驱动器、音频设备和视频显示器)。CPU和设备控制器能够同时运行,并且相互竞争总线周期。为确保对共享存储器访问的有序性,需要提供一个存储控制器以同步对存储器的访问。
图 2.1
计算机开始运行(如开启电源或者重新启动)时需要首先运行一个初始化程序。这个初始化程序(或者说是引导程序)往往很简单。它通常存储在计算机硬件中的只读存储器(比如:固件或EEPROM)内。从CPU寄存器到设备控制器再到内存,引导程序初始化系统的各个方面。引导程序必须要知道如何装入操作系统并开始运行它。因此,引导程序必须要为操作系统内核分配内存空间并将其装入内存。操作系统此时才开始运行第一个进程(比如:“init”),然后等待事件的发生。
事件通常由硬件或软件中断触发。硬件随时会通过系统总线向CPU发送信号的方式触发一个中断。软件可能会运行一个特殊的操作触发一个中断,这个特殊的操作被称为系统调用(也称之为监督程序调用)。
现代操作系统是中断驱动的。如果没有进程运行、没有I/O设备运行并且没有用户响应,操作系统将停下来等待事件的发生。事件几乎总是通过中断或自陷发出信号。自陷(也称为异常)是一种由软件产生的中断,它由错误(例如,除以零或无效内存访问)或用户程序请求操作系统执行特殊的服务引起。操作系统的中断驱动的特性定义了系统的一般架构。针对每种类型的中断,操作系统中独立的代码段定义了应该执行什么样的操作。操作系统提供了中断服务程序,由它来负责处理中断。
当CPU接收到中断信号时,它会停止当前的工作并立即转向一个确定地点。这个地点通常存储了该中断服务程序的入口地址;处理完成后,CPU恢复被中断的计算。图2.2中表明了该操作的时序关系。
图 2.2
中断是计算机体系结构中的重要组成部分。每种计算机都有自己的中断机制,但是有些功能是共同的。中断必须要将控制移交给适当的中断服务程序。一个简单的方法是调用一个通用程序来检查中断信息;然后再调用具体的中断服务处理程序。然而,中断必须要得到快速处理,预定义中断数目是可行的,这样就可以使用一个指向中断处理程序的中断向量表。于是通过这个表间接调用中断程序,就不再需要中间程序了。通常,中断向量表存储在内存的低字节(前100位,大致如此)。这些位置存储了各种设备的中断服务进程地址。这个地址队列,或者说是中断向量指向由中断请求给定的唯一的设备号,并向中断设备提供中断服务程序地址。操作系统MS-DOS和UNIX在中断的工作方式上有所不同。
中断体系结构还必须保存被中断的指令的地址。许多老式的设计简单的将中断地址存储在一个确定地点或由设备号索引的地点。更新近的体系结构将返回地址存储在系统堆栈中。如果中断处理程序需要改变处理器状态(例如:通过修改寄存器值),它必须要显式的保存当前状态,然后在返回之前将其还原。在中断服务结束后,存储的返回地址将被装载到程序计数器中,此时被中断的计算重新开始,就像是中断没有发生过一样。
依据底层处理机提供的功能,有多种方式可以请求系统调用。不管用的是什么方式,它是进程请求操作系统服务的方法。系统调用通常采用自陷到中断向量指定地点的方式。通常可以执行通用的trap指令来产生一个自陷,而有些系统(例如:MIPS R2000家族)有一个专门的syscall指令。
2.2 I/O结构
正如在第2.1节中描述的,一个现代的通用计算机系统由一个CPU和多个设备控制器组成,这些设备控制器连接到一条公共总线上。每个设备控制器负责某种特定类型的设备。可能会连接有多个附属设备,这取决于于控制器。例如,小型计算机系统接口(SCSI)控制器具备七个或更多的设备挂载能力。一个设备控制器上有本地缓冲存储器和一系列特定用途的寄存器。设备控制器负责在它控制的外围设备和本地缓冲存储器之间传送数据。根据所控制设备的不同,设备控制器中本地缓冲器的大小也不尽相同。例如:磁盘控制器中缓存的大小应该等于磁盘最小的可寻址空间的大小(也就是一个扇区,通常是512字节),或者是它的倍数。
2.2.1 输入输出中断
要开始一个I/O操作,CPU首先要给设备控制器中相应的寄存器赋值。然后设备控制器检查这些寄存器的内容以决定下一步的动作。例如,如果设备控制器发现一个读请求,它就开始把数据从设备传送到本地缓冲器中。一旦数据传输结束,设备控制器就会通知CPU该操作已经结束。通过触发一个中断来完成这种通信。
通常,在用户进程请求I/O时就会发生这种情况。一旦I/O操作开始,就会有两种可能的情况。最简单的一种,I/O操作开始,然后,在I/O操作完成时将控制返回给用户进程。这种被认为是同步输入输出。另外一种可能被称之为异步输入输出,它并不等待输入输出结束,而是直接将控制返回给用户程序。于是,当其它的系统操作运行时输入输出可以继续进行(图 2.3)。
图 2.3
可以有两种方式来等待I/O操作的完成。有些计算机由一个特殊的wait指令,将CPU置于空闲等待状态直到产生下一个中断。没有这种指令的机器可能会有一个等待循环:
Loop: jmp Loop
这种死循环简单的持续运行直到发生一个中断,然后将控制提交给操作系统的另一个部分。这样的循环也可能需要轮流查询多个不支持中断结构的设备;这些设备简单的设置它们寄存器中的一个标志位,然后等待操作系统注意到这个标志。
如果CPU总是等待I/O结束,那么它最多一次只能响应一个I/O请求。因此,不论一个I/O中断何时发生,操作系统总是准确的知道那个设备正在响应中断。从另一方面讲,这种方法不允许多个设备的并行I/O操作,也不允许对I/O的重叠操作。
一种更好的方法是开始I/O操作,然后继续处理其它的操作系统代码或用户程序代码。这就需要一个系统调用允许用户程序在需要的时候等待I/O完成。如果没有用户程序准备好,且操作系统无事可做,那么我们还是像以前那样使用wait指令或空闲循环。我们也需要具备在同时监控多个I/O请求的能力。为此,操作系统使用一个表来为每一个I/O设备建立一个表项,这就是设备状态表(图 2.4)。每个表项指明了设备的类型、地址和状态(不是功能、空闲或忙碌)。如果这个设备正在处理一个请求,那么请求类型和其它的参数将存储在该设备所对应的表项中。因为也可能同时有其它的进程请求同样的设备,所以操作系统也将为每个I/O设备维护一个等待队列(一个等待的请求列表)。
图 2.4
一个I/O设备需要服务时它会触发一个中断。当发生一个中断时,操作系统首先判断是哪个I/O设备发出了中断。然后查询I/O设备表确定其状态,并且修改相应表项来反映该中断。对于大多数设备来说,一个中断信号标志着I/O请求的完成。如果在该设备的等待队列中有其它的请求,那么操作系统就开始处理下一个请求。
最后,控制从I/O中断中返回。如果一个进程正在等待这个请求的完成(在设备状态表中有记录),那么我们现在就可以将控制返给该进程。否则,我们就返回给I/O中断发生前进行的工作:执行其它的用户程序(此用户程序开始了I/O操作并且该操作已经完成,但是这个程序没有等待该操作执行完毕)或者是等待循环(该程序开始了两个或更多个I/O操作并且正在等待某个结束,但是这个中断来自于另外的操作)。在分时系统中,操作系统可能会转向其它的等待(ready-to-run)进程。
具体的方案可能有所不同。许多交互式系统允许用户事先通过键盘键入数据——在请求数据前输入数据。这样,当设备状态段指明当前没有对该设备的输入请求时,中断可能会发生,以表明接收到了来自终端的字符串。如果允许提前输入,那么就必须提供一个缓冲区来存储事先输入的字符直到某些程序读取它们。通常,可能需要为每个输入设备配备一个缓冲区。
异步输入输出的最大的优点是提高了系统效率。在I/O操作发生的同时,CPU可以处理其它工作或是开始其它设备的I/O操作。因为与处理器速度相比I/O的速度相当缓慢,所以异步输入输出可以提高系统效率。在2.2.2节,我们将描述提高系统性能的另外一种机制。
2.2.2 DMA结构
在一个简单的终端输入设备中,从终端读取数据时,输入的第一个字符被发送到计算机。当接收到这个字符,连接到终端线上的异步通信(或串行端口)设备就向CPU产生一个中断。当接收到来自终端的中断请求时,CPU就会执行一些指令。(如果CPU正在执行某个指令,那么中断就要等待该指令执行完毕。)保存这个中断指令的地址,并将控制转移给中断服务程序。
中断服务程序要保存一些CPU寄存器值,因为将来还需要用到这些数据。它要检查最近的输入操作有没有产生错误条件。然后从设备中读取字符并将其保存在一个缓冲器中。中断服务程序还必须调整指针和计数变量以保证将下一个输入字符存储在缓冲器中的下一个位置。下一步,中断服务程序将在内存中设置一个标记,以此向操作系统的其它部分表明接收到了新的输入。其它的部分则负责处理缓冲器中的数据,并将字符传送给请求数据的程序(2.5节)。然后,中断处理程序恢复刚才保存的寄存器内容并将控制返回给被中断的指令。
如果向9600波特的终端中输入字符,那么终端大约能够以每毫秒一个字符,也就是每1000微妙一个字符的速度接受和传输。一个优秀的中断服务程序输入一个字符可能需要2微秒,每秒钟留给CPU 998微秒计算(和为其它的中断服务)。根据这个不同点,异步输入输出通常具有一个较低的中断优先权,允许首先处理其它的更重要的进程,甚至是一个进程抢占当前进程的中断。然而一个高速设备(比如:磁带、磁盘或者是通讯网络)可能以接近于内存的速度传输数据;举个例子,如果CPU响应每个中断需要2微秒,中断每4微秒到达一次,那么就没有留下多少的时间用于进程执行。
为了解决这个问题,在高速设备中应用了直接内存访问(DMA)技术。在为设备设置好缓冲器、指针和计数器后,设备控制器直接或者是通过自身的缓冲器将整个数据块读取至内存,整个过程无需CPU干涉。每个数据块仅仅产生一个中断,而不像慢速设备每个字节(或字)就产生一个中断。
CPU的基本操作是相同的。一个用户程序或者操作系统本身可能请求数据传输。操作系统从缓冲池中为数据传输指定一个缓冲器(一个空缓冲区用于输入,一个满缓冲区用于输出)。(根据设备类型,一个缓冲器典型的大小在128到4,096字节之间。)下一步,设备驱动程序(操作系统的一部分)将源地址、目标地址和传输数据长度设置到DMA控制器的寄存器中。然后,DMA控制器命令开始I/O操作。当DMA控制器执行数据传输时,CPU可以自由的执行其它的任务。因为存储器一次通常只能够传输一个字,DMA控制器从CPU中“窃取”了存储周期。在进行DMA传输时,周期挪用(cycle stealing)降低了CPU的执行速度。传输结束后DMA控制器就向CPU发出中断。
(not ent...)
//受到文章体积的限制,不得以将图片删除,,见谅!