操作系统(三)

3.1 什么是进程

3.1.1 背景

在给进程下定义之前,先总结一下第1、2章介绍的一些概念:

  • 一个计算机平台包括一组硬件资源,比如处理器、内存、I/O 模块、定时器和磁盘驱动器等。
  • 计算机程序是为执行某些任务而开发的。在典型的情况下,它们接受外来的输入,做一些处理之后,输出结果。
  • 直接根据给定的硬件平台写应用程序效率是低下的,主要原因如下:
    • 针对相同的平台可以开发出很多应用程序,所以开发出这些应用程序访问计算机资源的通用例程是很有意义的。
    • 处理器本身只能对多道程序设计提供有限的支持,需要用软件去管理处理器和其他资源同时被多个程序共享。
    • 如果多个程序在同一时间都是活跃的,那么需要保护每个程序的数据、I/O 使用和其他资源不被其他程序占用。
  • 开发操作系统是为了给应用程序提供一个方便、安全和一致的接口。操作系统是计算机硬件和应用程序之间的一层软件,对应用程序和工具提供了支持。
  • 可以把操作系统想象为资源的统一抽象表示,可以被应用程序请求和访问。资源包括内存、网络接口和文件系统等。一旦操作系统为应用程序创建了这些资源的抽象表示,就必须管理它们的使用,例如一个操作系统可以允许资源共享和资源保护。

有了应用程序、系统软件和资源的概念,就可以讨论操作系统怎样以一个有序的方式管理应用程序的执行,以达到以下目的:

  • 资源对多个应用程序是可用的。
  • 物理处理器在多个应用程序间切换以保证所有程序都在执行中。
  • 处理器和I/O 设备能得到充分的利用。

所有现代操作系统采用的方法都是依据对应于一个或多个进程存在的应用程序执行的一种模型。

3.1.2 进程和进程控制块

第2章给进程下了以下几个定义:

  • 正在执行的程序。
  • 正在计算机上执行的程序实例。
  • 能分配给处理器并由处理器执行的实体。
  • 具有以下特征的活动单元:一组指令序列的执行、一个当前状态和相关的系统资源集。

也可以把进程当成由一组元素组成的实体,进程的两个基本的元素是程序代码(可能被执行相同程序的其他进程共享)和代码相关联的数据集。
假设处理器开始执行这个程序代码,且把这个执行实体叫做进程。
在进程执行时,任意给定一个时间,进程都可以唯一地被表征为以下元素:

  • 标识符:跟这个进程相关的唯一标识符,用来区别其他进程。
  • 状态:如果进程正在执行,那么进程处于运行态。
  • 优先级:相对于其他进程的优先级。
  • 程序计数器:程序中即将被执行的下一条指令的地址。
  • 内存指针:包括程序代码和进程相关数据的指针,还有和其他进程共享内存块的指针。
  • 上下文数据:进程执行时处理器的寄存器中的数据。
  • I/O 状态信息:包括显式的I/O 请求、分配给进程的I/O 设备(例如磁带驱动器)和被进程使用的文件列表等。
  • 记账信息:可能包括处理器时间总和、使用的时钟数总和、时间限制、记账号等。

上述列表信息存放在一个叫做进程控制块(如图)的数据结构中,该控制块由操作系统创建和管理。进程控制块包含了充分的信息,这样就可以中断一个进程的执行,并且在后来恢复执行进程时就好像进程未被中断过。进程控制块是操作系统能够支持多进程和提供多处理的关键工具。当进程被中断时,操作系统会把程序计数器和处理器寄存器(上下文数据)保存到进程控制块中的相应位置,进程状态也被改变为其他的值,例如阻塞态或就绪态(后面将讲述)。操作系统可以把其他进程设置为运行态,把其他进程的程序计数器和进程上下文数据加载到处理器寄存器中,这样其他进程就可以开始执行了。

操作系统(三) 
图3.1 简化的进程控制块

因此,进程是由程序代码和相关数据还有进程控制块组成。对于一个单处理器计算机,在任何时间都最多只有一个进程在执行,正在运行的这个进程的状态为运行态。

3.2 进程状态

正如前面所提到的,对一个被执行的程序,操作系统会为该程序创建一个进程或任务。

  • 从处理器的角度看,它在指令序列中按某种顺序执行指令,这个顺序根据程序计数器寄存器中不断变化的值来指示,程序计数器可能指向不同进程中不同部分的程序代码;
  • 从程序自身的角度看,它的执行涉及程序中的一系列指令。可以通过列出为该进程执行的指令序列来描述单个进程的行为,这样的序列称做进程的轨迹。可以通过给出各个进程的轨迹是如何被交替的来描述处理器的行为。

举例,图3.2 给出了三个进程在内存中的布局,为简化讨论,假设没有使用虚拟内存,因此所有三个进程都由完全载入内存中的程序表示,此外,有一个小的分派器一1使处理器从一个进程切换到另一个进程。图3.3 给出了这三个进程在执行过程早期的轨迹,给出了进程A 和C 中最初执行的12 条指令,进程B 执行4 条指令,假设第4 条指令调用了进程必须等待的I/O 操作。

操作系统(三) 
图3.2 在指令周期13 时的执
行快照(如图3.4 所示)

现在从处理器的角度看这些轨迹。图3.4 给出了最初的52个指令周期中交替的轨迹(为方便起见,指令周期都给出了编号)。在图中,阴影部分代表由分配器执行的代码。在每个实例中由分派器执行的指令顺序是相同的,因为是分派器的同一个功能在执行。假设操作系统仅允许一个进程最多连续执行6 个指令周期,在此之后将被中断,这避免了任何一个进程独占处理器时间。如图3.4 所示,进程A 最初的6 条指令被执行,接下来是一个超时并执行分派器的某些代码,在控制转移给进程B 之前分派器执行了6 条指令二2。在进程B 的4 条指令被执行后,进程B 请求一个它必须等待的I/O 动作,因此,处理器停止执行进程B,并通过分派器转移到进程C。在超时后,处理器返回进程A,当这次处理超时时,进程B 仍然等待那个I/O 操作的完成,因此分派器再次转移到进程C。

操作系统(三) 
(点击查看大图)图3.3 图3.2 中进程的轨迹
操作系统(三) 
(点击查看大图)图3.4 图3.2 中进程的组合轨迹

3.2.1 两状态进程模型

操作系统的基本职责是控制进程的执行,这包括确定交替执行的方式和给进程分配资源。在设计控制进程的程序时,第一步就是描述进程所表现出的行为。

任何时刻,一个进程可以处于以下两种状态之一:运行态或未运行态,如图3.5a 所示。当操作系统创建一个新进程时,它将该进程以未运行态加入到系统中,操作系统知道这个进程是存在的,并正在等待执行机会。当前正在运行的进程不时地被中断,操作系统中的分派器部分将选择一个新进程运行。前一个进程从运行态转换到未运行态,另外一个进程转换到运行态。

从这个简单的模型可以意识到操作系统的一些设计元素。必须用某种方式来表示每个进程,使得操作系统能够跟踪它,也就是说,必须有一些与进程相关的信息,包括进程在内存中的当前状态和位置,即进程控制块。未运行的进程必须保持在某种类型的队列中,并等待它们的执行时机。图3.5b 给出了一个结构,结构中有一个队列,队列中的每一项都指向某个特定进程的指针,或队列可以由数据块构成的链表组成,每个数据块表示一个进程。

可以用该排队图描述分派器的行为。被中断的进程转移到等待进程队列中,或者,如果进程已经结束或取消,则被销毁(离开系统)。在任何一种情况下,分派器均从队列中选择一个进程来执行。

操作系统(三) 
图3.5 两状态进程模型

3.2.2 进程的创建和终止

在对简单的两状态模型进行改进之前,讨论一下进程的创建和终止。无论使用哪种进程行为模型,进程的生存期都围绕着进程的创建和终止。

进程的创建

当一个新进程添加到那些正在被管理的进程集合中去时,操作系统需要建立用于管理该进程的数据结构(见3.3 节),并在内存中给它分配地址空间。将在3.3 节中讲述这些数据结构,这些行为构成了一个新进程的创建过程。

通常有4 个事件会导致创建一个进程,如表。

  • 在批处理环境中,响应作业提交时会创建进程;
  • 在交互环境中,当一个新用户试图登录时会创建进程。

不论在哪种情况下,操作系统都负责新进程的创建;操作系统也可能会代表应用程序创建进程,eg:如果用户请求打印一个文件,则操作系统可以创建一个管理打印的进程,进而使请求进程可以继续执行,与完成打印任务的时间无关。

表3.1 导致进程创建的原因

操作系统(三) 

传统地,操作系统创建进程的方式对用户和应用程序都是透明的。但允许一个进程引发另一个进程的创建将是很有用的。例如,一个应用程序进程可以产生另一个进程,以接收应用程序产生的数据,并将数据组织成适合以后分析的格式。新进程与应用程序并行地运行,并当得到新的数据时被**。这个方案对构造应用程序是非常有用的,例如,服务器进程(如打印服务器、文件服务器)可以为它处理的每个请求产生一个新进程。当操作系统为另一个进程的显式请求创建一个进程时,这个动作称为进程派生

当一个进程派生另一个进程时,前一个称做父进程,被派生的进程称做子进程。在典型的情况下,相关进程需要相互之间的通信和合作。相关主题将在第5 章讲述。

进程终止

表3.2 概括了进程终止的典型原因。任何一个计算机系统都必须为进程提供表示其完成的方法,批处理作业中应该包含一个Halt 指令或用于终止的操作系统显式服务调用来终止。

  • Halt 指令将产生一个中断,警告操作系统一个进程已经完成。
  • 对交互式应用程序,用户的行为将指出何时进程完成,例如,在分时系统中,当用户退出系统或关闭自己的终端时,该用户的进程将被终止。在个人计算机或工作站中,用户可以结束一个应用程序(如字处理或电子表格)。所有这些行为最终导致发送给操作系统的一个服务请求,以终止发出请求的进程。
  • 此外,很多错误和故障条件会导致进程终止。表3.2 列出了一些最常见的识别条件。
  • 最后,在有些操作系统中,进程可以被创建它的进程终止,或当父进程终止时而终止。

表3.2 导致进程终止的原因

操作系统(三) 

3.2.3 五状态模型

如果所有进程都做好了执行准备,则图3.5b 所给出的排队原则是有效的。队列是“先进先出”的表,对于可运行的进程处理器以一种轮转方式操作(依次给队列中的每个进程一定的执行时间,然后进程返回队列,阻塞情况除外)。但即使对前面描述的简单例子,这个实现都是不合适的:存在着一些处于非运行状态但已经就绪等待执行的进程,而同时存在另外的一些处于阻塞状态等待I/O 操作结束的进程。因此,如果使用单个队列,分派器不能只考虑选择队列中最老的进程,相反,它应该扫描这个列表,查找那些未被阻塞且在队列中时间最长的进程

解决这种情况的一种比较自然的方法是将非运行状态分成两个状态:就绪(ready)和阻塞(blocked),如下图。新图中的5 个状态如下:

操作系统(三) 
图3.6 五状态进程模型
  • 运行态:该进程正在执行。在本章中,假设计算机只有一个处理器,因此一次最多只有一个进程处于这个状态。
  • 就绪态:进程做好了准备,只要有机会就开始执行。
  • 阻塞/等待态:进程在某些事件发生前不能执行,如I/O 操作完成。
  • 新建态:刚刚创建的进程,操作系统还没有把它加入到可执行进程组中。通常是进程控制块已经创建但还没有加载到内存中的新进程。
  • 退出态:操作系统从可执行进程组中释放出的进程,或者是因为它自身停止了,或者是因为某种原因被取消。

新建状态对应于刚刚定义的进程。例如,如果一位新用户试图登录到分时系统中,或者一个新的批作业被提交执行,那么操作系统可以分两步定义新进程。

  • 首先,操作系统执行一些必需的辅助工作,将标识符关联到进程,分配和创建管理进程所需要的所有表。此时,进程处于新建状态,这意味着操作系统已经执行了创建进程的必需动作,但还没有执行进程。例如,操作系统可能基于性能或内存局限性的原因,限制系统中的进程数量。当进程处于新建态时,操作系统所需要的关于该进程的信息保存在内存中的进程表中,但进程自身还未进入内存,就是即将执行的程序代码不在内存中,也没有为与这个程序相关的数据分配空间。当进程处于新建态时,程序保留在外存中,通常是磁盘中。

类似地,进程退出系统也分为两步。

  • 首先,当进程到达一个自然结束点时,由于出现不可恢复的错误而取消时,或当具有相应权限的另一个进程取消该进程时,进程被终止;终止使进程转换到退出态,此时,进程不再被执行了,与作业相关的表和其他信息临时被操作系统保留起来,这给辅助程序或支持程序提供了提取所需信息的时间。一个实用程序为了分析性能和利用率,可能需要提取进程的历史信息,一旦这些程序都提取了所需要的信息,操作系统就不再需要保留任何与该进程相关的数据,该进程将从系统中删除。

图3.6 显示了导致进程状态转换的事件类型。可能的转换如下:

  • 空→新建:创建执行一个程序的新进程。这个事件在表3.1 中所列出的原因下都会发生。
  • 新建→就绪:操作系统准备好再接纳一个进程时,把一个进程从新建态转换到就绪态。大多数系统基于现有的进程数或分配给现有进程的虚拟内存数量设置一些限制,以确保不会因为活跃进程的数量过多而导致系统的性能下降。
  • 就绪→运行:需要选择一个新进程运行时,操作系统选择一个处于就绪态的进程,这是调度器或分派器的工作。进程的选择问题将在第四部分探讨。
  • 运行→退出:如果当前正在运行的进程表示自己已经完成或取消,则它将被操作系统终止,见表3.2。
  • 运行→就绪:这类转换最常见的原因是,正在运行的进程到达了“允许不中断执行”的最大时间段;实际上所有多道程序操作系统都实行了这类时间限定。这类转换还有很多其他原因,例如操作系统给不同的进程分配不同的优先级,但这不是在所有的操作系统中都实现了的。假设,进程A 在一个给定的优先级运行,且具有更高优先级的进程B 正处于阻塞态。如果操作系统知道进程B 等待的事件已经发生了,则将B 转换到就绪态,然后因为优先级的原因中断进程A 的执行,将处理器分派给进程B,我们说操作系统抢占了进程A。最后一种情况是,进程自愿释放对处理器的控制,例如一个周期性地进行记账和维护的后台进程。
  • 运行→阻塞:如果进程请求它必须等待的某些事件,则进入阻塞态。对操作系统的请求通常以系统服务调用的形式发出,也就是说,正在运行的程序请求调用操作系统中一部分代码所发生的过程。例如,进程可能请求操作系统的一个服务,但操作系统无法立即予以服务,它也可能请求了一个无法立即得到的资源,如文件或虚拟内存中的共享区域;或者也可能需要进行某种初始化的工作,如I/O 操作所遇到的情况,并且只有在该初始化动作完成后才能继续执行。当进程互相通信,一个进程等待另一个进程提供输入时,或者等待来自另一个进程的信息时,都可能被阻塞。
  • 阻塞→就绪:当所等待的事件发生时,处于阻塞态的进程转换到就绪态。
  • 就绪→退出:为了清楚起见,状态图中没有表示这种转换。在某些系统中,父进程可以在任何时刻终止一个子进程。如果一个父进程终止,与该父进程相关的所有子进程都将被终止。
  • 阻塞→退出:前面一项提供了注释。

再回到前面的简单例子,图3.7 显示了每个进程在状态间的转换,图3.8a 给出了可能实现的排队规则,有两个队列:就绪队列和阻塞队列。进入系统的每个进程被放置在就绪队列中,当操作系统选择另一个进程运行时,将从就绪队列中选择。对于没有优先级的方案,这可以是一个简单的先进先出队列。当一个正在运行的进程被移出处理器时,它根据情况或者被终止,或者被放置在就绪或阻塞队列中。最后,当一个事件发生时,所有位于阻塞队列中等待这个事件的进程都被转换到就绪队列中。

操作系统(三) 
图3.7 图3.4 中的进程状态

后一种方案意味着当一个事件发生时,操作系统必须扫描整个阻塞队列,搜索那些等待该事件的进程。在大型操作系统中,队列中可能有几百甚至几千个进程,因此,拥有多个队列将会很有效,一个事件可以对应一个队列。那么,当事件发生时,相应队列中的所有进程都转换到就绪态(见图3.8b)。

操作系统(三) 
(点击查看大图)图3.8 图3.6 的排队模型

最后还有一种改进是,如果按照优先级方案分派进程,维护多个就绪队列(每个优先级一个队列)将会带来很多的便利。操作系统可以很容易地确定哪个就绪进程具有最高的优先级且等待时间最长。

3.2.4 被挂起的进程

交换的需要

前面描述的三个基本状态(就绪态、运行态和阻塞态)提供了一种为进程行为建立模型的系统方法,但可以证明往模型中增加其他状态也是合理的。为了说明加入新状态的好处,考虑一个没有使用虚拟内存的系统,每个被执行的进程必须完全载入内存,因此,图3.8b 中,所有队列中的所有进程必须驻留在内存中。

所有这些设计机制的原因都是由于I/O 活动比计算速度慢很多,因此在单道程序系统中的处理器在大多数时候是空闲的。但是图3.8b 的方案并没有完全解决这个问题。在这种情况下,内存保存有多个进程,当一个进程正在等待时,处理器可以转移到另一个进程,但是处理器比I/O要快得多,以至于内存中所有的进程都在等待I/O 的情况很常见。因此,即使是多道程序设计,大多数时候处理器仍然可能处于空闲状态。

一种解决方法是内存可以被扩充以适应更多的进程,但是这种方法有两个缺陷。

  • 首先是内存的价格问题,当内存大小增加到兆位及千兆位时,价格也会随之增加;
  • 再者,程序对内存空间需求的增长速度比内存价格下降的速度快。因此,更大的内存往往导致更大的进程,而不是更多的进程。

另一种解决方案是交换,包括把内存中某个进程的一部分或全部移到磁盘中。当内存中没有处于就绪状态的进程时,操作系统就把被阻塞的进程换出到磁盘中的“挂起队列”。操作系统在此之后取出挂起队列中的另一个进程,或者接受一个新进程的请求,将其纳入内存运行。进程行为模型(见图3.9a)中增加另一个状态:挂起态。

“交换”是一个I/O 操作,因而也可能使问题更加恶化。但是由于磁盘I/O 一般是系统中最快的I/O(相对于磁带或打印机I/O),所以交换通常会提高性能。

当操作系统已经执行了一个换出操作,它可以有两种将一个进程取到内存中的选择:

  • 可以接纳一个新近创建的进程;
  • 或调入一个以前被挂起的进程。

通常比较倾向于调入一个以前被挂起的进程,给它提供服务,而不是增加系统中的负载总数。

但所有已经挂起的进程在挂起时都处于阻塞态。显然,这时把被阻塞的进程取回内存没有任何意义,因为它仍然没有准备好执行。但是,挂起状态中的每个进程最初是阻塞在一个特定的事件上,当这个事件发生时,进程就不再阻塞,可以继续执行。因此,需要重新考虑设计方式。有两个独立的概念:进程是否在等待一个事件(阻塞与否)以及进程是否已经被换出内存(挂起与否)。为适应这种2×2 的组合,需要4 个状态:

  • 就绪态:进程在内存中并可以执行。
  • 阻塞态:进程在内存中并等待一个事件。
  • 阻塞/挂起态:进程在外存中并等待一个事件。
  • 就绪/挂起态:进程在外存中,但是只要被载入内存就可以执行。
操作系统(三) 
图3.9 有挂起态的进程状态转换图

在查看包含两个新挂起状态的状态转换图之前,必须提到另一点。到现在为止的论述都假设没有使用虚拟内存,进程或者都在内存中,或者都在内存之外。在虚拟内存方案中,可能会执行到只有一部分内容在内存中的进程,如果访问的进程地址不在内存中,则进程的相应部分可以被调入内存。虚拟内存的使用看上去会消除显式交换的需要,这是因为通过处理器中的存储管理硬件,任何期望的进程中的任何期望的地址都可以移入或移出内存。但是,正如在第8 章中将会看到的,如果有足够多的活动进程,并且所有进程都有一部分在内存中,则有可能导致虚拟内存系统崩溃。因此,即使在虚拟存储系统中,操作系统也需要不时地根据执行情况显式地、完全地换出进程。

现在来看图3.9b 中我们已开发的状态转换模型(图中的虚线表示可能但并不是必需的转换)。比较重要的新的转换如下:

  • 阻塞→阻塞/挂起:如果没有就绪进程,则至少一个阻塞进程被换出,为另一个没有阻塞的进程让出空间。如果操作系统确定当前正在运行的进程,或就绪进程为了维护基本的性能要求而需要更多的内存空间,那么,即使有可用的就绪态进程也可能出现这种转换。
  • 阻塞/挂起→就绪/挂起:如果等待的事件发生了,则处于阻塞/挂起状态的进程可以转换到就绪/挂起状态。注意,这要求操作系统必须能够得到挂起进程的状态信息。
  • 就绪/挂起→就绪:如果内存中没有就绪态进程,操作系统需要调入一个进程继续执行。此外,当处于就绪/挂起态的进程比处于就绪态的任何进程的优先级都要高时,也可以进行这种转换。这种情况的产生是由于操作系统设计者规定调入高优先级的进程比减少交换量更重要。
  • 就绪→就绪/挂起:通常,操作系统更倾向于挂起阻塞态进程而不是就绪态进程,因为就绪态进程可以立即执行,而阻塞态进程占用了内存空间但不能执行。但如果释放内存以得到足够空间的唯一方法是挂起一个就绪态进程,那么这种转换也是必需的。并且,如果操作系统确信高优先级的阻塞态进程很快将会就绪,那么它可能选择挂起一个低优先级的就绪态进程,而不是一个高优先级的阻塞态进程。

还需要考虑的几种其他转换有:

  • 新建→就绪/挂起以及新建→就绪:当创建一个新进程时,该进程或者加入到就绪队列,或者加入到就绪/挂起队列中。不论哪种情况,操作系统都必须建立一些表以管理进程,并为进程分配地址空间。操作系统可能更倾向于在初期执行这些辅助工作,这使得它可以维护大量的未阻塞的进程。通过这个策略,内存中经常会没有足够的空间分配给新进程,因此使用了(新建→就绪/挂起)转换。另一方面,我们可以证明创建进程的适时(just-in-time)原理,即尽可能推迟创建进程以减少操作系统的开销,并在系统被阻塞态进程阻塞时允许操作系统执行进程创建任务。
  • 阻塞/挂起→阻塞:这种转换在设计中比较少见,如果一个进程没有准备好执行,并且不在内存中,调入它又有什么意义?但是考虑到下面的情况:一个进程终止,释放了一些内存空间,阻塞/挂起队列中有一个进程比就绪/挂起队列中的任何进程的优先级都要高,并且操作系统有理由相信阻塞进程的事件很快就会发生,这时,把阻塞进程而不是就绪进程调入内存是合理的。
  • 运行→就绪/挂起:通常当分配给一个运行进程的时间期满时,它将转换到就绪态。但是,如果由于位于阻塞/挂起队列的具有较高优先级的进程变得不再被阻塞,操作系统抢占这个进程,也可以直接把这个运行进程转换到就绪/挂起队形中,并释放一些内存空间。
  • 各种状态→退出:在典型情况下,一个进程在运行时终止,或者是因为它已经完成,或者是因为出现了一些错误条件。但是,在某些操作系统中,一个进程可以被创建它的进程终止,或当父进程终止时终止。如果允许这样,则进程在任何状态时都可以转换到退出态。

挂起的其他用途

到目前为止,挂起进程的概念与不在内存中的进程概念是等价的一个不在内存中的进程,不论它是否在等待一个事件,都不能立即执行。

我们可以总结一下挂起进程的概念。首先,按照以下特点定义挂起进程:

  1. 进程不能立即执行。
  2. 进程可能是或不是正在等待一个事件。如果是,阻塞条件不依赖于挂起条件,阻塞事件的发生不会使进程立即被执行。
  3. 为阻止进程执行,可以通过代理把这个进程置于挂起状态,代理可以是进程自己,也可以是父进程或操作系统。
  4. 除非代理显式地命令系统进行状态转换,否则进程无法从这个状态中转移。

表3.3 列出了进程的一些挂起原因。

  • 提供更多的内存空间,这样可以调入一个就绪/挂起态进程或增加分配给其他就绪态进程的内存;
  • 操作系统因为其他动机而挂起一个进程,例如,记账或跟踪进程可能用于监视系统的活动,可以使用进程记录各种资源(处理器、内存、通道)的使用情况以及系统中用户进程的进行速度。在操作员控制下的操作系统可以不时地打开或关闭这个进程。
  • 如果操作系统发现或怀疑有问题,它可以挂起进程。死锁就是一个例子,将在第6 章讲述。
  • 另一个例子是,如果在进程测试时检测到通信线路中的问题,操作员让操作系统挂起使用该线路的进程。

另外一些原因关系到交互用户的行为。例如,如果用户怀疑程序中有缺陷,他(她)可以挂起执行程序并进行调试,检查并修改程序或数据,然后恢复执行;或者可能有一个收集记录或记账的后台程序,用户可能希望能够打开或关闭这个程序。

表3.3 导致进程挂起的原因

操作系统(三) 

时机的选择也会导致一个交换决策。例如,如果一个进程周期性地被**,但大多数时间是空闲的,则在它在两次使用之间应该被换出。监视使用情况或用户活动的程序就是一个例子。

最后,父进程可能会希望挂起一个后代进程。例如,进程A 可以生成进程B,以执行文件读操作;随后,进程B 在读文件的过程中遇到错误,并报告给进程A;进程A 挂起进程B,调查错误的原因。

在所有这些情况中,挂起进程的活动都是由最初请求挂起的代理请求的。

3.3 进程描述

操作系统控制计算机系统内部的事件,它为处理器执行进程而进行调度和分派,给进程分配资源,并响应用户程序的基本服务请求。因此,我们可以把操作系统看做是管理系统资源的实体。

这个概念如图3.10 所示。在多道程序设计环境中,在虚拟内存中有许多已经创建了的进程,每个进程在执行期间,需要访问某些系统资源,包括处理器、I/O 设备和内存。在图中,进程P1 正在运行,该进程至少有一部分在内存中,并且还控制着两个I/O 设备;进程P2 也在内存中,但由于正在等待分配给P1 的I/O 设备而被阻塞;进程Pn 已经被换出,因此是挂起的。

以后几章中将探讨操作系统代表进程管理这些资源的细节。这里关心的是一些最基本的问题:操作系统为了控制进程和管理资源需要哪些信息?

操作系统(三) 
图3.10 进程和资源(某一时刻的资源分配)

3.3.1 操作系统的控制结构

操作系统为了管理进程和资源,必须掌握关于每个进程和资源当前状态的信息。普遍使用的方法是:操作系统构造并维护它所管理的每个实体的信息表。如图3.11,操作系统维护着4 种不同类型的表:内存、I/O、文件和进程

操作系统(三) 
图3.11 操作系统控制表的通用结构

一、内存表用于跟踪内(实)存和外存(虚拟内存)。内存的某些部分为操作系统而保留,剩余部分是进程可以使用的,保存在外存中的进程使用某种类型的虚拟内存或简单的交换机制。内存表必须包括以下信息:

  • 分配给进程的内存。
  • 分配给进程的外存。
  • 内存块或虚拟内存块的任何保护属性,如哪些进程可以访问某些共享内存区域。
  • 管理虚拟内存所需要的任何信息。

第三部分将详细讲述用于内存管理的信息结构。

二、操作系统使用I/O 表管理计算机系统中的I/O 设备和通道。在任何给定的时刻,一个I/O 设备或者是可用的,或者已分配给某个特定的进程,如果正在进行I/O 操作,则操作系统需要知道I/O 操作的状态和作为I/O 传送的源和目标的内存单元。在第11 章将详细讲述I/O 管理。

三、操作系统维护着文件表,这些表提供关于文件是否存在、文件在外存中的位置、当前状态和其他属性的信息。大部分信息(不是全部信息)可能由文件管理系统维护和使用。在这种情况下,操作系统只有一点或者没有关于文件的信息;在其他操作系统中,很多文件管理的细节由操作系统自己管理。这方面的内容将在第12 章讲述。

四、最后,操作系统为了管理进程必须维护进程表。需要先明确两点:首先,尽管图3.11 给出了4 种不同的表,但是这些表必须以某种方式链接起来或交叉引用。内存、I/O 和文件是代表进程而被管理的,因此进程表中必须有对这些资源的直接或间接引用。文件表中的文件可以通过I/O 设备访问,有时它们也位于内存中或虚拟内存中。这些表自身必须可以被操作系统访问到,因此它们受制于内存管理。

其次,操作系统最初如何知道创建表?显然,操作系统必须有基本环境的一些信息,如有多少内存空间、I/O 设备是什么以及它们的标识符是什么等。这是一个配置问题,也就是说,当操作系统初始化后,它必须可以使用定义基本环境的某些配置数据,这些数据必须在操作系统之外,通过人的帮助或一些自动配置软件而产生。

3.3.2 进程控制结构

操作系统在管理和控制进程时,首先必须知道进程的位置以及在管理时所必需的进程属性(如进程ID、进程状态)。

进程位置

进程的物理表示是什么?

  • 进程最少必须包括一个或一组被执行的程序,与这些程序相关联的是局部变量、全局变量和任何已定义常量的数据单元。因此,一个进程至少包括足够的内存空间,以保存该进程的程序和数据;
  • 此外,程序的执行通常涉及用于跟踪过程调用和过程间参数传递的(见附录1B)。
  • 最后,与每个进程相关联的还有操作系统用于控制进程的许多属性,通常,属性的集合称做进程控制块。

程序、数据、栈和属性的集合称做进程映像(process image)(见表3.4进程映像中的典型元素)。

操作系统(三) 

进程映像的位置依赖于使用的内存管理方案。
最简单的情况,进程映像保存在邻近的或连续的存储块中。该存储块位于外存(通常是磁盘)中。如果操作系统要管理进程,其进程映像至少有一部分必须位于内存中,为执行该进程,整个进程映像必须载入内存中或至少载入虚拟内存中。因此,操作系统需要知道每个进程在磁盘中的位置,并且对于内存中的每个进程,需要知道其在内存中的位置。来看一下第2 章中CTSS 操作系统关于这个方案的一个稍微复杂些的变体。在CTSS 中,当进程被换出时,部分进程映像可能保留在内存中。因此,操作系统必须跟踪每个进程映像的哪一部分仍然在内存中。

现代操作系统假定分页硬件允许用不连续的物理内存来支持部分常驻内存的进程。在任何给定的时刻,进程映像的一部分可以在内存中,剩余部分可以在外存中。因此,操作系统维护的进程表必须表明每个进程映像中每页的位置。

图3.11 描绘了位置信息的结构。有一个主进程表,每个进程在表中都有一个表项,每一项至少包含一个指向进程映像的指针。如果进程映像包括多个块,则这个信息直接包含在主进程表中,或可以通过交叉引用内存表中的项得到。当然,这个描述是一般性描述,特定的操作系统将按自己的方式组织位置信息。

进程属性

每个进程的大量信息该信息可以保留在进程控制块中。不同的系统以不同的方式组织该信息。表3.5( 进程控制块中的典型元素) 列出了每个进程信息的简单分类。

可以把进程控制块信息分成三类:

  • 进程标识信息
  • 处理器状态信息
  • 进程控制信息
操作系统(三) 
操作系统(三) 

一、每个进程都分配了一个唯一的数字标识符。进程标识符可以简单地表示为主进程表(图3.11 所示)中的一个索引;否则,必须有一个映射,使得操作系统可以根据进程标识符定位相应的表。操作系统控制的许多其他表可以使用进程标识符交叉引用进程表。例如,内存表可以组织起来以便提供一个关于内存的映射,指明每个区域分配给了哪个进程。I/O 表和文件表中也会有类似的引用。当进程相互之间进行通信时,进程标识符可用于通知操作系统某一特定通信的目标;当允许进程创建其他进程时,标识符可用于指明每个进程的父进程和后代进程。

除了进程标识符,还给进程分配了一个用户标识符,用于标明拥有该进程的用户。

二、处理器状态信息包括处理器寄存器的内容。当一个进程正在运行时,其信息当然在寄存器中。当进程被中断时,所有的寄存器信息必须保存起来,使得进程恢复执行时这些信息都可以被恢复。所涉及的寄存器的种类和数目取决于处理器的设计。在典型情况下,寄存器组包括用户可见寄存器、控制和状态寄存器和栈指针,这些在第1 章中都曾介绍过。

处理器都包括一个或一组通常称做程序状态字( PSW)的寄存器,它包含状态信息。PSW 通常包含条件码和其他状态信息。Pentium处理器中的处理器状态字就是一个很好的例子,它称做EFLAGS 寄存器(如图3.12 和表3.6 所示),可被运行在Pentium 处理器上的任何操作系统(包括UNIX 和Windows)使用。

三、进程控制信息是操作系统控制和协调各种活动进程所需要的额外信息。表3.5 表明了这类信息的范围,随后章节将进一步详细分析。

图3.13 给出了虚拟内存中进程映像的结构。每个进程映像包括一个进程控制块、用户栈、进程的专用地址空间以及与别的进程共享的任何其他地址空间。在这个图中,每个进程映像表现为一段地址相邻的区域。在实际的实现中可能不是这种情况,这取决于内存管理方案和操作系统组织控制结构的方法。

操作系统(三) 
图3.12 PentiumⅡEFLAGS 寄存器

表3.6 Pentium EFLAGS 寄存器位

操作系统(三) 

正如表3.5 中所指出的,进程控制块还可能包含构造信息,包括将进程控制块链接起来的指针。因此,前一节中所描述的队列可以由进程控制块的链表实现,例如,图3.8a 中的排队结构可以按图3.14 中的方式实现。

进程控制块的作用

每个进程控制块包含操作系统所需要的关于进程的所有信息。实际上,操作系统中的每个模块,包括那些涉及调度、资源分配、中断处理、性能监控和分析的模块,都可能读取和修改它们。可以说,资源控制块集合定义了操作系统的状态。

操作系统(三) 
(点击查看大图)图3.13 虚拟内存中的用户进程

操作系统中的很多例程都需要访问进程控制块中,直接访问这些表并不难,每个进程都有一个唯一ID 号,可用作进程控制块指针表的索引。困难的不是访问而是保护,具体表现为下面两个问题:

  • 一个例程(如中断处理程序)中有错误,可能会破坏进程控制块,进而破坏了系统对受影响进程的管理能力。
  • 进程控制块的结构或语义的设计变化可能会影响到操作系统中的许多模块。

这些问题可以通过要求操作系统中的所有例程都通过一个处理例程来专门处理,处理例程的任务仅仅是保护进程控制块,它是读写这些块的唯一的仲裁程序。使用这类进程,需要权衡性能问题和对系统软件剩余部分正确性的信任程度。

操作系统(三) 

图3.14 进程链表结构

3.4 进程控制

3.4.1 执行模式

首先需区分“与操作系统相关联的”以及“与用户程序相关联”的处理器执行模式。
某些指令只能在特权态下运行,包括读取或改变诸如程序状态字之类控制寄存器的指令、原始I/O 指令和与内存管理相关的指令。另外,有部分内存区域仅在特权态下可以被访问到。

  • 非特权态常称做用户态,用户程序通常在该模式下运行;
  • 特权态可称做系统态、控制态或内核态,内核态指的是操作系统的内核,是操作系统中包含重要系统功能的部分。

表3.7 操作系统内核的典型功能

操作系统(三) 

使用两种模式的原因:保护操作系统和重要的操作系统表(如进程控制块)不受用户程序的干涉。在内核态下,软件具有对处理器以及所有指令、寄存器和内存的控制能力,这一级的控制对用户程序不是必需的,且为了安全起见也不是用户程序可访问的。

这样产生了两个问题:处理器如何知道它正在什么模式下执行以及如何改变这一模式。
对第一个问题,程序状态字中有一位表示执行模式,这一位应某些事件的要求而改变。在典型情况下,当用户调用一个操作系统服务或中断触发系统例程的执行时,执行模式被设置成内核态,当从系统服务返回到用户进程时,执行模式被设置为用户态。例如64 位IA-64 体系结构的Intel Itanium 处理器,有一个处理器状态寄存器(PSR),包含2 位的CPL(当前特权级别)域,级别0 是最高特权级别,级别3 是最低特权级别。大多数操作系统,如Linux,使用级别0 作为内核态,使用另一个级别作为用户态。当中断发生时,处理器清空大部分PSR 中的位,包括CPL域,这将自动把CPL 设置为0。在中断处理例程结束时,最后的一个指令是irt(中断返回),这条指令使处理器恢复中断程序的PSR 值,也就是恢复了程序的特权级别。当应用程序调用一个系统调用时,会发生类似的情况。对于Itanium,应用程序使用系统调用是通过以下方式实现的:把系统调用标识符和参数放在一个预定义的区域,然后通过执行一个特殊的指令中断用户态程序的执行,并把控制权交给内核。

3.4.2 进程创建

讨论了与进程相关的数据结构后,可简单描述实际创建进程时的步骤。

一旦操作系统决定基于某种原因(见表3.1)创建一个新进程,可按以下步骤进行:

  • 给新进程分配一个唯一的进程标识符。此时,在主进程表中增加一个新表项,表中的每个新表项对应着一个进程。
  • 进程分配空间。这包括进程映像中的所有元素。操作系统须知道私有用户地址空间(程序和数据)和用户栈需要多少空间。可以根据进程的类型使用默认值,也可以在作业创建时根据用户请求设置。如果一个进程是由另一个进程生成的,则父进程可以把所需的值作为进程创建请求的一部分传递给操作系统。如果任何现有的地址空间被这个新进程共享,则必须建立正确的连接。最后,必须给进程控制块分配空间。
  • 初始化进程控制块。进程标识符部分;处理器状态信息部分的大多数项目通常初始化成0,除了程序计数器(被置为程序入口点)和系统栈指针(用来定义进程栈边界);进程控制信息部分的初始化基于标准默认值和为该进程所请求的属性。例如,进程状态在典型情况下被初始化成就绪或就绪/挂起;除非显式地请求更高的优先级,否则优先级的默认值为最低优先级;除非显式地请求或从父进程处继承,否则进程最初不拥有任何资源(I/O 设备、文件)。
  • 设置正确的连接。例如,如果操作系统把每个调度队列都保存成链表,则新进程必须放置在就绪或就绪/挂起链表中。
  • 创建或扩充其他数据结构。例如,操作系统可能为每个进程保存着一个记账文件,可用于编制账单和/或进行性能评估。

3.4.3 进程切换

表面看进程切换的功能是简单的。某一时刻,一个正在运行的进程被中断,操作系统指定另一个进程为运行态,并把控制权交给这个进程。

  • 什么事件触发进程的切换?
  • 模式切换与进程切换之间的区别。
  • 为实现进程切换,操作系统须对它控制的各种数据结构做些什么?

何时切换进程

进程切换在操作系统从当前正在运行的进程中获得控制权的任何时刻发生。表3.8 给出了可能把控制权交给操作系统的事件。

一、系统中断。大多数操作系统区分两种类型的系统中断:

  • 中断,与当前正在运行的进程无关的某种类型的外部事件相关,如完成一次I/O 操作;
  • 陷阱,与当前正在运行的进程所产生的错误或异常条件相关,如非法的文件访问。

对于普通中断,控制首先转移给中断处理器,它做一些基本的辅助工作,然后转到与已经发生的特定类型的中断相关的操作系统例程。参见以下例子:

  • 时钟中断:操作系统确定当前正在运行的进程的执行时间是否已经超过了最大允许时间段(时间片,即进程在被中断前可以执行的最大时间段),如果超过了,进程必须切换到就绪态,调入另一个进程。
  • I/O 中断:操作系统确定是否发生了I/O 活动。如果I/O 活动是一个或多个进程正在等待的事件,操作系统就把所有相应的阻塞态进程转换到就绪态(阻塞/挂起态进程转换到就绪/挂起态),操作系统必须决定是继续执行当前处于运行态的进程,还是让具有高优先级的就绪态进程抢占这个进程。
  • 内存失效:处理器访问一个虚拟内存地址,且此地址单元不在内存中时,操作系统必须从外存中把包含这个引用的内存块(页或段)调入内存中。在发出调入内存块的I/O 请求之后,操作系统可能会执行一个进程切换,以恢复另一个进程的执行,发生内存失效的进程被置为阻塞态,当想要的块调入内存中时,该进程被置为就绪态。

二、对于陷阱,操作系统确定错误或异常条件是否是致命的。如果是,当前正在运行的进程被转换到退出态,并发生进程切换;如果不是,操作系统的动作取决于错误的种类和操作系统的设计,其行为可以是试图恢复或通知用户,操作系统可能会进行一次进程切换或者继续执行当前正在运行的进程。

三、操作系统可能被来自正在执行的程序的系统调用**。例如,一个用户进程正在运行,并且正在执行一条请求I/O 操作的指令,如打开文件,这个调用导致转移到作为操作系统代码一部分的一个例程上执行。通常,使用系统调用会导致把用户进程置为阻塞态。

表3.8 进程执行的中断机制

操作系统(三) 

模式切换

中断阶段,处理器通过中断信号检查是否发生了任何中断。若没有未处理的中断,处理器继续取指令周期,即取当前进程中的下一条指令,若存在未处理的中断,处理器需要做以下工作:

  • 把程序计数器置成中断处理程序的开始地址
  • 从用户态切换到内核态,使得中断处理代码可以包含有特权的指令

处理器继续取指阶段,并取中断处理程序的第一条指令,它将给中断提供服务。此时,被中断的进程上下文保存在被中断程序的进程控制块中

保存的上下文环境包括什么?所有中断处理可能改变的信息和恢复被中断程序时所需要的信息。因此,须保存称做处理器状态信息的进程控制块部分,这包括程序计数器、其他处理器寄存器和栈信息。

还需要做些其他工作吗?取决于下一步会发生什么。中断处理程序通常执行与中断相关的基本任务的小程序。例如,它重置表示出现中断的标志或指示器。可能给产生中断的实体如I/O 模块发送应答。它还做一些与产生中断的事件结果相关的基本辅助工作。例如,如果中断与I/O 事件有关,中断处理程序将检查错误条件;如果发生了错误,中断处理程序给最初请求I/O操作的进程发一个信号。如果是时钟中断,处理程序将控制移交给分派器,当分配给当前正在运行进程的时间片用尽时,分派器将控制转移给另一个进程。

进程控制块中的其他信息如何处理?如果中断之后是切换到另一个应用程序,则需要做一些工作。但是,在大多数操作系统中,中断的发生并不是必须伴随着进程切换的。可能是中断处理器执行之后,当前正在运行的进程继续执行。在这种情况下,所需要做的是当中断发生时保存处理器状态信息,当控制返回给这个程序时恢复这些信息。在典型情况下,保存和恢复功能由硬件实现。

进程状态的变化

显然,模式切换与进程切换是不同的。发生模式切换可以不改变正处于运行态的进程状态,这种情况下,保存上下文环境和以后恢复上下文环境只需要很少的开销。但是,如果当前正在运行的进程被转换到另一个状态(就绪、阻塞等),则操作系统必须使其环境产生实质性的变化,完整的进程切换步骤如下:

  • 保存处理器上下文环境,包括程序计数器和其他寄存器。
  • 更新当前处于运行态进程的进程控制块,包括将进程的状态改变到另一状态(就绪态、阻塞态、就绪/挂起态或退出态)。还必须更新其他相关域,包括离开运行态的原因和记账信息。
  • 将进程的进程控制块移到相应的队列(就绪、在事件i 处阻塞、就绪/挂起)。
  • 选择另一个进程执行,这方面的内容将在本书的第四部分探讨
  • 更新所选择进程的进程控制块,包括将进程的状态变为运行态。
  • 更新内存管理的数据结构,这取决于如何管理地址转换,这方面的内容将在第三部分探讨。
  • 恢复处理器在被选择的进程最近一次切换出运行状态时的上下文环境,这可以通过载入程序计数器和其他寄存器以前的值来实现。

因此,进程切换涉及状态变化,因而比模式切换需要做更多的工作。

3.5 操作系统的执行

操作系统的两个特殊事实:

  • 操作系统与普通的计算机软件以同样的方式运行,也就是说,它也是由处理器执行的一个程序。
  • 操作系统经常释放控制权,并且依赖于处理器恢复控制权。

若操作系统仅仅是一组程序,那么操作系统是一个进程吗?如果是,如何控制它?这些有趣的问题列出了大量的设计方法,图3.15 给出了在当代各种操作系统中使用的各种方法。

3.5.1 无进程的内核

老操作系统中,传统和通用的方法是在所有的进程之外执行操作系统内核(见图3.15a)。通过这种方法,在当前正运行的进程被中断或产生一个系统调用时,该进程的模式上下文环境被保存起来,控制权转交给内核。操作系统有自己的内存区域和系统栈,用于控制过程调用和返回。操作系统可以执行任何预期的功能,并恢复被中断进程的上下文,这将导致被中断的用户进程重新继续执行。或者,操作系统可以完成保存进程环境的功能,并继续调度和分派另一个进程,是否这样做取决于中断的原因和当前的情况。

无论哪种情况,其关键点是进程的概念仅仅适用于用户程序,操作系统代码作为一个在特权模式下工作的独立实体被执行。

3.5.2 在用户进程中执行

在较小的机器的操作系统中,常见的方法是在用户进程的上下文中执行几乎所有操作系统软件。其观点是操作系统从根本上说是用户调用的一组例程,在用户进程环境中执行,用于实现各种功能,如图3.15b 所示。在任何时刻,操作系统管理着n 个进程映像,每个映像不仅包括图3.13 中列出的区域,而且还包括内核程序的程序、数据和栈区域。

操作系统(三) 
图3.15 操作系统和用户进程的关系

图3.16 给出了这个策略下的一个典型的进程映像结构。当进程在内核态下时,独立的内核栈用于管理调用/返回。操作系统代码和数据位于共享地址空间中,被所有的用户进程共享。

操作系统(三) 
图3.16 进程映像:操作
系统在用户空间中执行

当发生一个中断、陷阱或系统调用时,处理器被置于内核态,控制权转交给操作系统。为了将控制从用户程序转交给操作系统,需要保存模式上下文环境并进行模式切换,然后切换到一个操作系统例程,但此时仍然是在当前用户进程中继续执行,因此,不需要执行进程切换,仅在同一个进程中进行模式切换。

如果操作系统完成其操作后,确定需要继续运行当前进程,则进行一次模式切换,在当前进程中恢复被中断的程序。该方法的重要优点之一是,一个用户程序被中断以使用某些操作系统例程,然后被恢复,所有这些不用以牺牲两次进程切换为代价。如果确定需要发生进程切换而不是返回到先前执行的程序,则控制权被转交给进程切换例程,这个例程可能在当前进程中执行,也可能不在当前进程中执行,这取决于系统的设计。在某些特殊的情况下,如当前进程必须置于非运行态,而另一个进程将指定为正在运行的进程。为方便起见,这样一个转换过程在逻辑上可以看做是在所有进程之外的环境中被执行的。

在某种程度上,对操作系统的这种看法是非常值得注意的。在某些时候,一个进程可以保存它的状态信息,从就绪态进程中选择另一个进程,并把控制权释放给这个进程。之所以说这是一种混杂的情况,是因为在关键时候,在用户进程中执行的代码是共享的操作系统代码而不是用户代码。基于用户态和内核态的概念,即使操作系统例程在用户进程环境中执行,用户也不能篡改或干涉操作系统例程。这进一步说明进程和程序的概念是不同的,它们之间不是一对一的关系。在一个进程中,用户程序和操作系统程序都有可能执行,而在不同用户进程中执行的操作系统程序是相同的。

3.5.3 基于进程的操作系统

图3.15c 中显示的是另一种选择,即把操作系统作为一组系统进程来实现。与其他选择一样,作为内核一部分的软件在内核态下执行。不过在这种情况下,主要的内核函数被组织成独立的进程,同样,还可能有一些在任何进程之外执行的进程切换代码。

这种方法有几个优点。它利用程序设计原理,促使使用模块化操作系统,并且模块间具有最小的、简明的接口。此外,一些非关键的操作系统函数可简单地用独立的进程实现,例如,很早就曾经提到过的用于记录各种资源(处理器、内存、通道)的使用程度和系统中用户进程的执行速度的监控程序。由于这个程序没有为任何活动进程提供特定的服务,它只能被操作系统调用。作为一个进程,这个函数可以在指定的优先级上运行,并且在分派器的控制下与其他进程交替执行。最后,把操作系统作为一组进程实现,在多处理器或多机环境中都是十分有用的,这时一些操作系统服务可以传送到专用处理器中执行,以提高性能。

3.6 安全问题

操作系统对于每个进程都关联了一套权限。这些权限规定了进程可以获取哪些资源,包括内存区域、文件和特权系统指令等。典型的是,一个进程的运行代表着一个用户拥有操作系统认证的权限。在配置的时候,一个系统或者是一个实用进程就被分配了权限。

在一个典型的系统中,*别的权限指的是管理员、超级用户或根用户的访问权限。根用户的访问权限提供了对一个操作系统所有的功能和服务的访问。一个有着根用户访问权限的进程可以安全地控制一个系统,可以增加或者改变程序和文件,对其他进程进行监控,发送和接收网络流量和改变权限。

设计任何操作系统的一个关键问题是阻止或者至少是探测一个用户或者是一种恶意软件(恶意程序)获得系统授权的权限的企图,尤其是从根用户获取。本节,我们将简短地总结关于这种安全问题的威胁和对策。在第七部分将对其做更加详细的阐述。

3.6.1 系统访问威胁

系统访问威胁分为两大类:入侵者和恶意软件。

入侵者

对于安全,一个最普通的威胁就是入侵者(另外一个是病毒),通常是指黑客和解密高手。在早期的一项对入侵的重要研究中,Anderson [ANDE80] 定义了三种类型的入侵者:

冒充者:没有授权的个人去使用计算机和通过穿透系统的访问控制去使用一个合法用户的账号。

滥用职权者:一个合法的用户访问没有授权的数据、程序或资源,或者用户具有这种访问的授权,但是滥用了他的权限。

秘密用户:一个用户获得了系统的管理控制,然后使用这种控制来逃避审计和访问控制,或者废止审查收集。

冒充者有可能就是外部人员;滥用职权者一般都是内部人员;秘密用户可能是外部人员也可能是内部人员。

入侵者的攻击有良性的也有严重的。在良性阶段,许多人仅仅是想浏览互联网并想知道在互联网上到底有什么。在严重阶段,这些人尝试着去读权限数据,修改未授权的数据或者是破坏系统。

入侵者的目的是获得一个系统的访问权限,或是增加一个系统的权限获取的范围。最初许多攻击是利用了系统或软件的漏洞,这些漏洞可以让用户执行可以开启系统后门的代码。当程序以一定的权限运行,入侵者可以利用如缓冲溢出区攻击来获得系统的访问。将在7 章介绍缓冲区溢出攻击。

此外,入侵者也可以尝试获取那些已经被保护的信息。在一些情况下,信息就是在表框里面的用户密码。如果知道了一些用户的密码,那么入侵者可以登录一个系统,然后运行合法用户的所有权限。

恶意软件

计算机系统最为复杂的威胁可能就是利用计算机系统漏洞的程序。这些威胁被称为恶意软件,或者是恶意程序。在这一部分,我们将关注如编辑器、编译器和内核级程序等应用程序以及通用程序的威胁。

恶意软件分为两大类:一种需要宿主程序,一种则是独立的。对于前者,也被称为寄生,其本质上是一些不能独立于实际应用程序、通用程序或系统程序而存在的片段,例如病毒、逻辑炸弹和后门。后者则是独立并可以被操作系统调度和运行的程序,例如蠕虫和僵尸程序。

我们也可以对这些软件威胁进行以下的区分:一种不能进行复制,而另外一种则可以。前者是通过触发器**的程序或者程序片段,例如,逻辑炸弹、后门和僵尸程序。后者由一个程序片段或者一个独立的程序构成,当其运行后,可能产生一个或者多个它自己的副本,这些副本将在同一系统或其他系统中被**,例如病毒和蠕虫。

比较而言,恶意软件可能是无害的,或者表现为一个或多个有害的操作,这些有害的操作包括毁坏内存里的文件和数据,通过绕开控制而获得权限访问和为入侵者提供一种绕开访问控制的方法。

3.6.2 对抗措施

入侵检测

RFC2828(网络安全术语表)对入侵检测的定义如下:入侵检测是一种安全服务,通过监控和分析系统事件发现试图通过未经授权的方法访问系统资源的操作,并对这种操作提供实时或者准实时的警告。

入侵检测系统(IDS)可以分为如下几类:

基于宿主的IDS:对于可疑活动,监控单个宿主的特征和发生在宿主上的事件。

基于网络的IDS:监控特定的网络段或者设备网络流量,分析网络、传输和应用协议来辨别可疑活动。

IDS 由三个部分组成:

感应器:感应器负责收集数据。感应器的输入可能是系统的任一部分,输入可能包含了侵扰的证据。典型的感应器输入包括网络包、日志文件和系统调用记录。感应器收集这些信息,并把这些信息发送给分析器。

分析器:分析器从一个或多个感应器或者其他分析器接受输入数据。分析器负责确定入侵是否发生。这部分的输出表明了一个入侵已经发生。输出可能包含了支持入侵发生的结论的证据。分析器可能提供对于入侵结果采取何种操作的指导。

用户界面:IDS 的用户界面可以让用户查看系统的输出和控制系统的行为。在一些系统中,用户界面可以等同于负责人、控制器或者控制台部分。

入侵检测系统用来侦测人类入侵者行为,以及恶意软件的行为。

认证

在许多计算机安全内容中,用户认证是一个主要的构建模块和最初防线。用户认证是许多种访问控制和用户责任的主要部分。RFC2828 对用户认证做了如下定义:

系统实体定义了验证和确认的过程。认证过程包括以下两步:

确认步骤:对于安全系统,提出了标识符。(应小心地分配标识符,对于访问控制服务等其他安全服务,认证定义是基本部分。)

验证步骤:提出或产生认证信息,用来证实在实体与标识符之间的绑定。

例如,用户Alice Toklas 拥有一个用户标识符ABTOKLAS。标识符信息可能被存储在Alice想要使用的任意一台服务器或者计算机系统中,而且系统管理员和其他用户可能知道这些信息。一种典型的与用户ID 相关联的认证信息项就是密码,密码是用于保守秘密的(秘密仅仅被Alice和系统所知)。如果没有人得到或猜出Alice 的密码,管理员可以通过Alice 的用户ID 和密码的结合建立Alice 的访问许可,并审核Alice 的操作。由于Alice 的ID 不是秘密,系统用户可以给她发送电子邮件,但是由于她的密码是保密的,所以没有人可以冒充成Alice。

本质上,识别是这样一种方法:用户向系统提供一个声明的身份;用户认证就是确认声明的合法性的方法。

一共有4 种主要的认证用户身份的方法,它们既可以单独使用,也可以联合使用:

个人知道的一些事物:例如包括密码、个人身份号码(PIN)或者是预先安排的一套问题的答案。

个人拥有的一些事物:例如包括电子通行卡、智能卡、物理钥匙。这种类型的身份验证称为令牌。

个人自身的事物(静态生物识别技术):例如包括指纹、虹膜和人脸的识别。

个人要做的事物(动态生物识别技术):例如包括语音模式、笔迹特征和输入节奏的识别。

适当的实现和使用所有的这些方法,可以提供可靠的用户认证。但是每个方法都有问题,使得对手能够猜测或盗取密码。类似地,用户能够伪造或盗取令牌。用户可能忘记密码或丢失令牌。而且,对于管理系统上的密码、令牌信息和保护系统上的这些信息,还存在显著的管理开销。对于生物识别技术,也有各种各样的问题,包括误报和假否定、用户接受程度、费用和便利与否。

访问控制

访问控制实现了一种安全策略:指定谁或何物(例如进程的情况)可能有权使用特定的系统资源和在每个场景下被允许的访问类型。

访问控制机制调节了用户(或是代表用户执行的进程)与系统资源之间的关系,系统资源包括应用程序、操作系统、防火墙、路由器、文件和数据库。系统首先要对寻求访问的用户进行认证。通常,认证功能完全决定了用户是否被允许访问系统。然后访问控制功能决定了是否允许用户的特定访问要求。安全管理员维护着一个授权数据库,对于允许用户对何种资源采用什么样的访问方式,授权数据库做了详细说明。访问控制功能参考这个数据库来决定是否准予访问。审核功能监控和记录了用户对于系统资源的访问。

防火墙

对于保护本地系统或系统网络免于基于网络的安全威胁,防火墙是一种有效的手段,并且防火墙同时提供了经过广域网和互联网对外部网络的访问。传统上,防火墙是一种专用计算机,是计算机与外部网络的接口;防火墙内部建立了特殊的安全预防措施用以保护网络中计算机上的敏感文件。其被用于服务外部网络,尤其是互联网、连接和拨号线路。使用硬件或软件来实现,并且与单一的工作站或者PC 连接的个人防火墙也很常见。

[BELL94]列举了如下防火墙的设计目标:

1)从内部到外部的通信必须通过防火墙,反之亦然。通过对除经过防火墙之外本地网络的所有访问都进行物理阻塞来达到目的。可以对其进行各种各样的配置,将在本章的后面部分进行阐述。

2)仅仅允许本地安全策略定义的授权通信通过。使用的不同类型防火墙是通过不同类型的安全策略实现的。

3)防火墙本身对于渗透是免疫的。这意味着在一个安全的操作系统上使用强固系统。值得信赖的计算机系统适合用作防火墙,并且在管理应用中也被要求使用。

3.7 UNIX SVR4 进程管理

UNIX 系统V 使用了一种简单但是功能强大的进程机制,且对用户可见。UNIX 采用图3.15b 中的模型,其中大部分操作系统在用户进程环境中执行。UNIX 使用两类进程,即系统进程和用户进程。系统进程在内核态下运行,执行操作系统代码以实现管理功能和内部处理,如内存空间的分配和进程交换;用户进程在用户态下运行以执行用户程序和实用程序,在内核态下运行以执行属于内核的指令。当产生异常(错误)或发生中断或用户进程发出系统调用时,用户进程可进入内核态。

3.7.1 进程状态

UNIX 操作系统*有9 种进程状态,如表3.9 所示。图3.17(基于[BACH86]中的图)是相应的状态转换图,它与图3.9b 类似,有两个UNIX 睡眠状态对应于图3.9b 中的两个阻塞状态,其区别可简单概括如下:

UNIX 采用两个运行态表示进程在用户态下执行还是在内核态下执行。

UNIX 区分内存中就绪态和被抢占态这两个状态。从本质上看,它们是同一个状态,如图中它们之间的虚线所示,之所以区分这两个状态是为了强调进入被抢占状态的方式。当一个进程正在内核态下运行时(系统调用、时钟中断或I/O 中断的结果),内核已经完成了其任务并准备把控制权返回给用户程序时,就可能会出现抢占的时机。这时,内核可能决定抢占当前进程,支持另一个已经就绪并具有较高优先级的进程。在这种情况下,当前进程转换到被抢占态,但是为了分派处理,处于被抢占态的进程和处于内存中就绪态的进程构成了一条队列。

操作系统(三) 
图3.17 UNIX 进程状态转换图

只有当进程准备从内核态移到用户态时才可能发生抢占,进程在内核态下运行时是不会被抢占的,这使得UNIX 不适用于实时处理。有关实时处理需求的讨论请参见第10 章。

UNIX 中有两个独特的进程。进程0 是一个特殊的进程,是在系统启动时创建的。实际上,这是预定义的一个数据结构,在启动时刻被加载,是交换进程。此外,进程0 产生进程1,称做初始进程,进程1 是系统中的所有其他进程的祖先。当新的交互用户登录到系统时,由进程1为该用户创建一个用户进程。随后,用户进程可以创建子进程,从而构成一棵分支树,因此,任何应用程序都是由一组相关进程组成的。

表3.9 UNIX 进程状态

操作系统(三) 

 

3.7.2 进程描述

UNIX 中的进程是一组相当复杂的数据结构,它给操作系统提供管理进程和分派进程所需要的所有信息。表3.10 概括了进程映像中的元素,它们被组织成三部分:用户上下文、寄存器上下文和系统级上下文。

表3.10 UNIX 进程映像

 

操作系统(三) 

 

用户级上下文包括用户程序的基本成分,可以由已编译的目标文件直接产生。用户程序被分成正文和数据两个区域,正文区是只读的,用于保存程序指令。当进程正在执行时,处理器使用用户栈进行过程调用和返回以及参数传递。共享内存区是与其他进程共享的数据区域,它只有一个物理副本,但是通过使用虚拟内存,对每个共享进程来说,共享内存区看上去好像在它们各自的地址空间中一样。当进程没有运行时,处理器状态信息保存在寄存器上下文中。

系统级上下文包含操作系统管理进程所需要的其余信息,它由静态部分和动态部分组成,静态部分的大小是固定的,贯穿于进程的生命周期;动态部分在进程的生命周期中大小可变。静态部分的一个成分是进程表项,这实际上是由操作系统维护的进程表的一部分,每个进程对应于表中的一项。进程表项包含对内核来说总是可以访问到的进程控制信息。因此,在虚拟内存系统中,所有的进程表项都在内存中,表3.11 中列出了进程表项的内容。用户区,即U 区,包含内核在进程的上下文环境中执行时所需要的额外的进程控制信息,当进程调入或调出内存时也会用到它。表3.12 给出了这个表的内容。

表3.11 UNIX 进程表项

 

操作系统(三) 

 

(续)

 

操作系统(三) 

 


表3.12 UNIX 的U 区

 

操作系统(三) 

 

进程表项和U 区的区别反映出UNIX 内核总是在某些进程的上下文环境中执行,大多数时候,内核都在处理与该进程相关的部分,但是,某些时候,如当内核正在执行一个调度算法,准备分派另一个进程时,它需要访问其他进程的相关信息。当给定进程不是当前进程时,可以访问进程控制表中的信息。

系统级上下文静态部分的第三项是本进程区表,它由内存管理系统使用。最后,内核栈是系统级上下文环境的动态部分,当进程正在内核态下执行时需要使用这个栈,它包含当发生过程调用或中断时必须保存和恢复的信息。

3.7.3 进程控制

UNIX 中的进程创建是通过内核系统调用fork()实现的。当一个进程产生一个fork 请求时,操作系统执行以下功能[BACH86]:

1)为新进程在进程表中分配一个空项。

2)为子进程赋一个唯一的进程标识符。

3)做一个父进程上下文的逻辑副本,不包括共享内存区。

4)增加父进程拥有的所有文件的计数器,以表示有一个另外的进程现在也拥有这些文件。

5)把子进程置为就绪态。

6)向父进程返回子进程的进程号;对子进程返回零。

所有这些操作都在父进程的内核态下完成。为当内核完成这些功能后可以继续下面三种操作之一,它们可以认为是分派器例程的一个部分:

在父进程中继续执行。控制返回用户态下父进程进行fork 调用处。

处理器控制权交给子进程。子进程开始执行代码,执行点与父进程相同,也就是说在fork调用的返回处。

控制转交给另一个进程。父进程和子进程都置于就绪状态。

很难想象这种创建进程的方法中父进程和子进程都执行相同的代码。其区别在于:当从fork中返回时,测试返回参数,如果值为零,则它是子进程,可以转移到相应的用户程序中继续执行;如果值不为零,则它是父进程,继续执行主程序。

3.8 小结

现代操作系统中最基本的构件是进程,操作系统的基本功能是创建、管理和终止进程。当进程处于活跃状态时,操作系统必须设法使每个进程都分配到处理器执行时间,并协调它们的活动、管理有冲突的请求、给进程分配系统资源。

为执行进程管理功能,操作系统维护着对每个进程的描述,或者称为进程映像,它包括执行进程的地址空间和一个进程控制块。进程控制块含有操作系统管理进程所需要的所有信息,包括它的当前状态、分配给它的资源、优先级和其他相关数据。

在整个生命周期中,进程总是在一些状态之间转换。最重要的状态有就绪态、运行态和阻塞态。一个就绪态进程是指当前没有执行但已做好了执行准备的进程,只要操作系统调度到它就立即可以执行;运行态进程是指当前正在被处理器执行的进程,在多处理器系统中,会有多个进程处于这种状态;阻塞态进程正在等待某一事件的完成,如一次I/O 操作。

一个正在运行的进程可被一个在进程外发生且被处理器识别的中断事件打断,或者被执行操作系统的系统调用打断。不论哪种情况,处理器都执行一次模式切换,把控制转交给操作系统例程。操作系统在完成必需的操作后,可以恢复被中断的进程或者切换到别的进程。

 

转载于:https://my.oschina.net/cqlcql/blog/689229