《现代操作系统》读书笔记——引论

发布于 2020-06-04  1.84k 次阅读


引论

计算机安装了一层软件,称为操作系统,它的任务是为用户程序提供一个更好、更简单、更清晰的计算机模型,并管理计算机硬件设备。

多数计算机有两种运行模式:内核态用户态(描述的是cpu的运行模式,存储在处理器的程序状态器中)。软件中最基础的部分是操作系统,它运行在内核态(也称为管态、核心态)。在这个模式中,操作系统具有对所有硬件的完全访问权,可以执行机器能够运行的任何指令。软件的其余部分运行在用户态下。在用户态下,只使用了机器指令中的一个子集(即cpu处于不同的运行模式所能执行的指令是不同的)。特别地,那些会影响机器的控制或可进行I/O(输入/输出)操作的指令,在用户态中的程序里是禁止的。

image-20200604202234759

什么是操作系统

抽象是管理复杂性的一个关键。好的抽象可以把一个几乎不可能管理的任务划分为两个可管理的部分。其第一部分是有关抽象的定义和实现,第二部分是随时用这些抽象解决问题。

操作系统是一种运行在内核态的软件。从上往下,看操作系统给应用程序提供了一个相对美观的抽象接口,屏蔽以及管理了底层的复杂性,从下往上看,操作系统管理着所有的系统资源,目的是实现有限资源的良好分配,依赖空间(内存划分、硬盘划分)与时间(cpu时间片划分)上的复用技术,让多道程序能够和谐的相处。

image-20200604204816079

硬件简介

概览

操作系统扩展了计算机指令集并管理计算机的资源。

从概念上讲,一台简单的个人计算机可以抽象为类似于图1-6中的模型。CPU、内存以及I/O设备都由一条系统总线连接起来并通过总线与其他设备通信。

image-20200604205008955

处理器

处理器是计算机的大脑,他和内存进行交互(cpu并不直接和硬盘交互),在每个CPU基本周期中,首先从内存中取出指令,解码以确定其类型和操作数,接着执行之,然后取指、解码并执行下一条指令。按照这一方式,程序被执行完成。

不同的cpu架构对应着不同的专门指令集,不能直接互通。由于用来访问内存以得到指令或数据的时间要比执行指令花费的时间长得多(这里体现着cpu和内存存取速度之间的矛盾,利用寄存器和用内存来缓解cpu和硬盘存取速度的矛盾差不多),因此,所有的CPU内都有一些用来保存关键变量和临时数据的寄存器。这样,通常在指令集中提供一些指令,用以将一个字从内存调入寄存器,以及将一个字从寄存器存入内存。其他的指令可以把来自寄存器、内存的操作数组合,或者用两者产生一个结果,诸如将两个字相加并把结果存在寄存器或内存中。

除了用来保存变量和临时结果的通用寄存器之外,多数计算机还有一些对程序员可见的专门寄存器:

  • 程序计数器,它保存了将要取出的下一条指令的内存地址。在指令取出之后,程序计数器就被更新以便指向后继的指令。
  • 堆栈指针,它指向内存中当前栈的顶端。该栈含有已经进入但是还没有退出的每个过程的一个框架。在一个过程的堆栈框架中保存了有关的输入参数、局部变量以及那些没有保存在寄存器中的临时变量。
  • 程序状态字(ProgramStatusWord,PSW)寄存器。这个寄存器包含了条件码位(由比较指令设置)、CPU优先级、模式(用户态或内核态),以及各种其他控制位。用户程序通常读入整个PSW,但是,只对其中的少量字段写入。在系统调用和I/O中,PSW的作用很重要。

操作系统必须知晓所有的寄存器。在时间多路复用(timemultiplexing)CPU中,操作系统经常会中止正在运行的某个程序并启动(或再启动)另一个程序。每次停止一个运行着的程序时,操作系统必须保存所有的寄存器,这样在稍后该程序被再次运行时,可以把这些寄存器重新装入。(上下文切换)

现代cpu广泛采用流水线机制代替单一的存取执行模型

image-20200604211332293

为了从操作系统中获得服务,用户程序必须使用系统调用(systemcall)系统调用陷入内核并调用操作系统。TRAP指令把用户态切换成内核态,并启用操作系统。当有关工作完成之后,在系统调用后面的指令把控制权返回给用户程序。在本章的后面我们将具体解释系统调用过程,但是在这里,请读者把它看成是一个特别的过程调用指令,该指令具有从用户态切换到内核态的特别能力。

系统调用区别于库函数,系统调用由内核提供,库函数由函数库或者开发者自己提供,主要的区别也是类似用户态和内核态所能执行cpu指令的区别。

有必要指出,计算机使用陷阱(陷入内核态)而不是一条指令来执行系统调用。其他的多数陷阱是由硬件引起的,用于警告有异常情况发生,诸如试图被零除或浮点下溢等。在所有的情况下,操作系统都得到控制权并决定如何处理异常情况。有时,由于出错的原因程序不得不停止。在其他情况下可以忽略出错(如下溢数可以被置为零)。最后,若程序已经提前宣布它希望处理某类条件时,那么控制权还必须返回给该程序,让其处理相关的问题。

存储器

在任何一种计算机中的第二种主要部件都是存储器。在理想情形下,存储器应该极为迅速(快于执行一条指令,这样CPU不会受到存储器的限制),充分大,并且非常便宜。但是目前的技术无法同时满足这三个目标,于是出现了不同的处理方式。存储器系统采用一种分层次的结构。

image-20200604212119760

这里其实构成了计算机系统的基础缓存结构,缓存的目的就是为了缓解上下游速度差异的矛盾。

缓存在计算机科学的许多领域中起着重要的作用,并不仅仅只是RAM的缓存行。只要存在大量的资源可以划分为小的部分,那么,这些资源中的某些部分就会比其他部分更频繁地得到使用,通常缓存的使用会带来性能上的改善。操作系统一直在使用缓存。例如,多数操作系统在内存中保留频繁使用的文件(的一部分),以避免从磁盘中重复地调取这些文件。

在任何缓存系统中,都有若干需要尽快考虑的问题,包括:

1)何时把一个新的内容放入缓存。

2)把新内容放在缓存的哪一行上。

3)在需要时,应该把哪个内容从缓存中移走。

4)应该把新移走的内容放在某个较大存储器的何处。

  • 寄存器。它们用与CPU相同的材料制成,所以和CPU一样快。显然,访问它们是没有时延的。
  • 高速缓存。它多数由硬件控制。主存被分割成高速缓存行(cacheline),其典型大小为64个字节,地址0至63对应高速缓存行0,地址64至127对应高速缓存行1,以此类推。最常用的高速缓存行放置在CPU内部或者非常接近CPU的高速缓存中。当某个程序需要读一个存储字时,高速缓存硬件检查所需要的高速缓存行是否在高速缓存中。如果是,称为高速缓存命中,缓存满足了请求,就不需要通过总线把访问请求送往主存。高速缓存命中通常需要两个时钟周期。高速缓存未命中就必须访问内存,这要付出大量的时间代价。由于高速缓存的价格昂贵,所以其大小有限。有些机器具有两级甚至三级高速缓存,每一级高速缓存比前一级慢且容量更大。
  • 主存。这是存储器系统的主力。主存通常称为随机访问存储器(RandomAccessMemory,RAM)。过去有时称之为磁芯存储器,因为在20世纪50年代和60年代,使用很小的可磁化的铁磁体制作主存。目前,存储器的容量在几百兆字节到若干吉字节之间,并且其容量正在迅速增长。所有不能在高速缓存中得到满足的访问请求都会转往主存。
  • 磁盘(硬盘)。磁盘同RAM相比,每个二进制位的成本低了两个数量级,而且经常也有两个数量级大的容量。磁盘惟一的问题是随机访问数据时间大约慢了三个数量级。其低速的原因是因为磁盘是一种机械装置。

许多计算机支持一种著名的虚拟内存机制,这将在第3章中讨论。这种机制使得期望运行大于物理内存的程序成为可能,其方法是将程序放在磁盘上,而将主存作为一种缓存,用来保存最频繁使用的部分程序。这种机制需要快速地映像内存地址,以便把程序生成的地址转换为有关字节在RAM中的物理地址。这种映像由CPU中的一个部件,称为存储器管理单元(MemoryManagementUnit,MMU)来完成,如图1-6所示。缓存和MMU的出现对系统的性能有着重要的影响。在多道程序系统中,从一个程序切换到另一个程序,有时称为上下文切换(contextswitch),有必要对缓存中来的所有修改过的块进行写回磁盘操作,并修改MMU中的映像寄存器。但是这两种操作的代价很昂贵,所以程序员们努力避免使用这些操作。

I/O设备

I/O设备一般包括两个部分:设备控制器和设备本身。

每类设备控制器都是不同的,所以,需要不同的软件进行控制。专门与控制器对话,发出命令并接收响应的软件,称为设备驱动程序(devicedriver)。

实现输入输出的三种方式:

  • 在最简单的方式中,用户程序发出一个系统调用,内核将其翻译成一个对应设备驱动程序的过程调用。然后设备驱动程序启动I/O并在一个连续不断的循环中检查该设备,看该设备是否完成了工作(一般有一些二进制位用来指示设备仍在忙碌中)。当I/O结束后,设备驱动程序把数据送到指定的地方(若有此需要),并返回。然后操作系统将控制返回给调用者。这种方式称为忙等待(busywaiting),其缺点是要占据CPU,CPU一直轮询设备直到对应的I/O操作完成。
  • 设备驱动程序启动设备并且让该设备在操作完成时发出一个中断。设备驱动程序在这个时刻返回。操作系统接着在需要时阻塞调用者并安排其他工作进行。当设备驱动程序检测到该设备的操作完毕时,它发出一个中断通知操作完成。
  • I/O使用一种特殊的直接存储器访问(DirectMemoryAccess,DMA)芯片,它可以控制在内存和某些控制器之间的位流,而无须持续的CPU干预。CPU对DMA芯片进行设置,说明需要传送的字节数、有关的设备和内存地址以及操作方向,接着启动DMA。当DMA芯片完成时,它引发一个中断,其处理方式如前所述。

关于中断:

在操作系统中,中断是非常重要的,所以需要更具体地讨论。在图1-11a中,有一个I/O的三步过程。在第1步,设备驱动程序通过写设备寄存器通知设备控制器做什么。然后,设备控制器启动该设备。当设备控
制器传送完毕被告知的要进行读写的字节数量后,它在第2步中使用特定的总线发信号给中断控制器芯片。如果中断控制器已经准备接收中断(如果正忙于一个更高级的中断,也可能不接收),它会在CPU芯片的一个管脚上声明,这就是第3步。在第4步中,中断控制器把该设备的编号放到总线上,这样CPU可以读总线,并且知道哪个设备刚刚完成了操作(可能同时有许多设备在运行)。

image-20200604214305363

一旦CPU决定取中断,通常程序计数器和PSW就被压入当前堆栈中,并且CPU被切换到用户态。设备编号可以成为部分内存的一个引用,用于寻找该设备中断处理程序的地址。这部分内存称为中断向量(interruptvector)。当中断处理程序(中断设备的设备驱动程序的一部分)开始后,它取走已入栈的程序计数器和PSW,并保存之,然后查询设备的状态。在中断处理程序全部完成之后,它返回到先前运行的用户程序中尚未执行的头一条指令。这些步骤如图1-11b所示。

中断经常会在非常不合适的时刻发生,比如,在另一个中断程序正在运行时发生。正由于此,CPU有办法关闭中断并在稍后再开启中断。在中断关闭时,任何已经发出中断的设备,可以继续保持其中断信号,但是CPU不会被中断,直至中断再次启用为止。如果在中断关闭时,已有多个设备发出了中断,中断控制器将决定先处理哪个中断,通常这取决于事先赋予每个设备的静态优先级。最高优先级的设备赢得竞争。

总线

计算机中各个组件需要通过总线来进行通讯。

image-20200604214732256

操作系统概念

进程

在所有操作系统中,一个重要的概念是进程(process)。进程本质上是正在执行的一个程序。与每个进程相关的是进程的地址空间(addressspace),这是从某个最小值的存储位置(通常是零)到某个最大值存储位置的列表。在这个地址空间中,进程可以进行读写。该地址空间中存放有可执行程序、程序的数据以及程序的堆栈。与每个进程相关的还有资源集,通常包括寄存器(含有程序计数器和堆栈指针)、打开文件的清单、突出的报警、有关进程清单,以及运行该程序所需要的所有其他信息。进程基本上是容纳运行一个程序所需要所有信息的容器

在许多操作系统中,与一个进程有关的所有信息,除了该进程自身地址空间的内容以外,均存放在操作系统的一张表中,称为进程表(processtable),进程表是数组(或链表)结构,当前存在的每个进程都要占用其中一项。

一个(挂起的)进程包括:进程的地址空间,往往称作磁芯映像(coreimage,纪念过去年代中使用的磁芯存储器),以及对应的进程表项,其中包括寄存器以及稍后重启动该进程所需要的许多其他信息。

地址空间

较复杂的操作系统允许在内存中同时运行多道程序。为了避免它们彼此互相干扰(包括操作系统),需要有某种保护机制。虽然这种机制必然是硬件形式的,但是它由操作系统掌控。

上述的观点涉及对计算机主存的管理和保护。另一种不同的但是同样重要并与存储器有关的内容,是管理进程的地址空间。通常,每个进程有一些可以使用的地址集合,典型值从0开始直到某个最大值。在最简单的情形下,一个进程可拥有的最大地址空间小于主存。在这种方式下,进程可以用满其地址空间,而且内存中也有足够的空间容纳该进程。但是,在许多32位或64位地址的计算机中,分别有2的32次方或2的64次方字节的地址空间。如果一个进程有比计算机拥有的主存还大的地址空间,而且该进程希望使用全部的内存,那怎么办呢?在早期的计算机中,这个进程只好承认坏运气了。现在,有了一种称为虚拟内存的技术,正如前面已经介绍过的,操作系统可以把部分地址空间装入主存,部分留在磁盘上,并且在需要时穿梭交换它们。在本质上,操作系统创建了一个地址空间的抽象,作为进程可以引用地址的集合。该地址空间与机器的物理内存解耦,可能大于也可能小于该物理空间。对地址空间和物理空间的管理组成了操作系统功能的一个重要部分,

文件

实际上,支持操作系统的另一个关键概念是文件系统。如前所述,操作系统的一项主要功能是隐藏磁盘和其他I/O设备的细节特性,并提供给程序员一个良好、清晰的独立于设备的抽象文件模型。

在实例中,每个进程有一个工作目录(working directory)

在读写文件之前,首先要打开文件,检查其访问权限。若权限许可,系统将返回一个小整数,称作文件描述符(filedescriptor),供后续操作使用。若禁止访问,系统则返回一个错误码。

在UNIX中,另一个重要的概念是特殊文件(specialfile)。提供特殊文件是为了使I/O设备看起来像文件一般。这样,就像使用系统调用读写文件一样,I/O设备也可通过同样的系统调用进行读写。有两类特殊文件:块特殊文件(blockspecialfile)和字符特殊文件(characterspecialfile)。块特殊文件指那些由可随机存取的块组成的设备,如磁盘等。比如打开一个块特殊文件,然后读第4块,程序可以直接访问设备的第4块而不必考虑存放该文件的文件系统结构。类似地,字符特殊文件用于打印机、调制解调器和其他接收或输出字符流的设备。按照惯例,特殊文件保存在/dev目录中。例如,/dev/lp是打印机(曾经称为行式打印机)。

管道的概念:管道(pipe)是一种虚文件,它可连接两个进程(属于进程通讯的一种方式),如图1-16所示。如果进程A和B希望通过管道对话,它们必须提前设置该管道。当进程A想对进程B发送数据时,它把数据写到管道上,仿佛管道就是输出文件一样。进程B可以通过读该管道而得到数据,仿佛该管道就是一个输入文件一样。这样,在UNIX中两个进程之间的通信就很类似于普通文件的读写了。更为强大的是,若进程要想发现它所写入的输出文件不是真正的文件而是管道,则需要使用特殊的系统调用。

image-20200604220339713

系统调用

任何单CPU计算机一次只能执行一条指令。如果一个进程正在用户态中运行一个用户程序,并且需要一个系统服务,比如从一个文件读数据,那么它就必须执行一个陷阱或系统调用指令,将控制转移到操作系统。操作系统接着通过参数检查,找出所需要的调用进程。然后,它执行系统调用,并把控制返回给在系统调用后面跟随着的指令。在某种意义上,进行系统调用就像进行一个特殊的过程调用,但是只有系统调用可以进入内核,而过程调用则不能。

可移植作業系統接口(英語:Portable Operating System Interface,縮寫為POSIX)是IEEE为要在各种UNIX操作系统上运行软件,而定义API的一系列互相关联的标准的总称,其正式称呼为IEEE Std 1003,而国际标准名称为ISO/IEC 9945。此标准源于一个大约开始于1985年的项目。POSIX这个名称是由理查德·斯托曼(RMS)应IEEE的要求而提议的一个易于记忆的名称。它基本上是Portable Operating System Interface(可移植操作系统接口)的缩写,而X则表明其对Unix API的传承。

将POSIX过程映射到系统调用并不是一对一的。POSIX标准定义了构造系统所必须提供的一套过程,但是并没有规定它们是系统调用,是库调用还是其他的形式。如果不通过系统调用就可以执行一个过程(即无须陷入内核),那么从性能方面考虑,它通常会在用户空间中完成。不过,多数POSIX过程确实进行系统调用,通常是一个过程直接映射到一个系统调用上。在有一些情形下,特别是所需要的过程仅仅是某个调用的变体时,此时一个系统调用会对应若干个库调用。


面向ACG编程