操作系统博物馆
操作系统已经存在了大半个世纪,在这段时期内,出现了各种类型的操作系统,但并不是所有的操作系统都很出名,下面就罗列一些比较出名的操作系统
大型机操作系统
高端一些的操作系统是大型机操作系统,这些大型操作系统可在大型公司的数据中心找到。这些计算机的 I/O 容量与个人计算机不同。一个大型计算机有 1000 个磁盘和数百万 G 字节的容量是很正常,如果有这样一台个人计算机朋友会很羡慕。大型机也在高端 Web 服务器、大型电子商务服务站点上。
服务器操作系统
下一个层次是服务器操作系统。它们运行在服务器上,服务器可以是大型个人计算机、工作站甚至是大型机。它们通过网络为若干用户服务,并且允许用户共享硬件和软件资源。服务器可提供打印服务、文件服务或 Web 服务。Internet 服务商运行着许多台服务器机器,为用户提供支持,使 Web 站点保存 Web 页面并处理进来的请求。典型的服务器操作系统有 Solaris、FreeBSD、Linux 和 Windows Server 201x
多处理器操作系统
获得大型计算能力的一种越来越普遍的方式是将多个 CPU 连接到一个系统中。依据它们连接方式和共享方式的不同,这些系统称为并行计算机,多计算机或多处理器。他们需要专门的操作系统,不过通常采用的操作系统是配有通信、连接和一致性等专门功能的服务器操作系统的变体。
个人计算机中近来出现了多核芯片,所以常规的台式机和笔记本电脑操作系统也开始与小规模多处理器打交道,而核的数量正在与时俱进。许多主流操作系统比如 Windows 和 Linux 都可以运行在多核处理器上。
个人计算机系统
接下来一类是个人计算机操作系统。现代个人计算机操作系统支持多道处理程序。在启动时,通常有几十个程序开始运行,它们的功能是为单个用户提供良好的支持。这类系统广泛用于字处理、电子表格、游戏和 Internet 访问。常见的例子是 Linux、FreeBSD、Windows 7、Windows 8 和苹果公司的 OS X 。
掌上计算机操作系统
随着硬件越来越小化,我们看到了平板电脑、智能手机和其他掌上计算机系统。掌上计算机或者 PDA(Personal Digital Assistant),个人数字助理
是一种可以握在手中操作的小型计算机。这部分市场已经被谷歌的 Android
系统和苹果的 IOS
主导。
嵌入式操作系统
嵌入式操作系统用来控制设备的计算机中运行,这种设备不是一般意义上的计算机,并且不允许用户安装软件。典型的例子有微波炉、汽车、DVD 刻录机、移动电话以及 MP3 播放器一类的设备。所有的软件都运行在 ROM 中,这意味着应用程序之间不存在保护,从而获得某种简化。主要的嵌入式系统有 Linux、QNX 和 VxWorks
传感器节点操作系统
有许多用途需要配置微小传感器节点网络。这些节点是一种可以彼此通信并且使用无线通信基站的微型计算机。这类传感器网络可以用于建筑物周边保护、国土边界保卫、森林火灾探测、气象预测用的温度和降水测量等。
每个传感器节点是一个配有 CPU、RAM、ROM 以及一个或多个环境传感器的实实在在的计算机。节点上运行一个小型但是真是的操作系统,通常这个操作系统是事件驱动的,可以响应外部事件。
实时操作系统
另一类操作系统是实时操作系统,这些系统的特征是将时间作为关键参数。例如,在工业过程控制系统中,工厂中的实时计算机必须收集生产过程的数据并用有关数据控制机器。如果某个动作必须要在规定的时刻发生,这就是硬实时系统
。可以在工业控制、民用航空、军事以及类似应用中看到很多这样的系统。另一类系统是 软实时系统
,在这种系统中,虽然不希望偶尔违反最终时限,但仍可以接受,并不会引起任何永久性损害。数字音频或多媒体系统就是这类系统。智能手机也是软实时系统。
智能卡操作系统
最小的操作系统运行在智能卡上。智能卡是一种包含一块 CPU 芯片的信用卡。它有非常严格的运行能耗和存储空间的限制。有些卡具有单项功能,如电子支付;有些智能卡是面向 Java 的。这意味着在智能卡的 ROM 中有一个 Java 虚拟机(Java Virtual Machine, JVM)解释器。
操作系统概念
大部分操作系统提供了特定的基础概念和抽象,例如进程、地址空间、文件等,它们是需要理解的核心内容。下面我们会简要介绍一些基本概念,为了说明这些概念,我们会不时的从 UNIX
中提出示例,相同的示例也会存在于其他系统中,我们后面会进行介绍。
进程
操作系统一个很关键的概念就是 进程(Process)
。进程的本质就是操作系统执行的一个程序。与每个进程相关的是地址空间(address space)
,这是从某个最小值的存储位置(通常是零)到某个最大值的存储位置的列表。在这个地址空间中,进程可以进行读写操作。地址空间中存放有可执行程序,程序所需要的数据和它的栈。与每个进程相关的还有资源集,通常包括寄存器(registers)
(寄存器一般包括程序计数器(program counter)
和堆栈指针(stack pointer)
)、打开文件的清单、突发的报警、有关的进程清单和其他需要执行程序的信息。你可以把进程看作是容纳运行一个程序所有信息的一个容器。
对进程建立一种直观感觉的方式是考虑建立一种多程序的系统。考虑下面这种情况:用户启动一个视频编辑程序,指示它按照某种格式转换视频,然后再去浏览网页。同时,一个检查电子邮件的后台进程被唤醒并开始运行,这样,我们目前就会有三个活动进程:视频编辑器、Web 浏览器和电子邮件接收程序。操作系统周期性的挂起一个进程然后启动运行另一个进程,这可能是由于过去一两秒钟程序用完了 CPU 分配的时间片,而 CPU 转而运行另外的程序。
像这样暂时中断进程后,下次应用程序在此启动时,必须要恢复到与中断时刻相同的状态,这在我们用户看起来是习以为常的事情,但是操作系统内部却做了巨大的事情。这就像和足球比赛一样,一场完美精彩的比赛是可以忽略裁判的存在的。这也意味着在挂起时该进程的所有信息都要被保存下来。例如,进程可能打开了多个文件进行读取。与每个文件相关联的是提供当前位置的指针(即下一个需要读取的字节或记录的编号)。当进程被挂起时,必须要保存这些指针,以便在重新启动进程后执行的 read
调用将能够正确的读取数据。在许多操作系统中,与一个进程有关的所有信息,除了该进程自身地址空间的内容以外,均存放在操作系统的一张表中,称为 进程表(process table)
,进程表是数组或者链表结构,当前存在每个进程都要占据其中的一项。
所以,一个挂起的进程包括:进程的地址空间(往往称作磁芯映像
, core image,纪念过去的磁芯存储器),以及对应的进程表项(其中包括寄存器以及稍后启动该进程所需要的许多其他信息)。
与进程管理有关的最关键的系统调用往往是决定着进程的创建和终止的系统调用。考虑一个典型的例子,有一个称为 命令解释器(command interpreter)
或 shell
的进程从终端上读取命令。此时,用户刚键入一条命令要求编译一个程序。shell 必须先创建一个新进程来执行编译程序,当编译程序结束时,它执行一个系统调用来终止自己的进程。
如果一个进程能够创建一个或多个进程(称为子进程
),而且这些进程又可以创建子进程,则很容易找到进程数,如下所示
上图表示一个进程树的示意图,进程 A 创建了两个子进程 B 和进程 C,子进程 B 又创建了三个子进程 D、E、F。
合作完成某些作业的相关进程经常需要彼此通信来完成作业,这种通信称为进程间通信(interprocess communication)
。我们在后面会探讨进程间通信。
其他可用的进程系统调用包括:申请更多的内存(或释放不再需要的内存),等待一个子进程结束,用另一个程序覆盖该程序。
有时,需要向一个正在运行的进程传递信息,而该进程并没有等待接收信息。例如,一个进程通过网络向另一台机器上的进程发送消息进行通信。为了保证一条消息或消息的应答不丢失。发送者要求它所在的操作系统在指定的若干秒后发送一个通知,这样如果对方尚未收到确认消息就可以进行重新发送。在设定该定时器后,程序可以继续做其他工作。
在限定的时间到达后,操作系统会向进程发送一个 警告信号(alarm signal)
。这个信号引起该进程暂时挂起,无论该进程正在做什么,系统将其寄存器的值保存到堆栈中,并开始重新启动一个特殊的信号处理程,比如重新发送可能丢失的消息。这些信号是软件模拟的硬件中断,除了定时器到期之外,该信号可以通过各种原因产生。许多由硬件检测出来的陷阱,如执行了非法指令或使用了无效地址等,也被转换成该信号并交给这个进程。
系统管理器授权每个进程使用一个给定的 UID(User IDentification)
。每个启动的进程都会有一个操作系统赋予的 UID,子进程拥有与父进程一样的 UID。用户可以是某个组的成员,每个组也有一个 GID(Group IDentification)
。
在 UNIX 操作系统中,有一个 UID 是 超级用户(superuser)
,或者 Windows 中的管理员(administrator)
,它具有特殊的权利,可以违背一些保护规则。在大型系统中,只有系统管理员掌握着那些用户可以称为超级用户。
地址空间
每台计算机都有一些主存用来保存正在执行的程序。在一个非常简单的操作系统中,仅仅有一个应用程序运行在内存中。为了运行第二个应用程序,需要把第一个应用程序移除才能把第二个程序装入内存。
复杂一些的操作系统会允许多个应用程序同时装入内存中运行。为了防止应用程序之间相互干扰(包括操作系统),需要有某种保护机制。虽然此机制是在硬件中实现,但却是由操作系统控制的。
上述观点涉及对计算机主存的管理和保护。另一种同等重要并与存储器有关的内容是管理进程的地址空间。通常,每个进程有一些可以使用的地址集合,典型值从 0 开始直到某个最大值。一个进程可拥有的最大地址空间小于主存。在这种情况下,即使进程用完其地址空间,内存也会有足够的内存运行该进程。
但是,在许多 32 位或 64 位地址的计算机中,分别有 2^32 或 2^64 字节的地址空间。如果一个进程有比计算机拥有的主存还大的地址空间,而且该进程希望使用全部的内存,那该怎么处理?在早期的计算机中是无法处理的。但是现在有了一种虚拟内存
的技术,正如前面讲到过的,操作系统可以把部分地址空间装入主存,部分留在磁盘上,并且在需要时来回交换它们。
文件
几乎所有操作系统都支持的另一个关键概念就是文件系统。如前所述,操作系统的一项主要功能是屏蔽磁盘和其他 I/O 设备的细节特性,给程序员提供一个良好、清晰的独立于设备的抽象文件模型。创建文件、删除文件、读文件和写文件 都需要系统调用。在文件可以读取之前,必须先在磁盘上定位和打开文件,在文件读过之后应该关闭该文件,有关的系统调用则用于完成这类操作。
为了提供保存文件的地方,大多数个人计算机操作系统都有目录(directory)
的概念,从而可以把文件分组。比如,学生可以给每个课程都创建一个目录,用于保存该学科的资源,另一个目录可以存放电子邮件,再有一个目录可以存放万维网主页。这就需要系统调用创建和删除目录、将已有文件放入目录中,从目录中删除文件等。目录项可以是文件或者目录,目录和目录之间也可以嵌套,这样就产生了文件系统
进程和文件层次都是以树状的结构组织,但这两种树状结构有不少不同之处。一般进程的树状结构层次不深(很少超过三层),而文件系统的树状结构要深一些,通常会到四层甚至五层。进程树层次结构是暂时的,通常最多存在几分钟,而目录层次则可能存在很长时间。进程和文件在权限保护方面也是有区别的。一般来说,父进程能控制和访问子进程,而在文件和目录中通常存在一种机制,使文件所有者之外的其他用户也能访问该文件。
目录层结构中的每一个文件都可以通过从目录的顶部即 根目录(Root directory)
开始的路径名(path name)
来确定。绝对路径名包含了从根目录到该文件的所有目录清单,它们之间用斜杠分隔符分开,在上面的大学院系文件系统中,文件 CS101 的路径名是 /Faculty/Prof.Brown/Courses/CS101
。最开始的斜杠分隔符代表的是根目录 /
,也就是文件系统的绝对路径。
“出于历史原因,Windows 下面的文件系统以
\
来作为分隔符,但是 Linux 会以/
作为分隔符。
在上面的系统中,每个进程会有一个 工作目录(working directory)
,对于没有以斜线开头给出绝对地址的路径,将在这个工作目录下寻找。如果 /Faculty/Prof.Brown
是工作目录,那么 /Courses/CS101
与上面给定的绝对路径名表示的是同一个文件。进程可以通过使用系统调用指定新的工作目录,从而变更其工作目录。
在读写文件之前,首先需要打开文件,检查其访问权限。若权限许可,系统将返回一个小整数,称作文件描述符(file descriptor)
,供后续操作使用。若禁止访问,系统则返回一个错误码。
在 UNIX 中,另一个重要的概念是 特殊文件(special file)
。提供特殊文件是为了使 I/O 设备看起来像文件一般。这样,就像使用系统调用读写文件一样,I/O 设备也可以通过同样的系统调用进行读写。特殊文件有两种,一种是块儿特殊文件(block special file)
和 字符特殊文件(character special file)
。块特殊文件指那些由可随机存取的块组成的设备,如磁盘等。比如打开一个块特殊文件,然后读取第4块,程序可以直接访问设备的第4块而不必考虑存放在该文件的文件系统结构。类似的,字符特殊文件用于打印机、调制解调起和其他接受或输出字符流的设备。按照惯例,特殊文件保存在 /dev
目录中。例如,/devv/lp 是打印机。
还有一种与进程和文件相关的特性是管道,管道(pipe)
是一种虚文件,他可以连接两个进程
如果 A 和 B 希望通过管道对话,他们必须提前设置管道。当进程 A 相对进程 B 发送数据时,它把数据写到管道上,相当于管道就是输出文件。这样,在 UNIX 中两个进程之间的通信就非常类似于普通文件的读写了。
保护
计算机中含有大量的信息,用户希望能够对这些信息中有用而且重要的信息加以保护,这些信息包括电子邮件、商业计划等,管理这些信息的安全性完全依靠操作系统来保证。例如,文件提供授权用户访问。
比如 UNIX 操作系统,UNIX 操作系统通过对每个文件赋予一个 9 位二进制保护代码,对 UNIX 中的文件实现保护。该保护代码有三个位子段,一个用于所有者,一个用于与所有者同组(用户被系统管理员划分成组)的其他成员,一个用于其他人。每个字段中有一位用于读访问,一位用于写访问,一位用于执行访问。这些位就是著名的 rwx位
。例如,保护代码 rwxr-x--x
的含义是所有者可以读、写或执行该文件,其他的组成员可以读或执行(但不能写)此文件、而其他人可以执行(但不能读和写)该文件。
shell
操作系统是执行系统调用的代码。编辑器、编译器、汇编程序、链接程序、使用程序以及命令解释符等,尽管非常重要,非常有用,但是它们确实不是操作系统的组成部分。下面我们着重介绍一下 UNIX 下的命令提示符,也就是 shell
,shell 虽然有用,但它也不是操作系统的一部分,然而它却能很好的说明操作系统很多特性,下面我们就来探讨一下。
shell 有许多种,例如 sh、csh、ksh 以及 bash等,它们都支持下面这些功能,最早起的 shell 可以追溯到 sh
用户登录时,会同时启动一个 shell,它以终端作为标准输入和标准输出。首先显示提示符(prompt)
,它可能是一个美元符号($)
,提示用户 shell 正在等待接收命令,假如用户输入
date
shell 会创建一个子进程,并运行 date 做为子进程。在该子进程运行期间,shell 将等待它结束。在子进程完成时,shell 会显示提示符并等待下一行输入。
用户可以将标准输出重定向到一个文件中,例如
date > file
同样的,也可以将标准输入作为重定向
sort <file1> file2
这会调用 sort 程序来接收 file1 的内容并把结果输出到 file2。
可以将一个应用程序的输出通过管道作为另一个程序的输入,因此有
cat file1 file2 file3 | sort > /dev/lp
这会调用 cat 应用程序来合并三个文件,将其结果输送到 sort 程序中并按照字典进行排序。sort 应用程序又被重定向到 /dev/lp ,显然这是一个打印操作。