操作系统-进程

说明: 全文大部分都是《操作系统-精髓与设计原理 第八版》的原文,自己做了一些删改,使其更易于理解。

如果说操作系统是围绕进程这一概念创建的,有些言过其实,但进程在操作系统中的地位也至少是举足轻重的。

计算机的目的是要完成任务,而任务的完成需要一个或者多个进程的执行。操作系统维护了这些进程的运行,因为进程之间不但需要配合和调度,也需要使用计算机系统资源(操作系统抽象了计算机的系统资源,如文件系统,内存,网络接口等等),使用相同的资源可能就会产生冲突,操作系统就需要调节。在第5节,你将看到围绕进程实现的各种不同的操作系统的方法。

主要内容:

  • 操作系统表示和控制进程的方式
  • 进程状态,进程状态描述进程的行为特征
  • 操作系统表示每个进程的状态所需的数据结构
  • 操作系统为实现其目标所需要的进程的其他特征
  • 操作系统使用这些数据结构控制进程的方式
  • 一个例子:UNIX SVR4中的进程模型

1.什么是进程

可以把进程视为由一组元素组成的实体,进程的两个基本元素是:

  • 程序代码
  • 代码关联的数据集

如果进程处在运行之中,又由以下元素来表征:

  1. 标识符
  2. 状态
  3. 优先级
  4. 程序计数器
  5. 内存指针:包括程序代码和进程相关数据指针,以及与其他进程共享内存块指针
  6. 上下文数据: 处理器寄存器中的值
  7. I/O状态信息
  8. 记账信息:包括处理器时间总和,使用的时钟数总和,时间限制,记账号

以上信息存放在一个称为进程控制块的数据结构中,在后面我们将会看到进程控制块远不止只有这些信息。进程控制块由操作系统创建和管理。

进程由程序代码及其相关数据以及进程控制块组成,称为进程映像。

进程控制块包含了进程的充分信息,计算机中断后,会把程序计数器和处理器寄存器保存到进程控制块的相应位置,如果不修改进程状态,再次运行的时候,程序计数器和处理器寄存器值恢复。

2.进程状态

在本节中我们将通过对进程状态的描述和探索,来建立一个进程行为模型,并为操作系统实现提供系统方法。

2.1 两状态进程模型:

我们知道进程要么正在执行要么不在执行,所以我们可以构建最简单的模型,进程可处于两种状态:运行态和未运行态。那么我们就知道了,操作系统必须要以某种方式来表示每个进程,使得操作系统可以跟踪到它,这里给出了一个简单的模型:
两状态模型
图中队列元素指向某个进程,等待着被调度执行。这只是最简单的操作系统进程控制模型,我们知道这样的模型是远远不够的,在对两状态模型进行改进之前,我们来了解一下进程的创建和终止,因为无论使用哪种模型,进程的生存其都是围绕着进程的创建和终止。

关于创建:

进程的创建过程:

  • 将一个新进程添加到正被管理的进程集。
  • 操作系统建立管理进程的数据结构,并在内存中分配地址空间。

以上两点构成了进程的创建过程,详细过程可以查看4.2。

触发进程创建的事件:

  • 新的批处理作业:磁带或磁盘中的批处理作业控制流通常会提供给操作系统。当操作系统准备接收新工作时,将读取下一个作业控制命令。
  • 交互登录:终端用户登录到系统
  • 为提供服务而由操作系统创建:操作系统可以创建一个进程,代表用户程序执行一个功能,使用户无须等待(如控制打印的进程)
  • 由现有进程派生:基于模块化的考虑或开发并行性,用户程序可以指示创建多个进程

关于终止:

任何一个计算机都必须为进程提供表示其完成的方法。比如用户结束一个应用程序,将会给操作系统发出一个服务请求,以终止发出请求的进程。进程除了正常完成终止外,还包括
程序内部故障条件和错误导致进程终止:

  • 超过时限,无可用内存,内存访问超出范围,保护系统资源错误,算术错误,I/O失败,无效指令,特权指令,数据误用。

外部干涉终止:

  • 操作员或操作系统干涉(如出现死锁),父进程终止,父进程请求终止子进程。

2.2 五状态进程模型(改进一):

如果所有进程都做好了执行的准备,那么两状态进程模型给出的排队原则是有效的,但是我们知道

  • 有些进程可能在阻塞等待I/O之中,
  • 使用单个队列的话,我们不能只考虑选择队列中最老的进程,而是应该扫描这个列表,选择未被阻塞且在队列中时间最长的进程

所以我们将两状态进程模型中的非运行态分为就绪态和阻塞态,此外还增加了两个很有用的状态,他们一共组成了以下五个状态:

  1. 运行态:进程正在执行。本章中假设计算机只有一个处理器,因此一次最多只有一个进程处于这一状态。
  2. 就绪态:进程做好了准备,只要有机会就开始执行。
  3. 阻塞/等待态:进程在某些事件发生前不能执行,如I/O操作完成。
  4. 新建态:刚刚创建的进程,操作系统还未把它加入可执行进程组,它通常是进程控制块已经创建但还未加载到内存中的新进程。
  5. 退出态:操作系统从可执行进程组中释放出的进程,要么它自身已停止,要么它因某种原因被取消。退出态为辅助程序(可能是统计进程信息或则其他功能)提供了提取信息的时间,如果信息提取完成,那么将会从系统删除进程。
    五状态进程模型

这里再给给出图中未画出的两种状态转换说明:

  • 就绪->退出:为清楚起见,状态图中未表示这种转换。在某些系统中,父进程可在任何时刻终止一个子进程。如果父进程终止,那么与该父进程相关的所有子进程都将被终止。
  • 阻塞->退出:前一项给出了注释。

对五状态进程模型的再改进:

既然有了阻塞态,为了更加高效的进行进程调度,我们可以维护两个队列:就绪队列和阻塞队列。进入系统的每个进程都放置在就绪队列中,当操作系统选择另一个进程运行时,将从就绪队列中进行选择。对于无优先级的方案,这可以是一个简单的先进先出队列。当一个正在运行的进程被移出处理器时,它根据情况要么终止,要么放置在就绪或阻塞队列中。最后,当一个事件发生时,所有位于阻塞队列中等待该事件的进程都被放到就绪队列中。

但是这里我们可以非常容易的想到在阻塞队列中当一个事件发生时,操作系统必须扫描整个阻塞队列,搜索那些等待该事件的进程。在大型操作系统中,队列中可能有几百甚至几千个进程,此时拥有多个队列将会很有效,一个事件可以对应一个队列。因此,事件发生时,相应队列中的所有进程都将转换到就绪态。

依照此思想,我们还可以维护多个就绪队列,每个优先级一个队列,将会带来很大的便利。操作系统很容易就可确定哪个就绪进程具有最高优先级且等待时间最长。

2.3 进程挂起态的加入(改进二)

交换的需要
前面介绍的三个基本状态(就绪态、运行态和阻塞态)提供了一种为进程行为建立模型并指导操作系统实现的系统方法。许多实际的操作系统都是按照这三种状态具体构建的。

但是,我们可以证明向模型中增加其他状态也是合理的。为了说明加入新状态的好处,考虑一个未使用虚存的系统,每个被执行的进程必须完全载入内存。

回忆可知,机器变得复杂的原因是,I/O活动远慢于计算速度使得单道程序系统中的处理器大多数时间处于空闲状态。此时,内存中保存有多个进程,当一个进程被阻塞时,处理器可移向另一个进程,但由于处理器远快于I/0,会出现内存中的所有进程都在等待I/O的现象。因此,即便是多道程序设计,处理器多数时间仍可能处于空闲状态。

  • 解决方案之一是扩充内存来容纳更多的进程,但这种方法有两个缺点。首先是内存的价格问题,当内存大小增加到兆位及千兆位时,价格也会随之增加;其次是程序对内存空间需求的增长速度要快于内存价格的下降速度。因此,更大的内存往往会导致更大的进程而非更多的进程。

  • 解决方案之二是交换,即把内存中某个进程的一部分或全部移到磁盘中。当内存中不存在就绪态的进程时,操作系统就把被阻塞的进程换出到磁盘中的挂起队列(suspend queue),即临时从内存中“踢出”的进程队列。操作系统此后要么从挂起队列中取出另一个进程,要么接受一个新进程的请求,将其放入内存运行。

要使用前面介绍的交换,在进程行为模型中必须增加另一个状态:挂起态。当内存中的所有进程都处于阻塞态时,操作系统可把其中的一个进程置为挂起态,并将它转移到磁盘,此时内存所释放的空间就可被调入的另一个进程使用。操作系统执行换出操作后,将进程取到内存中的方式有两种:接纳一个新近创建的进程,或调入一个此前挂起的进程。显然,操作系统倾向于调入一个此前挂起的进程,并为它提供服务,而非增加系统的总负载数。

但这一推理也带来了一个难题,即所有已被挂起的进程都处于阻塞态。显然,这时把被阻塞的进程取回内存没有任何意义,因为它仍然未做好执行的准备。这时的进程状态转换为:
一个挂起态

但是,由于每个挂起的进程最初都阻塞在某个特定的事件上,因此发行该事件时,进程将不再阻塞而可以继续执行,因此我们可以使用两个挂起态,分别为就绪/挂起态,阻塞/挂起态。这时的进程状态转换为:
两个挂起态

在查看包含两个新挂起态的状态转换图前,必须注意迄今为止的论述都假设未使用虚存,进程要么都在内存中,要么都在内存外。使用虚存中,可能会执行只有部分内容在内存中的进程,若访问的进程地址不在内存中,则将进程的相应部分调入内存。使用虚存看上去不需要显式交换,因为通过处理器中的存储管理硬件,任何进程中的任何地址都可移入或移出内存。然而,若活动进程很多,且所有的进程都有一部分在内存中时,则可能会导致虚存系统崩溃。因此,即使是在虚存系统中,操作系统也需要不时地根据执行情况完全显式地换出进程。

这里就以下几个状态转换做出说明,其余请读者自行理解:

  • 就绪 -> 就绪/挂起:通常,操作系统更倾向于挂起阻塞态进程而非就绪态进程,因为就绪态进程可以立即执行,而阻塞态进程虽然占用了内存空间但不能执行。若释放内存来得到足够空间的唯一方法是挂起一个就绪态进程,则这种转换也是必需的。 此外,若操作系统确信高优先级的阻塞态进程很快将会就绪,则它可能会选择挂起一个低优先级的就绪态进程,而非一个高优先级的阻塞态进程。
  • 阻塞/挂起 -> 阻塞:这种转换在设计中很少见,原因是如果一个进程未准备好执行且不在内存中,调入它没有意义。但此时要考虑如下情况:一个进程终止后,会释放一些内存空间,而阻塞/挂起队列中有一个进程的优先级要比就绪/挂起队列中任何进程的优先级都高,并且操作系统有理由相信阻塞进程的事件很快就会发生。这时,把阻塞进程而非就绪进程调入内存是合理的。
  • 运行 -> 就绪/挂起:通常,当一个运行进程的分配时间到期后,它将转换到就绪态。但在阻塞/挂起队列中具有较高优先级的进程不再被阻塞时,操作系统会抢占这个进程,或直接把这个运行进程转换到就绪/挂起队形中,并释放一些内存空间。
  • 各种状态 -> 退出:典型情况下,一个进程的运行终止,要么是它已完成运行,要么是出现了一些错误条件。但在某些操作系统中,进程可被父进程终止,或在父进程终止时终止。若这种情况允许,则进程在任何状态下都可转换到退出态。

以上讨论的是内存的原因来挂起进程,实际上它还有以下以及其他多种原因来挂起进程,因此你也可以知道使用了挂起态的更多好处:

  • 其他OS原因:操作系统可能挂起后台进程或工具程序进程,或挂起可能会导致问题的进程(如死锁)
  • 交互式用户请求:用户希望挂起一个程序的执行,以便进行调试或关联资源的使用
  • 定时:进程可被周期性地执行(如记账或系统监视进程),并在等待下一个时间间隔时挂起
  • 父进程请求:父进程可能会希望挂起后代进程的执行,以检查或修改挂起的进程,或协调不同后代进程之间的行为

以上就是我们对进程状态的描述,以及我们最后构造出的五状态+两挂起态进程模型。操作系统可以依靠这个模型来构建系统。那么操作系统要控制进程在这些状态之间转换,需要哪些信息?接下来我们就将讨论这一基本问题,要控制进程并管理资源,操作系统需要哪些信息,即进程的描述。

3.进程描述

3.1操作系统的总体控制结构

操作系统为了管理进程和资源,必须掌握每个进程和资源的当前状态。普遍采用的方法是,操作系统构造并维护其管理的每个实体的信息表。图3.11给出了这种方法的大致范围,即操作系统维护的4种不同类型的表:内存、I/O、文件和进程。尽管不同操作系统的实现细节不同,但所有操作系统维护的信息基本都可以分为这4类。
操作系统的总体控制结构

  • 内存表(memory table):用于跟踪内(实)存和外(虚)存。内存的某些部分为操作系统保留,剩余部分供进程使用,外存中保存的进程使用某种虚存或简单的交换机制。内存表必须包含如下信息:
    ○ 分配给进程的内存。
    ○ 分配给进程的外存。
    ○ 内存块或虚存块的任何保护属性,如哪些进程可以访问某些共享内存区域。
    ○ 管理虚存所需要的任何信息。
  • I/O表:管理计算机系统中的I/O设备和通道。在任意给定时刻,某个I/O设备要么可用,要么已分配给特定的进程。正在进行I/0操作时,操作系统需要知道I/O操作的状态,以及作为I/O传送的源与目标的内存单元。
  • 文件表:操作系统还会维护文件表(file table)。文件表提供关于文件是否存在、文件在外存中的位置、当前状态和其他属性的信息。大部分信息(非全部信息)可能由文件管理系统维护和使用。此时,操作系统仅有少许或没有关于文件的信息;在其他操作系统中,文件管理的许多细节由操作系统本身管理。
  • 进程表:管理进程,也是接下来的主要内容

3.2 操作系统的进程控制结构

操作系统在管理和控制进程时,首先要知道进程的位置,其次要知道进程的属性(如进程ID、进程状态)。

进程位置

进程位置在处理进程位置和属性问题前,首先要解决一个更基本的问题:进程的物理表示是什么?进程最少必须包括一个或一组被执行的程序,而与这些程序相关联的是局部变量、全局变量和任何已定义常量的数据单元。因此,一个进程至少应有足够的内存空间来保存其的程序和数据。此外,程序的执行通常涉及用于跟踪过程调用和过程间参数传递的栈。最后,还有与每个进程相关的许多属性,以便操作系统控制该进程。通常,属性集称为进程控制块(process control block)。程序、数据、栈和属性的集合称为进程映像(process image)。这就解决了进程的物理内容是说明,也就才能在存储设备上有位置这一概念。

进程映像的位置取决于所用的内存管理方案。在最简单的情形下,进程映像保存在相邻的内存块中或连续的内存块中。存储块位于外存(通常是磁盘)中,因此在操作系统管理进程时,其进程映像至少应有一部分位于内存中。而要执行该进程,则必须将整个进程映像载入内存中或至少载入虚存中。因此,操作系统需要知道每个进程在磁盘中的位置,并知道每个进程在内存中的位置。这在涉及到虚拟内存的时候更加复杂。

图3.11(上图)给出了位置信息的结构。有一个主进程表,每个进程在主进程表中都有一个表项,每个表项至少包含一个指向进程映像的指针。如果进程映像包括多个块,则这一信息直接包含在主进程表中,或通过交叉引用内存表中的表项得到。当然,这种描述是一般性描述,不同操作系统会按自身的方式来组织位置信息。

进程属性

由之前的叙述我们知道,进程属性保存在进程控制块之中。这里我们就简要的介绍操作系统将会用到的信息。

进程控制块信息分为三类:

  • 进程标识信息
  • 进程状态信息
  • 进程控制信息

进程控制块信息

下图是虚拟内存中的进程映像,虽然这里看起来是连续的,但是实际上可能并非如此,但是对于程序编写者而言,这样理解进程的地址结构就可以了,其他的操作系统会帮我们搞定。
虚拟内存进程映像

此外我们可以看到在进程控制块中还有数据结构信息这一项,因此,可以使用队列实现进程控制块的链表,如下图所示,当然这种链表不是唯一的,例如你还可以将阻塞态的进程分别挂到不同事件阻塞的队列上:

进程链表示意

进程控制块的保护

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

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

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

这些问题可要求操作系统中的所有例程都通过一个处理程序例程来解决,即处理程序例程的任务仅是保护进程控制块,且是读写这些块的唯一仲裁程序,而不是简单的直接进行读写。使用这类进程时,需要在性能和其他系统软件对其的信任度之间进行折中。

4.进程控制

4.1 执行模式

大多数处理器至少支持两种执行模式。某些指令只能在特权模式下运行,包括读取或改变诸如程序状态字之类的控制寄存器的指令、原始I/O指令和与内存管理相关的指令。另外,部分内存区域仅能在特权模式下访问。

非特权模式通常称为用户模式(user mode),因为用户程序通常在该模式下运行;特权模式称为系统模式(system mode)、控制模式(control mode)或内核模式(kernel mode),内核模式指的是操作系统的内核,它是操作系统中包含重要系统功能的部分。操作系统内核的典型功能是:
操作系统内核典型功能

典型情况下,当用户调用一个操作系统服务或中断来触发系统例程的执行时,执行模式将被置为内核模式;而当从系统服务返回到用户进程时,执行模式则置为用户模式。不同的模式可以通过CPU寄存器来指示。

4.2进程创建

操作系统基于某种原因(之前说过)决定创建一个新进程时,会按如下步骤操作:

  1. 为新进程分配一个唯一的进程标识符。 此时,主进程表中会添加一个新表项,每个进程一个表项。
  2. 为进程分配空间。 这包括进程映像中的所有元素。因此,操作系统必须知道私有用户地址空间(程序和数据)和用户栈需要多少空间。默认情况下会根据进程的类型分配这些值,但也可在作业创建时基于用户请求设置这些值。若一个进程由另一个进程生成,则父进程可把所需的值作为进程创建请求的一部分传递给操作系统。若任何已有的地址空间将被这个新进程共享,则要建立正确的链接。最后,必须为进程控制块分配空间。
  3. 初始化进程控制块。 进程标识部分包括进程ID和其他相关的ID,如父进程的ID等;处理器状态信息部分的多数项目通常初始化为0,但程序计数器(置为程序入口点)和系统栈指针(定义进程栈边界)除外。进程控制信息部分根据标准的默认值和该进程请求的特性来初始化。例如,进程状态通常初始化为就绪或就绪/挂起。优先级默认情况下可设置为最低,除非显式请求了更高的优先级;进程最初不拥有任何资源(I/O设备、文件),除非显式地请求了这些资源,或继承了父进程的资源。
  4. 创建或扩充其他数据结构。 例如,操作系统可因编制账单和/或评估性能,为每个进程维护一个记账文件。

4.3进程切换

表面上看,进程切换很简单。在某个时刻,操作系统中断一个正在运行的进程,将另一个进程置于运行模式,并把控制权交给后者。然而,这会引发若干问题。首先,什么事件触发了进程的切换?其次,必须认识到模式切换与进程切换间的区别;最后,要实现进程切换,操作系统须对其控制的各种数据结构做些什么?

4.3.1 控制权转移到操作系统

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

大多数操作系统都会区分两种系统中断:一种称为中断,另一种称为陷阱。前者与当前正运行进程无关的某种外部事件相关,如完成一次I/O操作;后者与当前正运行进程产生的错误或异常条件相关,如非法的文件访问。

中断

对于普通中断(interrupt),控制权首先转给中断处理器,中断处理器完成一些基本的辅助工作后,再将控制权转给与已发生的特定中断相关的操作系统例程。示例如下:

  • 时钟中断:操作系统确定当前正运行进程的执行时间是否已超过最大允许时间段【时间片(time slice),即进程中断前可以执行的最大时间段】。若超过,进程就切换到就绪态,并调入另一个进程。
  • I/O中断:操作系统确定是否已发生I/O活动。若IVO活动是一个或多个进程正在等待的事件,则操作系统就把所有处于阻塞态的进程转换为就绪态(阻塞/挂起态进程转换为就绪/挂起态)。操作系统必须决定是继续执行当前处于运行态的进程,还是让具有高优先级的就绪态进程抢占这个进程。
  • 内存失效:处理器遇到一个引用不在内存中的字的虚存地址时,操作系统就必须从外存中把包含这一引用的内存块(页或段)调入内存。发出调入内存块的I/O请求后,内存失效进程将进入阻塞态;操作系统然后切换进程,恢复另一个进程的执行。期望的块调入内存后,该进程置为就绪态。

陷阱

对于陷阱(trap),操作系统则确定错误或异常条件是否致命。致命时,当前正运行进程置为退出态,并切换进程;不致命时,操作系统的动作将取决于错误的性质和操作系统的设计,操作系统可能会尝试恢复程序,或简单地通知用户。操作系统可能会切换进程,或继续当前运行的进程。

系统调用

最后,操作系统可被来自正执行程序的系统调用(supervisor call)激活。例如,正运行用户进程执行了一个请求I/O操作的指令(如打开文件),这时该调用会转移到作为操作系统代码一部分的一个例程。使用系统调用时会将用户进程置为阻塞态。

综上所述我们知道中断不是进程主动发生的,而陷阱和系统调用是进程主动产生的。在这里我在网上查阅了一些资料,这里的陷阱又像是有一点异常的意思在里面,而系统调用又使用了陷阱,总之概念似乎有点混淆,不过对于中断倒是没有异议。读者可以结合下面这篇博客来理解陷阱和系统调用。
https://www.cnblogs.com/broglie/p/5463359.html

4.3.2 模式切换过程

当进程切换的时候,需要进行执行模式的切换。执行模式的切换离不开中断。当出现中断的时候,处理器会做如下工作:

  1. 将程序计数器置为中断处理程序的开始地址。
  2. 从用户模式切换到内核模式,以便中断处理代码包含特权指令。

处理器现在继续取指阶段,并取中断处理程序的第一条指令来服务该中断。此时,将已中断进程的上下文保存到已中断程序的进程控制块中。

现在的问题是,保存的上下文包括哪些内容?答案是,它必须包含中断处理程序可能改变的所有信息,以及恢复被中断程序时所需要的所有信息。因此,必须保存称为处理器状态信息的进程控制块部分,包括程序计数器、其他处理器寄存器和栈信息。

进程控制块中的其他信息如何处理?若中断之后切换到另一个应用程序,则需要做一些工作。但在多数操作系统中,中断发生后并不一定进行进程切换。可能的情况是,执行中断处理程序后,继续执行正运行的进程。此时,所要做的工作是发生中断时保存处理器状态信息,并在控制权返回给该程序时恢复这些信息。保存和恢复功能通常由硬件实现。那么需要进程切换时,就需要做以下的工作了。

4.3.3 进程状态需要变化时,操作系统所作的工作

进程状态的变化显然,模式切换与进程切换是不同的。模式切换可在不改变运行态进程的状态的情况下出现。此时保存上下文并在以后恢复上下文仅需很少的开销。但是,若当前正运行进程将转换为另一状态(就绪、阻塞等),则操作系统必须使环境产生实质性的变化。完整的进程切换步骤如下:

  1. 保存处理器的上下文,包括程序计数器和其他寄存器。
  2. 更新当前处于运行态进程的进程控制块,包括把进程的状态改变为另一状态(就绪态、阻塞态、就绪/挂起态或退出态)。还须更新其他相关的字段,包括退出运行态的原因和记账信息。
  3. 把该进程的进程控制块移到相应的队列(就绪、在事件i处阻塞、就绪/挂起)。
  4. 选择另一个进程执行。
  5. 更新所选进程的进程控制块,包括把进程的状态改为运行态。
  6. 更新内存管理数据结构。是否需要更新取决于管理地址转换的方式。
  7. 载入程序计数器和其他寄存器先前的值,将处理器的上下文恢复为所选进程上次退出运行态时的上下文。

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

5.操作系统的实现

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

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

如果操作系统仅是像其他程序那样由处理器执行的一组程序,那么操作系统是一个进程吗?如果是,如何控制它?这些有趣的问题使得人们提出了大量的设计方法。图3.15中给出了当代操作系统中使用的各种方法。接下来,我们一一讲解。
操作系统实现方法

5.1 无进程内核

在许多老操作系统中,传统且通用的一种方法是在所有进程外部执行操作系统内核【见图3.15(a)】。采用这种方法时,若当前正运行进程被中断或产生一个系统调用,则会保存该进程的模式上下文,并将控制权转交给内核。操作系统本身具有控制过程调用和返回的内存区域与系统栈。操作系统可执行任何预期的功能,并恢复被中断进程的上下文,恢复中断用户进程的执行;操作系统也可保存进程的模式上下文,并继续调度和分派另一个进程,但是否这样做取决于中断的原因和当前的情况。

无论哪种情况,关键都是进程这一概念仅适用于用户程序,而操作系统代码则是在特权模式下单独运行的实体。

5.2 在用户进程内运行

较小计算机(PC、工作站)的操作系统通常采用另一种方法,即在用户进程的上下文中执行所有操作系统软件。此时,操作系统是用户调用的一组例程,它在用户进程的环境内执行并实现各种功能,如图3.15(b)所示。任何时刻操作系统都管理着n个进程映像,这些进程映像就变成了下图所示的样子:
用户进程运行虚拟内存进程映像

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

操作系统完成操作后,需要继续运行当前的进程,则会切换模式以在当前进程内恢复已中断的程序。这种方法的关键优点是:中断一个用户程序,使用某些操作系统例程,然后恢复用户程序,所有这些都不会招致两次进程切换的惩罚。

然而,若确认将出现进程切换而非返回到先前正执行的程序,则控制权会传递给一个进程切换例程,进程切换例程是否在当前进程中执行,则取决于系统的设计。然而,在某些特殊情况下,当前进程必须置于非运行态,而另一个进程则指定为正运行进程。此时,将执行视为发生在所有进程外部逻辑上最为方便。

5.3 基于进程的操作系统

图3.15(c)所示的另一种方法是把操作系统作为一组系统进程来实现。类似于其他方法,该软件是在内核模式下运行的内核的一部分。但在这种情况下,主要的内核功能被组织为独立的进程。同样,此时存在一些在任何进程之外执行的进行切换代码。

这种方法有几个优点。首先,它利用了鼓励使用模块化操作系统的程序设计原理,可使模块间的接口最小且最简单。其次,有些非关键操作系统功能可简单地用独立的进程来实现,例如前面提及的监视各种资源(处理器、内存、通道)利用率和系统中用户进程进展状态的程序。因为这种程序不向任何活动进程提供特殊的服务,因此只能被操作系统调用。作为一个进程,这一功能可以任何指定的优先级在分派器的控制下与其他进程交替执行。第三,把操作系统作为一组进程来实现时,在多处理器或多机环境中很有用,因此此时为提高性能,有些操作系统服务可传送到专用的处理上执行。

6 UNIX SVR4 进程管理

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

6.1 进程状态

UNIX操作系统中共有9种进程状态,如表3.9所示。图3.17(基于【BACH86】中的图形)是相应的状态转换图,它与之前的五状态+两挂起进程模型类似,两个UNIX休眠态对应于其中的两个阻塞态。两个进程模型的主要不同之处如下:

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

和之前说到的五状态+两挂起进程模型对应总结如下:

  • 新建态:被创建
  • 阻塞/挂起态:休眠,被交换
  • 阻塞态:休眠,并驻留内存
  • 就绪/挂起态:就绪,被交换
  • 就绪态:1.就绪,并驻留内存; 2.被抢占
  • 运行态:1.内核运行; 2.用户运行
  • 退出态:僵死

6.2 进程描述

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

6.3 新进程创建

UNIX中的进程创建是由内核系统调用fork()实现的。一个进程发出一个fork请求时,操作系统执行如下功能:

  1. 在进程表中为新进程分配一个空项。
  2. 为子进程分配一个唯一进程标识符。
  3. 复制父进程的进程映像,但共享内存除外。
  4. 增加父进程所拥有文件的计数器,反映另一个进程(子进程)现在也拥有这些文件的事实。
  5. 将子进程置为就绪态。
  6. 将子进程的ID号返回给父进程,将0值返回给子进程。

所以子进程和父进程除了进程标识符和共享内存外,完全一致

所有这些工作都在父进程的内核模式下完成。内核完成这些功能后,可继续分派器例程工作一部分的如下三种操作之一:

  1. 停留在父进程中。控制权返回到用户模式下父进程调用fork的位置。
  2. 处理器控制权交给子进程。子进程开始执行代码,执行点与父进程相同,即在fork调用的返回处。
  3. 控制权转交给另一个进程。父进程和子进程都置于就绪态。

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

两个特殊进程

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

7 . 总结

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

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

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值