操作系统概念

操作系统和用户程序之间的接口由操作系统提供的一组“扩展指令”定义。这些扩展指令通常被称为系统调用,尽管它们可以通过多种方式实现。

要真正了解操作系统的功能,我们需要仔细参详这些接口。 接口中可用的调用也因操作系统而异(尽管基本概念往往相似)。

因此我们只能在泛泛的概括与仔细而系统的学习之间作出一个选择。

我们选择了后者,这样要做更多的工作,但它可以更深入地了解操作系统的真正功能。在后结章节中,我们将仔细研究UNIX(包括BSD的各种版本)、Linux和MINIX 3中的基本系统调用。为了简单起见,我们只引用MINI 3,但是在大多数情况下,对应的UNIX和Linux系统调用都是基于POSIX的。

在我们研究实际的系统调用之前,有必要先鸟瞰一下minix3,以大致了解操作系统是什么。如上所述,这个概述同样适用于UNIX和Linux。

minix3系统调用大致分为两大类:处理进程的调用,和处理文件系统的调用。现在我们将依次查看每一个。

进程

在minix3和所有操作系统中,进程是一个关键概念。进程基本上是一个正在执行的程序。与每个进程相关联的是它的地址空间,一个内存位置列表,从最小值(通常为0)到最大值(进程可以读写)。地址空间包含可执行程序、程序的数据及其堆栈。

与每个进程相关联的还有一组寄存器,包括程序计数器、堆栈指针和其他硬件寄存器,以及运行程序所需的所有其他信息。

我们将在后续章节中更详细地讨论进程的概念,但目前,获得进程直观感觉的最简单方法是考虑多道程序设计系统。例如,操作系统会定期停止运行一个进程并开始运行另一个进程,因为第一个进程在过去一秒钟中,占用的CPU时间达到了它所分配的份额。

当一个进程像这样被暂时挂起,在它被重新启动时必须与停止时的状态完全相同,这意味着有关进程的所有信息,必须在挂起期间显式地保存在某个地方。例如,进程可能同时打开几个文件进行读取。

与这些文件相关联的是一个指针,它给出了当前的位置(即接下来要读取的字节或记录的数量)。当进程暂时挂起时,必须保存这些指针,以便在重新启动进程后执行读取调用,能读取到正确的数据。

在许多操作系统中,关于每个进程的信息,除了进程的地址空间之外,都存储在一个称为“进程表”的操作系统表中,它的结构是一个数组(或链表),每个结构对应一个当前存在的进程。

因此,(挂起的)进程由它的地址空间(通常称为核心映像[以纪念过去使用的磁芯存储器]),和它的处理表条目(其中包含寄存器)组成。

进程管理的关键是在系统调用时,对进程进行创建与终止。考虑一个常见的例子,一个称为命令解释器或shell的进程从终端读取命令。用户刚刚输入了一个命令,要求编译一个程序。shell现在必须创建一个运行编译器的新进程。当该进程完成编译时,它执行一个系统调用来终止自已。

在Windows和其他具有GUI的操作系统上,(双击)桌面图标启动程序的方式,与在命令提示符下键入其名称的方式非常相似。 虽然我们不会过多讨论GUI,但它们实际上是非常简单的命令解释器。

如果一个进程可以创建一个或多个其他进程(通常称为子进程),而这些进程又可以创建子进程,那么我们很快就会得到图1-5所示的进程树结构。合作完成某些工作的相关进程,通常需要彼此通信并同步它们的活动。这种通信称为进程间通信,将在后面章节详细讨论。

操作系统进程

其他进程的系统调用可用于请求获取更多内存(或释放未使用的内存),等待一个子进程结束,再用另一个程序覆盖它。

有时候,需要将信息传递给正在运行的进程,而不是坐在那里等待它,与不同计算机上的进程进行通信,进程需要通过网络向远程计算机上的进程,发送消息来完成通信。

当指定的秒数到达时,操作系统向进程发送警报信号,这个信号导致进程暂时被挂起,它的寄存器被保存在堆栈上,并开始运行一个特殊的信号处理程序,例如,重传可能丢失的消息。

当信号处理程序完成时,进程在信号之前的状态下重新被启动,信号是软件模拟硬件中断。每个授权使用Minix 3系统的人都被系统管理员分配一个UID(用户标识)。启动的每个进程都有启动者的UID。子进程与其父进程具有相同的UID。用户可以是组的成员,每个组都有一个GID(组标识)。

一个称为超级用户的UID(在UNIX中)具有特殊的功能,可能违反许多保护规则。在大型安装中,只有系统管理员知道成为超级用户所需的密码,但是许多普通用户(特别是学生)花费了很大的精力来寻找系统中的缺陷,使他们能够在没有密码的情况下成为超级用户。

文件系统

另一类常见的系统调用与文件系统有关,如前所述,操作系统的一个主要功能是隐藏磁盘和其他I/O设备的特征,并向程序员提供一个干净友好独立于设备的文件抽象模型。显然,创建文件、删除文件、读取文件及写入文件都需要系统调用。在读取文件之前,必须打开它,并且在读取文件之后,要关闭它,因此操作系统提供了执行这些操作的接口。

为了提供一个保存文件的地方,MINIX 3引入了目录的概念,作为一种对文件分组的方法,例如,一个学生可能有一个目录用于他正在学习的每门课程(用于该课程所需的程序),另一个目录用于他的电子邮件,还有一个目录用于他的万维网主页。然后通过系统调用来创建和删除目录。还提供了将现有文件放入目录,和从目录中删除文件的调用。目录条目可以是文件或其他目录。该模型产生了如图1-6所示的层级化文件系统。

操作系统文件系统结构

进程和文件层级结构都以树的形式组织,但相似之处仅限于此。进程层级结构通常不是很深(通常不超过三个层级),而文件层级结构通常是4、5甚至更多。进程层级结构通常是短暂的,通常最多几分钟,而目录层级结构可能存在多年。进程和文件的所有权和保护也不同。通常,只有父进程可以控制甚至访问子进程,但是目录通常不仅仅是只有所有者可以读取。

目录层级结构中的每个文件,都可以从目录层级结构的顶部根目录指定其路径,这样的绝对路径包含,必须从根目录遍历才能到达文件的目录列表,并以斜杠分隔构成。在图1-6中,CS101文件的路径是/Faculty/Prof.Brown/Courses/CS101。前斜杠表示路径是绝对的,即从根目录开始。顺便说一下,在Windows中,反斜杠(\)字符被用作分隔符而不是斜杠(/)字符,所以上面给出的文件路径将被写成\Faculty\Prof.Brown\Courses\CS101。后续我们将使用UNIX约定的路径。

在任何时刻,每个进程都有一个当前的工作目录,在该目录中查找的路径名不是以斜杠开头的。例如,在图1-6中,假如 /Faculty/Prof.Brown是工作目录,那么使用路径名Courses/CS101,将会生成与上面给出的绝对路径名相同的文件。进程通过发出新工作目录的系统调用来改变工作目录。

MINIX 3中通过为每个文件和目录分配一个11位二进制保护代码,对文件和目录进行保护。保护代码由三个3位(3-bit)字段组成:一个用于所有者,一个用于所有者所在分组的其他成员(用户由系统管理员进行分组),一个用于其他所有人,以及稍后将讨论的2位(2 bits)。 每个字段都有一个标识读访问的位,一个标识写访问的位和一个标识执行访问的位。

例如,保护代码rwxr-x-x表示所有者可以读取,写入或执行该文件,其他组成员可以读取或执行(但不写入)该文件,其他所有人都可以执行(但不能读取或写)文件。 对于目录(而不是文件),x表示搜索权限。 破折号表示缺少相应的权限(该位为零)。

在读取或写入文件之前,必须打开该文件,此时将检查权限。 如果允许访问,系统将返回一个称为文件描述符的小整数,以便在后续操作中使用。 如果禁止访问,则返回错误代码(1)。

MINIX 3中的另一个重要概念是挂载文件系统。 几乎所有个人计算机都有一个或多个CD-ROM驱动器,可以插入和移除CD-ROM。 为了提供一种处理可移动媒体(CD-ROM,DVD,软盘,Zip驱动器等)的简洁方法,MINIX 3允许将CD-ROM上的文件系统连接到主树。 考虑图1-7(a)的情况。 在挂载调用之前,硬盘上的根文件系统和CD-ROM上的第二个文件系统是分开且无关的。

不能使用CD-ROM上的文件系统,因为无法在其上指定路径名。 MINIX 3不允许路径名以驱动器名称或编号作为前缀,这正是操作系统应该消除的设备依赖性。相反,挂载调用允许CD-ROM上的文件系统连接到根文件系统。在图1-7(b)中,驱动器0上的文件系统被挂载在目录b上,因此可以访问文件/b/x和/b/y。

操作系统文件挂载

如果目录b最初包含文件,它们在挂载CD-ROM时将无法访问,因为/b将引用驱动器0的根目录。(无法访问这些文件并不像相像的那样严重,似乎:文件系统几乎总是安装在空目录上。)如果系统包含多个硬盘,它们也可以全部挂载到一个树中。

Minix 3中的另一个重要概念是设备文件(special file)。为了使I/O设备看起来像文件,提供了设备文件。这样,就可以使用与读取和写入文件,相同的系统调用进行读写。 存在两种设备文件:块设备文件(block special files)和字符设备文件(character special files)。 块设备文件通常用于对由一组随机可寻址块(例如磁盘)组成的设备进行建模。 

通过打开块设备文件并读取,例如,块4,程序可以直接访问设备上的第四个块,而不考虑其上包含的文件系统的结构。 同样,字符设备文件用于模拟打印机,调制解调器以及接受或输出字符流的设备。 按照惯例,设备文件保存在/dev目录中。 例如,/dev/lp可能是行式打印机。

最后一个与进程和文件相关的特性:管道。管道是一种可以用来连接两个进程的伪文件,如图1-8所示。如果进程A和进程B希望使用管道进行对话,它们必须提前设置管道。当进程A想要向进程B发送数据时,它在管道上写入数据,就好像它是一个输出文件一样。

进程B可以像读取输入文件一样从管道读取数据。因此,minix3进程之间的通信,看起来非常像普通的文件读写。

操作系统管道通信

Shell

操作系统是执行系统调用的代码。编辑器、编译器、汇编器、链接器和命令解释器肯定不是操作系统的一部分,尽管它们很重要也很有用。为了避免混淆,在本节中,我们将简要介绍称为shell的minix3命令解释器。虽然它不是操作系统的一部分,但是它使用了大量的操作系统特性,因此可以作为,学习使用系统调用的最佳案例。

它也是终端用户和操作系统之间的主界面,除非用户使用图形界面。目前存在许多shell,包括csh、ksh、zsh和bash。它们都支持以下所述的功能,这些功能来自原始shell (sh)。

当用户登录时,都会启动shell。shell终端作为标准输入和标准输出。首先键入提示符(例如美元符号),提示符告诉用户shell正在等待接受命令。如果用户现在输入

date

示例shell创建一个子进程,并以子进程运行date程序。子进程运行时,shell等待它终止。当子进程完成时,shell再次键入提示符并尝试读取下一行输入。

用户可以指定将标准输出重定向到文件,例如:

date >file

类似地,标准输入可以重定向,如:

sort <file1 >file2

调用排序程序,输入从file1获取,输出发送到file2。

一个程序的输出可以通过“管道”连接,作为另一个程序的输入,例如:

cat file1 file2 file3 | sort >/dev/lp

调用cat程序连接三个文件,并把输出发送给sort,然后按字母顺序排列所有行。 sort的输出被重定向到文件/dev/lp,通常是打印机。

如果用户在命令后放置&符号,则shell不会等待它完成。 相反,它只是立即给出一个提示。

cat file1 file2 file3 | sort >/dev/lp &

以后台作业的方式启动排序,在排序过程中用户可以继续工作。shell还有许多其他有趣的特性,在这里我们就不一一讨论这些特性了。