操作系统

现在我们拥有一块微处理器,一些随机访问存储器,一款键盘,一台视频显示器和一个磁盘驱动器。我们将这些计算机所拥有的部件组装起来,伸手给它上电。

当阴极射线管加热之后,一串排列整齐——而又完全随机的——ASCII码字符阵列出现在屏幕上。不出我们所料,掉电的时候,半导体存储器中的内容就会被全部清零;而再一次打开电源时,屏幕上的ASCII又以随机的排列出现。

这里我们缺少的是软件。当微处理器加电或复位时,它会执行内存中某个地址里存放的机器代码。对8080来说,这个地址是0000h。对正确的计算机来说,加电时,该地址处应该有一个机器代码指令。

机器代码指令如何存放到内存初始位置的

在新设计的计算机中,把软件放到合适地方的处理过程可能是最令人费解的。要理解它,先从控制面板着手。该控制面板和第16章讲到的用来写入字节到随机访问存储器然后再读取出的控制面板相似。

与以前的控制面板不同的是,这个控制面板有一个标明为复位的开关,这个开关连接到微处理器的复位输入,只要这个开关是闭合的,处理器就什么都不做;当断开这个开关后,微处理器就开始执行机器码。

控制面板的使用方法是:复位开关置ON,复位微处理器,中止执行机器码;接管开关ON,则接收总线上的地址信号和数据信号。这时,可以使用A0~A15开关输入16位的存储器地址。标为D0~D7的灯用来显示该地址的8位内容。要写入一个新的字节到相应的地址,则应在D0~D7开关上设置该字节,然后把写入开关先拨到ON再拨到OFF。完成了向内存中写入相应字节以后,把接管开关设置为OFF,复位开关设置为OFF,则微处理器开始执行程序。

这就是如何向刚刚开头建成的计算机中输入第一个机器码程序的过程,不用说,这是很费事的。

程序输出显示在视频显示器

在上一章中已经讲到,只显示字符的视频显示器有1KB的随机访问存储器用来存放25行,每行40个字符的ASCII码。程序把内容写入到该存储器中,方法与写入到计算机中其他存储器中的方法一样。

然而,把程序的输出显示到视频显示器并不是那么简单。例如,如果一段程序执行结果是4Bh,则不能简单地把这个值写入视频显示器的存储器中。如果这样做,屏幕上将会看到的是字符K。正确的是写出两个ASCII码字符到显示器:34h(4的ASCII)和42h(B的ASCII)。8位的计算结果每半个字节是一个十六进制数字,该数字必须通过对应的ASCII码来显示。

当然,也可以写一段小的子程序来完成这种转换。下面的一段8080汇编语言程序用来把十六进制数中的一位转换成对应的ASCII码(假定包含的十六进制数范围从00h~0Fh):

NibbleToAscii: CMP A, 0Ah  ; 判断是数字还是字母
               JC Number
               ADD A, 37h  ; 把A~F转换成41h~46h
               RET
Number:        ADD A, 30h  ; 把0~9转换成30h~39h
               RET

下面的子程序调用NibbleToAscii两次,把累加器A中的一个字节转换成两个ASCII码数字,并放在寄存器B和C中:

ByteToAscii:   PUSH PSW  ; 保存A
               RRC       ; A右移4次,取得高半字节
               RRC
               RRC
               RRC
               CALL NibbleToAscii ; 转换成ASCII码
               MOV B, A  ; 把结果放入寄存器B
               POP PSW   ; 取出原来的A
               AND A, 0Fh; 取低半字节
               CALL NibbleToAscii ; 转换成ASCII码
               MOV C, A  ; 结果放入寄存器C中
               RET

通常这些程序,可以把一个用十六进制表示的字节显示在视频显示器上。进一步来讲,如果想把它转换成十进制数,还需要做写别的工作。这个过程与把一个数从十六进制转换成十进制的过程(用10除以几次)十分相似。

键盘处理程序

控制面板的确不需要很多硬件支持,但是它不便于使用。它有着最糟糕的输入/输出方式——通过按键输入0和1.如何把控制面板去掉是要解决的重要问题。

实现按键来控制输入/输出的不二之选是键盘。前面我们已经搭建了一个计算机键盘,每次有按键按下的时候,就会产生一个中断信号送至微处理器。计算机内有中断控制芯片,通过执行一条RST指令使得微处理器响应这次中断,例如RST 1,微处理器执行这条指令,把当前程序计数器的值压入到堆栈中,然后跳转到地址0008h处。可以直接在这片地址空间上输入一些代码(使用控制面板),这些代码称为键盘处理程序(keyboard handler)。

为了使复位后微处理器能正常工作,微处理器在复位的时候需要执行一些代码,称为初始化代码(initialization code)。堆栈指针在运行初始化代码的时候会被设置,以保证堆栈处在内存的有效区域内。为了不让屏幕上显示随机字符,初始化代码还把视频显示器内存中的每一个字节设置成十六进制数20h,在ASCII码中这是一个空格符。此外,初始化代码还要把光标定位在第一行第一列的位置——OUT(Output)执行可以完成这一操作:光标在视频显示器上是以下划线的形式出现的——它可以显示处下一个要输入字符的位置。为了使微处理器能响应键盘中断,必须设置EI指令开中断,而HLT指令可以使微处理器停止工作。

上面讲述的就是初始化代码的作用。执行了HLT指令后,计算机则处于停机状态。为了把计算机从停机状态唤醒,只能通过控制面板的复位信号或键盘的中断信号来实现。

键盘处理程序的规模要远大于初始化代码,这个程序才是真正响应键盘事件的代码段。

任何时候,只要键盘上的一个按键被按下,微处理器就会响应本次中断,并从初始化代码末尾的HLT语句跳转到键盘处理程序。键盘处理程序利用IN(Input)指令用来检查是哪个按键被按下,并根据这个按键执行相关的操作(就是说,键盘处理程序对每个按键进行相应的处理),然后执行RET(Return)指令以返回HLT语句,等待另一个键盘中断。

当你按下字母,数字或者标点符号键的时候,键盘扫描程序就会启用键盘扫描码,并根据Shift键是按下与否确定相应的ASCII码。接下来我们要做的,就是把这个ASCII码写到视频显示器的内存中,当然这不是随意的,而是要写在光标所在的位置。这样的一个过程,我们可以很形象地称之为按键到显示器的回显(echoing)。光标会随着字符的写入而移动,换言之,它总会出现在刚显示的字符后面的空格处。通过键盘,可以输入一串字符,然后把它们在屏幕上显示出来。

当按下回退键(相应的ASCII码值为08h时),最后写入视频显示器内存中的字符回被键盘处理程序删除(其实并不复杂——我们要把空格符对应的ASCII码——20h写入到那个内存位置处就行了)。在这个过程中,光标会移回一格。

通常我们在输入一行字符时,错误是难免的,这时就需要用退格键改正错误,然后按下回车键。回车键并不难找到,键盘上标有“Enter”字样的按键就是。打字员在电动打字机上按下“Return”键表示已经完成一行文字的输入,同时也标明他们已经做好了输入下一行的准备,光标会指向下一行的开始。同样,在计算机中“Enter”键用来实现相同的功能,结束一行的输入并转到下一行。

当键盘处理程序对“Return”或“Enter”键(对应的ASCII码是0Dh)进行处理时,它把视频显示器内存中的这一行文本解释为计算机的一条命令(command),换言之,键盘处理程序的任务是执行此命令。实际上,在键盘处理程序内含有一格命令处理程序(command processor),它可以解释如下三条命令:W命令,D命令和R命令。下面我们来深入地理解一下它们。

首先是W命令。它是以W开头的文本行,此命令用来把若干字节写入(Write)到内存中。例如输入到屏幕上的一行内容如下所示:

W 1020 35 4F 78 23 9B AC 67

运行这条命令,命令处理程序会从内存地址1020处开始,把35,4F等十六进制表示的字节写入内存中。要完成这项工作,键盘处理程序需要把ASCII码转换成字节——前面讲过把字节转换成ASCII码,这些其实就是它的逆变换。

接下来是D命令。它是以D开头的文本行,此命令用来把内存中的一些字节显示(Display)出来。例如输入到屏幕上的一行内容如下所示:

D 1030

接收到命令后,命令处理程序会把地址1030h开始的11个字节的内容显示出来(这里之所以说是11个字节,是因为在一个每行可以容纳40个字符的显示器上,除去显示命令与地址标示,后面能显示的也只有这么多了)。有了这条命令,就可以很方便地查看内存中的内容。

最后是R命令。它是以R开头的文本行,表示运行(Run)。该命令形式如下:

R 1000

执行此命令意味着“处理器会运行从地址 1000h 开始的一段程序”。首先命令处理程序把1000h 存储在寄存器对HL中,接着执行命令PCHL,这条指令的功能是把HL说存储的值加载到程序计数器中,然后跳转到程序计数器指向的地址并运行程序。

键盘处理程序及命令处理程序简化了很多工作,可以说是计算机发展的一个里程碑。一旦使用了它,就无需理会那个令人难以忍受的控制面板了。我们不得不承认,键盘输入更简单,更快,更好,这是其他方法无法媲美的。

命令处理程序

但是,你仍然没有摆脱之前的老问题,一旦关闭电源,你辛辛苦苦所输入的数据会全部消失。当然,你可以把所有新代码存到只读存储器(ROM)中。

上一章讲到的ROM芯片,就包含了把ASCII码字符显示到视频显示器上所需的全部点阵模式。在这里假定,这些数据在厂家制造芯片时已经配置好了,当然你也可以对ROM芯片进行编程。可编程只读存储器(Programmable Read-Only Memory, PROM)只能编程一次;而可擦除可编程只读存储器(Erasable Programmable Read-Only Memory, EPROM),可重复擦除和写入,该芯片的正面开有一个玻璃窗口,让紫外线透过这个孔照射内部芯片就可以擦除其中的内容了。

回忆前面曾经讲过的内容,你一定会想起,RAM板与一个DIP开关相连,有了这个开关就可以设定RAM板的起始位置了。8080系统在初始化时,其中的一个RMA板的其实地址被设置为0000h。但是如果有了ROM的话,这个地址就会被占用,而RAM板着转到更高的地址。

命令处理程序的使用极大地推动了计算机的发展,通过它不仅可以更快地向存储器输入数据,更重要的是,计算机变得可交互(interactive)了。当你通过键盘输入一些内容,计算机会立即做出响应,把你所输入的内容显示出来。

将命令处理程序存储到ROM后,就可以执行操作了:把内存中的数据写入到磁盘驱动器(可能是按照与磁盘的扇区大小一致的块的形式),然后再把数据读回到内存中。因为掉电的时候,RAM中的内容会丢失,所以与RAM相比,把程序和数据存储到磁盘中会更安全(它们不会因为突然断电而丢失数据),就灵活性而言,也比存储到ROM中要好。

仅仅有上面的命令还是不够的,还需要向命令处理程序中添加新命令。例如,S命令表示存储(Store):

S 2080 2 15 3

运行这条命令后,在磁盘的第2面,第15道,第3扇区中将存放起始地址为2080h的内存块数据(被存放内存块的大小是由磁盘扇区的大小决定的)。

类似地,还可以通过加载(Load)命令,把磁盘上相应扇区的内容写回内存中,如下所示:

L 2080 2 15 3

当然,还要把存储的位置纪录下来,这时必须要做的。需要注意的是:你不能把位于某个地址处的代码又加载道内存的另外一个地址中,如果这样的话,程序将不能正常运行。具体来说,程序代码在内存中改变位置后,其跳转(Jump)和调用(Call)指令标识的依然是原来的地址,所以运行时会报错。也存在这样的请看,程序比磁盘的扇区大,这时就需要多个扇区来存放程序。而磁盘上某些扇区已被其他程序或数据占用了,而另外一些扇区是空闲的,可能在磁盘上找不到一块足够大的,连续的扇区来存放数据。

文件系统

最后,所有的东西存储在磁盘的什么位置,都需要你手工的纪录下来,这个工作挺多,也挺麻烦。出于这个原因,文件系统(file system)应运而生。

文件系统是磁盘存储的一种方法,就是数据组织成文件(file)。简单地所,文件是相关数据的集合,占用磁盘上一个或多个扇区。更重要的是,你可以为每个文件命名,这有助于记下文件里存放的内容。想象一下,磁盘是不是类似于一个文件柜,每个文件都由个小标签,标签上有文件的名称。

操作系统

操作系统(operating system)是许多软件构成的庞大程序集合,文件系统就是其中的一部分。前面讲到的键盘处理程序和命令处理程序最终也能经过拓展,演变成为操作系统。那么操作系统到底能够做些什么,又是如何工作的呢?这里撇开操作系统漫长的发展演化过程,把重点放在刚才提出的问题上,目的就是试图让大家了解操作系统的工作机制。

CP/M

如果你对操作系统的发展史有一定了解的话,你就会知道CP/M(Control Program for Micros)是最重要的8位微处理器操作系统,它是20世纪70年代中期由加里·基尔代尔(Gary Kildall,生于1942年)专门为Intel 8080微处理器而开发的,加里后来成了DRI(Digital Research Incorporated)公司的创始人。

存放文件

CP/M操作系统是存放在磁盘上的。单面,8英寸的磁盘是早期的CP/M最常用的存储介质,它有77个磁道,每个磁道有26个扇区,每个扇区的大小是128个字节(中共算下来共有256,256个字节),CP/M系统存放在磁盘最开始的两个磁道。在启动计算机时,需要把CP/M从磁盘调入道计算机的内存中,下面将介绍这一过程是如何进行的。

上面我们讲过,存放CP/M本身只占用2个磁道,那么剩下的75个磁道用来存储文件。CPM的文件系统固然简单,但两个最基本的要求还是可以满足的。首先,每个文件在磁盘中都有属于自己的名字,便于识别,这个名字也是存放在磁盘中的;实际上,文件以及读取这些文件需要的所有信息也是一起存储在磁盘中的。其次,文件存储在磁盘中不一定要占据连续的扇区空间,可以想象,由于大小不同的文件会被经常地创建和删除,磁盘上的可用空间就会很零碎。那么如果将文件存储在零碎的空间里呢?这主要得益于文件系统具有强大的管理功能,它可以把一个大文件分散存储在不连续的磁盘上。

上面提到过剩下的75个磁道中的扇区用来存放文件,这些扇区按分配块(allocation blocks)进行分组。每个分配块中有8个扇区,总计1024字节(每个扇区大小是128字节)。可以计算,在磁盘上共有243个分配块,编号为0~242.

目录(directory)区占用最开始的两个分配块(编号0和1,总共2048字节)中。目录区是磁盘中的一个非常重要的区域,磁盘文件中每个文件的名字和其他一些重要信息都存储在该区域,根据目录就能够很方便,高效地查找文件。存放目录也需要占用空间,磁盘让每个文件对应的目录项(directory entry)大小均为32字节,由于目录区大小为2048字节,所以这个磁盘上最多存放64(2048/32)个文件。

为了能够根据目录找到相应的文件,每一个32字节的目录项包含以下信息。

字节 含义
0 通常设为0
1~8 文件名
9~11 文件类型
12 文件扩展
13~14 保留(设为0
15 最后一块扇区数
16~31 磁盘存储表

在目录项中,第一个字节用来设置文件的共享属性,只有文件系统被两个或更多人同时共享时才设置此字节为1.在CP/M中,这个字节跟第13,14字节一样,通常设置为0.

CP/M中每个文件的文件名由两部分构成,第一部分称为文件名(filename),文件名最多由8个字符构成,目录项的第1~8字节用来存储文件名。第二部分称为文件类型(file type),最多由3个字符表示,这些字符存储在目录项的第9~11字节中。我们会经常遇到一些常见的标准文件类型,例如:TXT表示文本文件(此文件只包含ASCII码);COM(command的介些)表示这个文件存放的是8080机器码指令,或者说是一段程序。当命名一个文件时,通过一个点来隔开这两部分,如下所示:

MYLETTER.TXT
CALC.COM

人们习惯形象地称这种命名方式为8.3,就是说在点号隔开的前半部分最多有8个字母,后半部分最多有3个字母。

接下来介绍一下如何利用目录项查找文件。目录项中的第16~31字节是磁盘存储表,它能够标明文件存放的分配块。假设磁盘存储表的前4项分别为14h,15h,07h,23h,其余项全为0,这表明文件占用了4个分配块的空间,大小为4KB,而实际上文件可能并没有用完4KN空间,因为最后一个分配块往往只有部分扇区被使用。目录项的第15字节表明最后一个分配块到底用道了多少个128字节的扇区。

磁盘存储表的长度为16字节,最多可以容纳16,384字节(16KB)的文件如果文件的长度超过16KB,就需要用多个目录项来表示,这种方法称为扩展(extents)。如果一个大文件使用目录项扩展来,则将第一个目录项的第12字节设置为0,第二个目录项的第12个字节则设置为1,依此类推。

文本文件与二进制文件

文本文件对我们并不陌生,通常也称为ASCII文件(ASCII file),纯文本文件(text-only file)或纯ASCII文件(pure-ASCII file),当然还有其他一些类型的名称。ASCII码(包括换行符和回车符)是文本文件中唯一包含的字符,文本文件通俗易懂,便于浏览。除了文本文件外,其余的文件称为二进制文件(binary file),在CM/P中,COM文件存放的是二进制的8080机器码,它是二进制文件。

假设一个小文件中包含了三个16位数,如:5A48h,78BFh和F510h。如果此文件是二进制文件,只要6字节就可以了。

48 5A BF 78 10 F5

这些采用Intel格式来存储的,放在前面的是低位,放在后面的是高位。并不是所有的数据都是按照这种格式来存储的,例如为 Motorola 处理器编写的程序更倾向于按以下的方式来组织文件:

5A 48 78 BF F5 10

假如上面的三个16位数用ASCII文本文件来存放,则文件中保存的数据如下所示:

35 41 34 38 68 0D 0A 37 38 42 46 68 0D 0A 46 35 31 30 68 0D 0A

上面的这些字节是数字和字母的ASCII码表示形式,用回车符(0Dh)和换行符(0Ah)来表示每一个数结束。因为文本文件可以不通过解释相应的ASCII字节串来显示字符,而是将字符本身直接显示,所以显得更加方便,如下所示。

5A48h
78BFh
F510h

也可以用如下的形式来表示包含这三个数的ASCII文本文件:

32 33 31 31 32 0D 0A 33 30 39 31 31 0D 0A 36 32 37 33 36 0D 0A

这是用是金子数的ASCII码形式来表示上述三个数,这两种表示形式是等价的,如下所示:

23112
30911
62736

显然,文本文件更易于人们阅读,同样,与十六进制相比,十进制更符合人们的习惯,没有理由使用十六进制而拒绝十进制。

操作系统的引导与CP/M的内存结构

前面曾提到过,磁盘最开始的两个磁道存储CP/M系统本身,而CP/M在磁盘上是无法运行的,必须将其加载到内存里。只读存储器(ROM)在CP/M 计算机中使用得并不多,只需要用它来存放一小段称为引导程序(bootstrap loader,操作系统的其余部分引用)的代码即可。开机启动时,磁盘上最开始的 128 字节的扇区内容,会首先由引导程序加载到内存并运行,这个扇区包含有特定的代码,可以把CP/M 中的其余部分加载到内存中,整个过程称为操作系统的引导(booting)。

操作系统的引导过程完成后,随机存储器(RAM)的最高地址区域用来存放CP/M,加载完CP/M后,整个内存空间的组织结构如下所示。

该图仅仅粗略地表示出了内存各构成部分,没有按比例刻画各部分所占内存的大小。控制台命令处理程序(Console Command Processor,CCP),基本磁盘操作系统(Base Disk Operating System,BDOS)和基本输入/输出系统(Basic Input/Output System, BIOS)是CP/M的三个组成部分,这三个部分只占用了6KB大小的内存空间。在拥有64KB的内存空间的计算机中,大约58KB被临时程序区(Transient Program Area,TPA)占用,但是这58KB空间一开始时是空的。

控制台命令处理程序

控制台命令程序的功能和以前讨论过的命令处理程序是一样的。在这里,键盘和显示器组成了控制台(console)。控制台命令处理程序显示如下所示的命令提示符(prompt):

A>

在命令提示符后面可以输入一些信息。大多数计算机可能不止一个磁盘驱动器,第一个磁盘驱动器标为A,CP/M 被加载到该驱动器中。在命令提示符后面敲入命令并按回车(Enter)键,控制台命令处理程序会执行该命令,然后将执行的结果显示到屏幕上。执行完命令后,命令提示符又会显示在屏幕上,等待下一次输入。

控制台命令处理程序只能识别一部分命令,其中最重要的命令是:

DIR

该命令用于显示磁盘的目录信息,换句话说,这列出了存储在磁盘上的所有文件。然而有时候需要查看具有特定名字和类型的文件,这时候就可以在命令中使用像”?” 和”*“这样的特殊字符来限定。如果想显示当前目录下所有的文本文件,可以使用如下指令:

DIR *.TXT


DIR A???B.*
则显示所有文件名由5各字符构成,其中第一个字符是A,最后宇哥字符是B的文件。

如果想删除磁盘中的文件,要用到的命令ERA,它是Erase的缩写,例如:

ERA MTLETTER.TXT

用来删除名为MYLETTER.TXT的文件,而运行下面的命令:

ERA *.TXT

则所有的文本文件都被删除。一旦删除文件,此文件的目录项及其所占用的磁盘空间都被释放。

REN 也是一个常用命令,它是Rename 的缩写,此命令可以改变文件名。

如果想显示文本文件的内容,可以使用TYPE命令。这条命令的功能还不仅如此,要知道文本文件中包含ASCII码,所以屏幕上文件的内容也可以通过这条命令来浏览,例如:

TYPE MYLETTER.TXT

表示查看MYLETTER.TXT文件的内容。SAVE命令用来保存文件,它可把临时存储区域中的一个或多个256字节的内存保存到磁盘中,并且给这个内存块指定一个名字。

COM文件

当然,上述所介绍的都是CP/M可识别的命令,如果你输入一个不能被CP/M识别的命令,CP/M就会默认认为输入的是保存在磁盘上的一个程序名。而程序通常是以文件形式存储的,其文件类型为COM,代表着命令。控制台命令处理程序负责在磁盘上查找此文件,如果找到,此文件会被CP/M从磁盘加载到临时程序区域,该区域的地址从0100h开始,一旦文件被调入内存即可运行。上面从流程的角度介绍了磁盘中的文件是如何被运行的,下面举个例子来说明。假如在CP/M命令提示符后面输入:

CALC

如果在磁盘中存在名为CALC.COM的文件,则该文件会被控制台命令处理程序调入到以地址0100h开始的内存中,接着控制台命令处理程序会转到该地址并执行这段程序。

前面介绍了如何将机器码指令插入到内存空间的任意位置并执行。但是存储在磁盘上的CP/M程序并不能被随意存放到内存中的任意位置,它必须被加载到指定的位置,在这里是以0100h开始的内存空间。

CP/M由一些实用的程序组成,如PIP(Peripheral Interchange Program),即外设交换程序,通过它可以复制文件。ED是文本编辑器,可以创建和修改文本文件。在CP/M系统中,有很多像PIP和ED一样的程序,他们很小但是可以完成简单的事务处理,这类程序称为实用(utility)程序。仅仅有这些小的程序是远远不够的,还有一些大的商业化应用程序(application),比如字处理软件或计算机电子报表软件等,在CP/M系统中,这些软件的使用会给你的工作带来很大方便。当然,如果你是一个程序开发高手,也可以自动动手编写这些软件,这些程序的存储类型都是COM文件类型。

应用程序接口

在CP/M(跟许多操作系统一样)中,我们了解了很多内容,比如如何利用命令和实用程序对文本进行基本操作,如何将程序加载到内存中运行等。操作系统的功能远远不止如此,下面将介绍它的第三个功能。

前面提到过,把输出信息写到视频显示器上,或者从键盘读取输入内容,或者读/写磁盘中的文件,这些都是运行在CP/M下的程序常常要做的操作。这就需要CP/M程序能够直接向视频显示器的内存写入输出内容,也需要CP/M程序能够访问磁盘硬件来捕获所输入的内容,还需要CP/M程序能够访问磁盘驱动器来读/写磁盘扇区,然而在通常情况下程序本身是很难直接做到的。

那么有没有别的办法来实现上述的要求呢?答案是肯定的,这些常用事务由CP/M中的子程序集来完成,在CP/M下运行的程序通过调用这些子程序即可完成相应的操作。这些子程序都是专门设计的,计算机中的所有硬件都可以很容易地通过它们来访问,如视频显示器,键盘,磁盘驱动器等,而程序员无须关心这些外设实际是如何连接的。更重要的是,像磁道,扇区这类信息,程序没有必要知道,这些工作都是由CP/M来完成的,它可以负责读/写磁盘上的文件。

操作系统提供的第三个主要功能是让程序能够方便地访问计算机的硬件,操作系统提供的这种访问操作称为API(Application Programming Interface),即应用程序接口。

那么如何设置和使用API呢?在CP/M下运行的程序,可以通过将寄存器C设置为特定的值(称为功能值),并且运行如下指令:

CALL 5

来使用API。例如,你从键盘上按下一个键,程序会通过执行下面的指令来获取此键对应的ASCII码:

MVI C, 01h
CALL 5

并且将这个键的ASCII码值保存在累加器A中。类似的,运行这条命令:

MVI C, 02h
CALL 5

在视频显示器上当前的光标位置将显示累加器A中的ASCII码字符,然后光标移到下一个位置。

如果程序要新建一个文件,它首先将文件名所在区域的地址保存在寄存器对DE中,接着执行如下代码:

MVI C, 16h
CALL 5

执行此命令,CP/M会在磁盘上新建一个空文件。程序可以利用CP/M提供的其他功能来向空文件中写入内容,最后关闭(close)文件,关闭文件意味着文件使用完毕,不需要在对该文件执行任何操作了。当然,该文件可以再次被此程序和其他程序打开(open)并读取内容。

CALL 5指令到底如何工作呢?CP/M在内存中地址为0005h处设置了一条JMP(Jump)指令,它跳转到CP/M基本磁盘操作系统(Basic Disk Operating System,BDOS)中的某个位置。这个区域包含许多小程序,CP/M的每一项功能都可由它们完成。BDOS,顾名思义,它的主要功能是维护磁盘上的文件系统。文件系统经常要与终端设备打交道,所有BDOS经常要调用CP/M基本输入/输出系统(Basic Input Output System,BIOS)中的一些子程序。这里顺便一提,BIOS可以对硬件进行访问,比如键盘,视频显示器和磁盘驱动器等。实际上,BIOS是CP/M中唯一需要了解计算机中硬件的程序,其他一些对硬件的操作都可通过调用BIOS中的子程序来实现。控制台命令处理程序通过调用BDOS的子程序来实现自己所有的功能,而在CP/M中运行的程序也是这样。

对于计算机硬件来说,API是一个与设备无关(device-independent)的接口。换言之,对于特定的机器上键盘的工作机制,视频显示器的工作机制以及磁盘扇区的读/写 机制,在CP/M下编写的程序不需要知道也没有必要知道。程序使用CP/M提供的功能便可完成对键盘,视频显示器和磁盘驱动器操作,简言之,API屏蔽了硬件之间的差异。有了API,尽管不同计算机硬件差别很大,其访问外设的方式也不尽相同,但CP/M程序都可以在上面运行,从而实现了CP/M程序的跨平台(这里要求所有CP/M程序恩许必须运行在Intel 8080微处理器上,或者运行在能执行Intel 8080指令的处理器上,例如Intel 8085或Zilog Z-80处理器)。所有,在使用CP/M系统的计算机中,程序对硬件访问是通过CP/M来实现的。如果没有标准的API,程序必须根据不同型号的计算机进行相应的修改后,才能访问硬件。

MS-DOS

CP/M曾经是8080中非常流行的操作系统,至今仍具有重要的历史意义。CP/M对其后的16位操作系统QDOS(quick and dirty operating system)有很大的影响。QDOS是西雅图计算机产品公司(seattle computer products)的TimPaterson为Intel的16位8086和8088芯片而编写的。QDOS后来改名为86-DOS,由Microsoft公司注册。该操作系统被授权给IBM以MS-DOS(Microsoft Disk Operating System)这个名称用于第1代IBMPC机。尽管CP/M的16位版本(称为CP/M-86)也可用于IBMPC,但MS-DOS很快成了标准。MS-DOS(在IBM计算机上叫PC-DOS)也允许其他生产IBMPC兼容机的厂商使用。

CP/M的文件系统在MS-DOS没有被继续使用,在MS-DOS中,文件系统是以文件分配表(FAT,File Allocation Table的简写)的形式来组织的,Microsoft公司早在1977年就开始使用FAT了。FAT的基本思想是:将磁盘空间分成簇(cluster)——簇的大小由磁盘空间的大小来决定——从512直接到16K字节不等,每个文件占用若干簇。文件的目录项只纪录文件起始(starting)簇的位置,而磁盘上每一簇的下一簇的位置由FAT来记录。

每个目录项在MS-DOS磁盘上占用32字节,其命名形式跟CP/M上的8.3形式一样,只是使用的术语有些区别:最后的三个字符称作文件扩展名,而非CP/M中的文件类型。MS-DOS目录项中没有包含分配块列表,它主要包含如下所示的有用信息:文件的最后修改的日期,时间和文件的大小等。

实际上,MS-DOS的早期版本与CP/M在结构上很类似,只是IBM PC本身在ROM中已经包含了一套完整的BIOS了,所以在MS-DOS中不再需要BIOS。在MS-DOS中,命令处理器是一个命名为COMMAND.COM的文件。MS-DOS有两种运行程序:一是以COM为扩展名的文件,其大小不能超过64KB,二是更大一些的程序,以EXE(意思是可以被执行)为文件的扩展名,表明文件本身是可执行的。

尽管MS-DOS起初支持API函数的CALL5接口,但不久Microsoft为新的程序重新设计了一款新的接口。这个新的接口使用了8086的一个称为软件中断(software interrupt)的功能,说起软件中断,它与子程序调用很类似,只不过程序不必知道它所调用的子程序的确切地址。通过执行INT 21h这条指令,程序可以调用MS-DOS的API功能。

从理论上来讲,应用程序并不能直接访问计算机的硬件,如果它要访问计算机的硬件,可以通过操作系统提供的接口来进行。然而实际上,在20世纪70年代末和80年代初,由于计算机上运行的都是小型操作系统,许多应用程序往往都绕过它们,这种情况在处理有关视频显示器任务的时候显得特别明显。为什么会这样呢?因为如果程序把直接直接写入视频显示存储器,它的执行速度要远快于不直接写入视频显示存储器。实际上,对于一些程序——比如要将图形显示在视频显示器上——操作系统就显得“心有余而力不足”,这就需要在操作系统之外另作处理。也许正是因为MS-DOS系统卓越的“反传统性”,使得大多数程序员都很热衷于它,它可以让程序员编写的程序最大限度地达到硬件地最快速度,更加充分地发挥硬件的性能。

恰恰是这个原因,运行在IBM PC上的流行软件通常依赖于IBM PC这个硬件平台,换句话说,就是这些软件是根据IBM PC的硬件特点编制的。为了和IBM PC竞争,其他机器制造商不得不沿袭这些特点。如果不这样做的话,再流行的软件也将流行不起来,因为它不能在这些机器上运行。所以在软件的显著位置往往会出现“与IBM PC百分百兼容”的字样,这同时体现了软件对硬件的要求。

微软于1983年3月发布了MS-DOS 2.0版本,与最开始的版本相比,它加强了对硬盘驱动器的管理。虽然当时的磁盘容量很小(按今天的标准),但是发展很快,没经过多久,磁盘的容量变得越来越大。这带来了不少问题,磁盘容量越大存储的文件也就越多,存储的文件越多,当然,查找某个指定的文件或组织文件也就越困难。

为了解决上述问题,MS-DOS 2.0引入了层次文件系统(hierarchical file system),它只是在原有MS-DOS的文件系统上做了一些小的改动。前面介绍过,目录存储在磁盘中特定的区域,它是一个文件列表,包含了文件存储在磁盘位置的信息。在层次文件系统中,有些文件其本身可能就是目录,也就是说这些文件包含其他文件,其中的一些可能还是目录。在磁盘中,目录的称呼也是有讲究的,常规的目录称为根目录(root directory),子目录(subdirectories)是包含在其他目录里的目录。有了目录(有时称为文件夹,folder),就可以很方便地对相关文件进行分组,所以目录在磁盘文件的管理中起着非常重要的作用。

UNIX

讲到操作系统,我们不能不提到著名的UNIX系统,它在操作系统的发展史上有着举足轻重的地位。实际上,层次文件系统和MS-DOS 2.0的其他很多功能都是从UNIX操作系统借鉴而来的。UNIX操作系统是20世纪70年代初在贝尔实验室开发的,肯·汤普森(Ken Thompson,生于1943年)和丹尼斯·里奇(Dennis Ritchie,生于1941年)是此系统的主要开发者。UNIX系统(包括名字本身)的发展历程在这里要提一下,UNIX系统来源于早期的Multics(Multiplex Information and Computing Services,表示多路复用信息和计算业务),Multics是贝尔实验室和MIT和GE(通用电气公司)合作开发的一个项目,开发初期由于Multics没能达到预定的目的而切进度缓慢,加之昂贵的开发代价,1969年贝尔实验室退出了该项目,但肯·汤普森和丹尼斯·里奇继续对此进行开发,为了区别于Multics,取名为Unix。

对于计算机核心程序开发的精英们来说,UNIX无疑是最受欢迎的操作系统。以前大部分操作系统是针对特定的计算机硬件平台开发的,但UNIX打破了常规,它不针对具体的计算机硬件平台,具有很好的可移植性(portable),也就意味着它在各种机器上都可以运行。

在开发UNIX的时候,贝尔实验室是电信业巨头 AT&T (American Telephone & Telegraph,美国电话电报公司)旗下的一员,为了限制AT&T在电话业务的垄断地位,法院裁定禁止AT&T销售UNIX系统,AT&T被迫将它授权给别人。因此从1973年初,很多大学,公司和政府机构被授权使用和研究UNIX,这在一定程度上促进了UNIX的发展。1983年,AT&T获准重返计算机业务并发布了它自己的UNIX版本。

由于UNIX的广泛授权导致了许多不同的版本共存的情况,它们使用着不同的名字,运行在不同的计算机上,并由不同的经销商销售。由于UNIX的影响力和开源性,使得很多人将精力投入到UNIX中并推动者它的不断发展。当人们向UNIX中添加新的组件时,无形之中流行着一种不成文的“UNIX思想”。在这种思想的指导下,人们都适用文本文件作为公用文件形式。许多UNIX使用程序读取文本文件,利用它提供的一些功能做些处理,然后将其写入另一个文本文件。因此可以用链的形式来组织UNIX实用程序,然后对这些文本文件做相应的处理。

在20世纪60-70年代,计算机不仅体积庞大而切价格昂贵,仅仅为一个人服务显然不太现实,为了能够让多个人同时使用计算机,必须有相应操作系统来支持,这也就是开发UNIX系统的最初目的。使用UNIX系统的计算机通过时分复用(time sharing)技术——这种技术允许多个用户同时于计算机进行交互——来达到这个目的。计算机链接多个配备了显示器和键盘的终端(terminals),每个用户通过这些终端访问计算机。通过在所有终端间的快速切换,使用户感觉这台计算机似乎只为自己工作,而其实计算机同时在为多个用户服务。

如果在一个操作系统上同时运行多个程序,则称此系统为多任务(multitasking)操作系统。显然,与CP/M和MS-DOS 这样的单任务的系统相比,这种操作系统要复杂得多。正是由于支持多任务这种功能,文件系统变得很复杂,因为同一个文件可能被多个用户同时访问。程序得运行需要占用内存空间,多任务系统就要考虑内存的分配问题,也就是所需要进行内存管理(memory management)。也许你会有疑问,多道程序并行运行需要占用大量内存,如果内存不够怎么办?为此,操作系统引入了虚拟内存(virtual memory)技术。虚拟内存是指,在磁盘上划出部分空间用来保存临时文件,程序把暂时不需要用的内存块放到临时文件里,待需要时再把它调入内存。

UNIX 能够存在并发展到现在时无数人共同努力的结晶,如今FSF(Free Software Foundation,自由软件基金会)和GNU项目为推动UNIX的发展注入了新的活力,它们都是由理查德·斯塔门(Richard Stallman)创建的。CNU意味着:“GNU与UNIX,既要划清界限又相辅相成。”GNU项目的宗旨是:创建一个与UNIX系统兼容,但不受私有权限制的操作系统和开发环境。在这个项目的推动下,涌现出许多和UNIX兼容的实用程序和工具,其中最著名的要算Linux系统。Linux系统的内核(Core与Kernel都可以表示内核的意思)和UNIX的是完全兼容的,它的大部分程序是由芬兰的李纳斯·托沃兹(Linus Torvalds)完成的。由于Linux系统的开源性,近年来已成为非常流行的操作系统。

从20世界80年代中期,开发大型的,复杂度更高的系统成为了操作系统发展的一大趋势,例如苹果公司的Macintoush系统和微软的Windows系统,它们融合了图形和高级可视化显示技术,使应用程序变得更易于使用。关于图形和可视化方面的发展趋势,我们将在本书的最后一章进一步介绍。

Loading Disqus comments...
Table of Contents