1.1 什么是操作系统
众所周知,处理机、主存、磁盘、终端、网卡等硬件资源通过主板连接构成了看得见、摸得着的计算机硬件系统。为了能使这些硬件资源高效地、尽可能并行地被用户程序使用,也为了给用户程序提供易用的访问这些硬件的方法,我们必须为计算机配备操作系统软件。操作系统的工作就是管理计算机的处理机、主存、外设等硬件资源,提供存放于存储设备的文件等逻辑资源,并组织用户任务(如以进程形式)使用这些资源。
操作系统是一种系统软件,是软、硬资源的控制中心,它以尽量合理有效的方法组织单个或多个用户以多任务方式共享计算机的各种资源。
1.1.1 计算机系统的软件构成
计算机系统的软件层次及构成如图1.1所示。
图1.1 计算机系统的软件层次及构成
当用户在计算机中安装操作系统时,如图1.1所示的操作系统内核、命令解释器、编辑器、编译器、各种库程序,甚至数据库管理器、Web服务器等都从安装介质复制到了计算机系统的磁盘上。
无论是安装UNIX、Linux还是Windows操作系统,命令解释器都是必不可少的一个程序,用户通过它来使用计算机系统。如果没有它,用户就无法操作计算机,无法输入命令让计算机去执行(注意,在窗口界面中,用户通过与命令解释器对应的程序管理器,如Windows的explorer.exe来使用计算机)。在现代操作系统实现中,命令解释器程序没有作为操作系统内核的组成部分,但它在使用计算机过程中是不可缺少的,用户在终端输入的命令就是由命令解释器程序接收并解释执行的。其他的操作系统内核层之上的程序则是根据计算机的定位(服务器或工作站)而选择安装的。如果将计算机定位成程序开发用的工作站,那么用户必须安装编辑器进行程序编辑,并安装编译器进行程序编译。如果把计算机作为一个网络上的Web服务器,那么必须安装Web服务器程序。无论是用户自编的普通C语言程序还是数据库应用程序,都在操作系统安装并运行后开发或安装。这些在操作系统内核层之上的程序,不管是命令解释器、Web服务器或用户自编的程序,都是通过操作系统提供的进程机制来运行的。
从狭义上看,操作系统只包含如图1.1所示的操作系统内核,它是一个非常重要的系统程序,管理着系统中所有的公共资源,并提供实现程序运行的进程机制。由于操作系统内核工作的重要性、特殊性,它必须在一种特殊的保护状态下运行,以免受到用户层程序的干扰和破坏,它提供一组称为系统调用的接口,供上层程序调用,从而保证操作系统内核在特殊保护状态下运行的需求,并且满足上层程序对系统资源的申请、使用、释放以及进程的创建、结束等诸多功能的需求。
图1.1中的各种库程序实际上就是一些可以重用的、公用的子程序,它们提供形形色色的功能。系统提供这些库程序是为了方便用户编程,用户不必为了实现一个通用的功能再重写上述库程序代码,而只要引用库程序中的函数即可。库程序可以看成一些通用的、公共的程序集合,利用内核提供的简单的资源管理功能实现复杂的复合功能。这些通用的公共程序之所以不放到操作系统内核中去实现,是因为它们不涉及系统公共资源的管理,也是为了控制内核的大小。
1.1.2 操作系统作为特殊子程序
从图1.1可以看出,操作系统内核位于计算机硬件之上。操作系统内核为用户层程序提供系统调用(又称广义指令)功能。系统调用与普通函数调用相似,可以看成是特殊的公共子程序,因为这些程序提供了一些可以被任意用户层程序调用的公共功能,所以用户不需要再编写实现这些功能的程序,只要调用操作系统内核提供的相应“系统调用”即可。但是,要特别注意系统调用的特殊性,即系统调用处理程序运行在一种特殊的保护状态下。在这种状态下,程序可以执行一些特权指令,访问用户层程序访问不到的系统存储空间。系统调用之所以具有这样的特殊性,是因为系统调用处理程序涉及系统共享资源的操作。
举例来说,求的值是许多用户程序都要做的工作,可以把它作为一个公共子程序实现。那么它需要作为系统调用在操作系统内核实现吗?回答是否定的。虽然计算需要许多条机器指令来实现,但因为它不涉及系统的共享资源,只对输入变量x进行操作,因此可以把它作为数学函数库中的子程序来实现。
以前的计算机都使用软盘,许多基于Intel x86的个人计算机都使用NEC的PD765软盘驱动器(简称软驱),该软驱支持16条命令,可以通过对软驱控制部件中的寄存器置不同的值来执行初始化、移动磁头、读/写数据等命令。其中,最基本的命令是读/写命令,需要13个参数,如磁盘块地址、每个磁盘的扇区数、物理介质中所用的记录模式、扇区间距等。当操作完成时,软驱控制部件中的状态寄存器中有一堆状态位,由驱动程序判定是正常完成还是异常结束。在启动读/写命令前还需要判定软驱电机是否已启动,若未启动还需要先启动电机。如果这些操作都交给用户编程实现,不仅复杂,而且每个用户都要重复编程,多个用户使用时还会引起混乱。因此,操作系统给用户提供一个简单的统一的文件操作界面,即软盘上包含多个文件,每个文件可以按照读/写方式打开,然后进行读/写,最后关闭文件。用户无需知道电机如何启动、如何读/写数据,也不需要知道要读/写的数据放在软盘的哪个扇区,只需要知道读/写哪个文件的哪一段数据即可,利用这个简单的文件操作界面就可以与软盘进行数据交换。这个文件操作界面由操作系统的系统调用实现,因为软驱不是某个用户的私有资源,软盘上的文件可以供多个用户访问,涉及到软驱和文件的管理数据都应该受到保护,所以文件操作以操作系统内核系统调用形式实现。
1.1.3 操作系统作为资源管理者
计算机由处理机、主存、辅存、终端设备、网络设备等硬件资源组成。处理机提供程序执行能力;主存、辅存提供程序和数据的存储能力;终端设备提供人机交互能力;网络设备提供机间通信能力。这些硬件资源要能被计算机用户高效地使用,必须有适合每种硬件资源特点的资源分配和使用机制。
为使硬件资源充分发挥作用,必须允许多用户或单用户以多任务方式同时使用计算机,以便让不同的资源由不同的用户任务同时使用,减少资源的闲置时间。例如,当一个用户任务将文件内容从磁盘往主存的缓冲区读出时,另一个用户任务可以让自己的程序在处理机上运行。这样,处理机、主存、磁盘同时工作,也就提高了资源利用率。
要让每种资源被多用户任务充分利用,就需要研究每种资源的特点。对于单处理机来说,它只能执行一个指令流。如果多个用户任务都要使用它,那只有让多个用户任务的程序分时地在处理机上运行,也就是说,处理机交替地运行多个用户任务中的程序。这意味着,操作系统要合理调度多用户任务使用处理机。存储设备为程序和数据提供存放空间,只要多个用户的程序和数据按照规定的位置存放,互不交叉占用,它们是可以共存的,操作系统要做的事就是管理存储空间,把适用的空间分配给用户的程序和数据使用,当用户任务访问这些程序和数据时要能够找到它们。
针对不同资源特点,资源管理包含两种资源共享使用的方法:“时分”和“空分”。
① 时分就是由多个用户进程分时地使用该资源,除了上述的处理机外,还有很多其他的资源也必须分时地使用,如外设控制器、网卡等,这些控制部件包含了控制I/O的逻辑,必须分时地使用。
② 空分是针对存储资源而言,存储资源的空间可以被多个用户进程共同以分割的方式占用。
在时分共享使用的资源当中,有如下两种不同的使用方法:
① 独占式使用。独占表示某用户任务占用该资源后,执行对资源的多个操作,使用一个完整的周期。例如,如果多用户任务使用打印机,那么对打印机的独占式使用是指多用户任务一定是分时地使用该打印机的,每个用户任务使用打印机时,执行了多条打印指令,打印了一个完整的对象(如完整的文件)。这里,每个用户任务要执行多条打印指令,为了不让多条打印指令在执行过程中被别的打印任务中断,用户任务需要在执行打印指令前申请独占该打印机资源,执行完所有打印指令后再释放。
② 分时式共享使用。这种共享使用是指用户任务占用该资源无需使用一个逻辑上的完整周期。例如,对处理机的使用,用户任务随时都可以被剥夺CPU,只要运行现场保存好了,下次该用户任务再次占用CPU时就可以继续运行。再如,对磁盘的输入/输出,当一个用户任务让磁盘执行一条I/O请求后,其他用户任务又可向磁盘发I/O请求,系统并不要求某个用户任务的几个I/O请求之间不能插入其他用户任务的I/O请求。
操作系统应针对不同的资源类型,实现不同的资源分配和使用策略,并为资源分配、释放、使用提供相应的系统调用接口。
1.1.4 操作系统提供程序并发运行机制
用户可使用计算机进行科学计算、数据管理、通信、控制等工作。要实现所述的这些任务,必须执行相应的程序。用户使用处理机来执行程序,用程序驱动外部设备来进行数据交换,驱动网络设备来进行通信。用户的意图必须由程序及程序的输入参数表示出来,为了实现用户意图,必须让实现相应功能的程序执行;为了能让程序执行,需要由操作系统给程序及程序数据安排存放空间;为了能提高资源利用率,增加并发度,还必须能让多个用户程序能分时占用处理机;为了能够让一个程序还没运行完就让另一个程序占用CPU运行,就必须保存上一个程序的运行现场。因此,必须要对实现各种用户意图的各个程序的执行过程进行描述和控制。
说明程序执行的状态、现场、标识等各种信息,有选择地调度某个程序占用CPU运行,这些工作必须由操作系统完成,这也是为了实现程序对CPU的分时使用。
操作系统一般用进程机制来实现程序的执行。
进程是指运行当中的程序,也就是指程序针对于某一数据集合的执行过程。操作系统的进程调度程序决定CPU在各执行程序间的切换。操作系统为用户提供进程创建和结束等的系统调用功能,使用户能够创建新进程以运行新的程序。操作系统在系统初始化后,会为每个可能的系统用户创建第一个用户进程,用户的其他进程则可以由先前生成的用户进程通过“进程创建”系统调用陆续创建,以完成用户的各种任务。
在支持交互使用计算机的系统中,用户的第一个进程往往运行命令解释器程序(对于图形窗口终端用户而言,就是具有窗口界面的程序管理器,如Windows操作系统的explorer.exe),这个程序会从终端获得用户输入的命令(或用户单击执行程序图标的信息),再进行相应的处理,可能会调用操作系统的“创建进程”系统调用,创建新进程去运行实现命令功能的程序。例如,在Linux操作系统控制的终端上输入:
$ cp /home/ly/test.c /home/wq/hello.c
那么,这一行字符串会由命令解释器程序获得,它会创建一个子进程,由子进程去运行cp实用程序,由cp实用程序建立一个新文件/home/wq/hello.c,并把/home/ly/test.c文件的内容读出来,写入hello.c中。