180211—【系统】发展史

串行处理

对于早期的计算机,从20 世纪40 年代后期到20 世纪50 年代中期,程序员都是直接与计算机硬件打交道的,因为当时还没有操作系统。这些机器都是在一个控制台上运行的,控制台包括显示灯、触发器、某种类型的输入设备和打印机。用机器代码编写的程序通过输入设备(如卡片阅读机)载入计算机。如果一个错误使得程序停止,错误原因由显示灯指示。如果程序正常完成,输出结果将出现在打印机中。

这些早期系统引出了两个主要问题:

  • 调度:大多数装置都使用一个硬拷贝的登记表预订机器时间。通常,一个用户可以以半小时为单位登记一段时间。有可能用户登记了1 小时,而只用了45
    分钟就完成了工作,在剩下的时间中计算机只能闲置,这时就会导致浪费。另一方面,如果用户遇到一个问题,没有在分配的时间内完成工作,在解决这个问题之前就会被强制停止。
  • 准备时间:一个程序称做作业,它可能包括往内存中加载编译器和高级语言程序(源程序),保存编译好的程序(目标程序),然后加载目标程序和公用函数并链接在一起。每一步都可能包括安装或拆卸磁带,或者准备卡片组。如果在此期间发生了错误,用户只能全部重新开始。因此,在程序运行前的准备需要花费大量的时间。

这种操作模式称做串行处理,反映了用户必须顺序访问计算机的事实。后来,为使串行处理更加有效,开发了各种各样的系统软件工具,其中包括公用函数库、链接器、加载器、调试器和I/O 驱动程序,它们作为公用软件,对所有的用户来说都是可用的。

简单批处理系统

早期的计算机是非常昂贵的,同时由于调度和准备而浪费时间是难以接受的,因此最大限度地利用处理器是非常重要的。

为提高利用率,人们有了开发批处理操作系统的想法。第一个批处理操作系统用在IBM 701 上。这个系统随后经过进一步的改进,被很多IBM 用户在IBM 704 中实现。在20 世纪60 年代早期,许多厂商为他们自己的计算机系统开发了批处理操作系统,用于IBM 7090/7094 计算机的操作系统IBSYS 最为著名,它对其他系统有着广泛的影响。

监控程序

简单批处理方案的中心思想是使用一个称做监控程序的软件。通过使用这类操作系统,用户不再直接访问机器,相反,用户把卡片或磁带中的作业提交给计算机操作员,由他把这些作业按顺序组织成一批,并将整个批作业放在输入设备上,供监控程序使用。每个程序完成处理后返回到监控程序,同时,监控程序自动加载下一个程序。

为了理解这个方案如何工作,可以从以下两个角度进行分析:监控程序角度和处理器角度

  • 监控程序角度:监控程序控制事件的顺序。为做到这一点,大部分监控程序必须总是处于内存中并且可以执行(见图2.3),这部分称做常驻监控程序(resident
    monitor)。其他部分包括一些实用程序和公用函数,它们作为用户程序的子程序,在需要用到它们的作业开始执行时被载入。监控程序每次从输入设备(通常是卡片阅读机或磁带驱动器)中读取一个作业。读入后,当前作业被放置在用户程序区域,并且把控制权交给这个作业。当作业完成后,它将控制权返回给监控程序,监控程序立即读取下一个作业。每个作业的结果被发送到输出设备(如打印机),交付给用户。

180211—【系统】发展史

  • 处理器角度:从某个角度看,处理器执行内存中存储的监控程序中的指令,这些指令读入下一个作业并存储到内存中的另一个部分。一旦已经读入一个作业,处理器将会遇到监控程序中的分支指令,分支指令指导处理器在用户程序的开始处继续执行。处理器继而执行用户程序中的指令,直到遇到一个结束指令或错误条件。不论哪种情况都将导致处理器从监控程序中取下一条指令。因此,“控制权交给作业”仅仅意味着处理器当前取和执行的都是用户程序中的指令,而“控制权返回给监控程序”的意思是处理器当前从监控程序中取指令并执行指令。
监控程序完成调度功能:

一批作业排队等候,处理器尽可能迅速地执行作业,没有任何空闲时间。监控程序还改善了作业的准备时间,每个作业中的指令均以一种作业控制语言(Job ControlLanguage,JCL)的基本形式给出。这是一种特殊类型的程序设计语言,用于为监控程序提供指令。举一个简单的例子,用户提交一个用FORTRAN 语言编写的程序以及程序需要用到的一些数据,所有FORTRAN 指令和数据在一个单独打孔的卡片中,或者是磁带中一个单独的记录。除了FORTRAN 指令和数据行,作业中还包括作业控制指令,这些指令以“$”符号打头。作业的整体格式如下所示:
180211—【系统】发展史

为执行这个作业,监控程序读 FTN 行,从海量存储器(通常为磁带)中载入合适的语言编译器。编译器将用户程序翻译成目标代码,并保存在内存或海量存储器中。如果保存在内存中,则操作称做“编译、加载和运行”。如果保存在磁带中,就需要 LOAD 指令。在编译操作之后监控程序重新获得控制权,此时监控程序读$LOAD 指令,启动一个加载器,并将控制权转移给它,加载器将目标程序载入内存(在编译器所占的位置中)。在这种方式中,有一大段内存可以由不同的子系统共享,但是每次只能运行一个子系统。

在用户程序的执行过程中,任何输入指令都会读入一行数据。用户程序中的输入指令导致调用一个输入例程,输入例程是操作系统的一部分,它检查输入以确保程序并不是意外读入一个JCL 行。如果是这样,就会发生错误,控制权转移给监控程序。用户作业完成后,监控程序扫描输入行,直到遇到下一条JCL 指令。因此,不管程序中的数据行太多或太少,系统都受保护。

可以看出,监控程序或者说批处理操作系统,只是一个简单的计算机程序。它依赖于处理器可以从内存的不同部分取指令的能力,以交替地获取或释放控制权。此外,还考虑到了其他硬件功能:

内存保护:当用户程序正在运行时,不能改变包含监控程序的内存区域。如果试图这样做,处理器硬件将发现错误,并将控制转移给监控程序,监控程序取消这个作业,输出错误信息,并载入下一个作业。

定时器:定时器用于防止一个作业独占系统。在每个作业开始时,设置定时器,如果定时器时间到,用户程序被停止,控制权返回给监控程序。

特权指令:某些机器指令设计成特权指令,只能由监控程序执行。如果处理器在运行一个用户程序时遇到这类指令,则会发生错误,并将控制权转移给监控程序。I/O 指令属于特权指令,因此监控程序可以控制所有I/O 设备,此外还可以避免用户程序意外地读到下一个作业中的作业控制指令。如果用户程序希望执行I/O,它必须请求监控程序为自己执行这个操作。

中断:早期的计算机模型并没有中断能力。这个特征使得操作系统在让用户程序放弃控制权或从用户程序获得控制权时具有更大的灵活性。

内存保护和特权指令引入了操作模式的概念。用户程序执行在用户态,在这个模式下,有些内存区域是受到保护的,特权指令也不允许执行。监控程序运行在系统态,也可以称为内核态,在这个模式下,可以执行特权指令,而且受保护的内存区域也是可以访问的。

当然,没有这些功能也可以构造操作系统。但是,计算机厂商很快认识到这样做会造成混乱,因此,即使是相对比较原始的批处理操作系统也提供这些硬件功能。

对批处理操作系统来说,用户程序和监控程序交替执行。这样做存在两方面的缺点:一部分内存交付给监控程序;监控程序消耗了一部分机器时间。所有这些都构成了系统开销,尽管存在系统开销,但是简单的批处理系统还是提高了计算机的利用率

多道程序设计批处理系统

即便对由简单批处理操作系统提供的自动作业序列,处理器仍然经常是空闲的。问题在于I/O 设备相对于处理器速度太慢。下图详细列出了一个有代表性的计算过程,这个计算过程所涉及的程序用于处理一个记录文件,并且平均每秒处理100条指令。在这个例子中,计算机96%的时间都是用于等待I/O设备完成文件数据传送。这种只有一个单独程序的情况,称做单道程序设计(uniprogramming)。处理器花费一定的运行时间进行计算,直到遇到一个I/O 指令,这时它必须等到这个I/O 指令结束后才能继续进行。

这种低效率是可以避免的。内存空间可以保存操作系统(常驻监控程序)和一个用户程序。假设内存空间容得下操作系统和两个用户程序,那么当一个作业需要等待I/O 时,处理器可以切换到另一个可能并不在等待I/O 的作业(见图b)。进一步还可以扩展存储器以保存三个、四个或更多的程序,并且在它们之间进行切换(见图c)。这种处理称做多道程序设计(multiprogramming)或多任务处理(multitasking),它是现代操作系统的主要方案。
180211—【系统】发展史

分时系统

通过使用多道程序设计,可以使批处理变得更加有效。但是,对许多作业来说,需要提供一种模式,以使用户可以直接与计算机交互。实际上,对一些作业如事务处理,交互模式是必需的。当今,通常使用专用的个人计算机或工作站来完成交互式计算任务,但这在20 世纪60 年代却是行不通的,当时大多数计算机都非常庞大而且昂贵,因而分时系统应运而生。

正如多道程序设计允许处理器同时处理多个批作业一样,它还可以用于处理多个交互作业。对后一种情况,由于多个用户分享处理器时间,因而该技术称做分时(time sharing)。在分时系统中,多个用户可以通过终端同时访问系统,由操作系统控制每个用户程序以很短的时间为单位交替执行。因此,如果有n 个用户同时请求服务,若不计操作系统开销,每个用户平均只能得到计算机有效速度的1/n。但是由于人的反应时间相对比较慢,所以一个设计良好的系统,其响应时间应该可以接近于专用计算机。

第一个分时操作系统是由麻省理工学院(MIT)开发的兼容分时系统,该系统最初是在1961 年为IBM 709 开发的,后来又移植到IBM 7094 中。

与后来的系统相比,CTSS 是相当原始的。该系统运行在一台内存为32 000 个36 位字的机器上,常驻监控程序占用了5000 个。当控制权被分配给一个交互用户时,该用户的程序和数据被载入到内存剩余的27 000 个字的空间中。程序通常在第5000 个字单元处开始被载入,这简化了监控程序和内存管理。系统时钟以大约每0.2 秒一个的速度产生中断,在每个时钟中断处,操作系统恢复控制权,并将处理器分配给另一位用户。因此,在固定的时间间隔内,当前用户被剥夺,另一个用户被载入。这项技术称为时间片(time slicing)技术。为了以后便于恢复,保留老的用户程序状态,在新的用户程序和数据被读入之前,老的用户程序和数据被写出到磁盘。随后,当获得下一次机会时,老的用户程序代码和数据被恢复到内存中。

为减小磁盘开销,只有当新来的程序需要重写用户存储空间时,用户存储空间才被写出。假设有4 个交互用户,其存储器需求如下:

JOB1:15 000

JOB2:20 000

JOB3:5000

JOB4:10 000

最初,监控程序载入JOB1 并把控制权转交给它,如图2a 所示。稍后,监控程序决定把控制权转交给JOB2,由于JOB2 比JOB1 需要更多的存储空间,JOB1 必须先被写出,然后载入JOB2,如图b 所示。接下来,JOB3 被载入并运行,但是由于JOB3 比JOB2 小,JOB2 的一部分仍然留在存储器中,以减少写磁盘的时间,如图c 所示。稍后,监控程序决定把控制交回JOB1,当JOB1 载入存储器时,JOB2 的另外一部分将被写出,如图d 所示。当载入JOB4 时,JOB1的一部分和JOB2 的一部分仍留在存储器中,如图e 所示。此时,如果JOB1 或JOB2 被**,则只需要载入一部分。在这个例子中是JOB2 接着运行,这就要求JOB4 和JOB1 留在存储器中的那一部分被写出,然后读入JOB2 的其余部分

180211—【系统】发展史

与当今的分时系统相比,CTSS 是一种原始的方法,但它可以工作。它非常简单,从而使监控程序最小。由于一个作业经常被载入到存储器中相同的单元,因而在载入时不需要重定位技术(在后面讲述)。这个技术仅仅写出必须的内容,可以减少磁盘的活动。在7094 上运行时,CTSS最多可支持32 个用户。

分时和多道程序设计引发了操作系统中的许多新问题。如果内存中有多个作业,必须保护它们不相互干扰,例如不会修改其他作业的数据。有多个交互用户时,必须对文件系统进行保护,只有授权用户才可以访问某个特定的文件,还必须处理资源(如打印机和海量存储器)竞争问题。在本书中会经常遇到这样或那样的问题以及可能的解决方法。

(内容来源:操作系统—精髓与设计原理第八版,图片来自网络)