进程描述和控制(os 笔记二)

简介: 进程描述和控制​ 计算机最初的主要任务之一就是高效的自动化我们的工作,完成用户交付的任务。而这种任务在计算机中的表示就是一个个的进程。从上一篇文章中描述的计算机的发展历史我们能发现,无论是单道批处理系统还是多道批处理系统,操作系统的目的都是围绕对进程的控制和调度,从而实现执行用户任务。

进程描述和控制


​ 计算机最初的主要任务之一就是高效的自动化我们的工作,完成用户交付的任务。而这种任务在计算机中的表示就是一个个的进程。从上一篇文章中描述的计算机的发展历史我们能发现,无论是单道批处理系统还是多道批处理系统,操作系统的目的都是围绕对进程的控制和调度,从而实现执行用户任务。因此,系统必须满足的大多数操作都涉及进程。而为了 控制进程,操作系统必须为每一个进程 维护一个数据结构,这个数据结构描述进程的状态和资源的所有权。

什么是进程

1、定义

  • 正在执行的程序
  • 正在计算机上执行的程序程序实例
  • 能分配给处理器并由处理器执行的实体
  • 一组指令序列的执行、一个当前状态和相关的系统资源集

进程的两个基本元素是程序代码和相关联的数据集。

2、进程控制块

进程控制块 是由操作系统创建和管理的具有一个进程充分信息的数据结构。通过使用进程控制块,操作系统可以透明的中断和恢复一个进程。它是操作系统能够支持多进程和提供多处理的关键工具。

进程控制块通常包括以下内容:

  • 标识符
  • 状态
  • 优先级
  • 程序计数器:程序中即将被执行的下一条指令的地址
  • 内存指针:包括程序代码和进程相关数据的指针,还有和其他进程共享内存块的指针
  • 上下文数据:进程执行时处理器的寄存器中的数据(用于恢复执行)
  • I/O 状态信息
  • 记账信息

现在,可以认为一个进程是由代码段,数据段和进程控制块组成。

进程状态

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

1、两状态进程模型

​ 通过观察可知,一个进程可以处于两种状态,运行和未运行,所以可以构造最简单的进程状态模型,只有两个状态,运行态和未运行态。由于多道程序设计的原因,一个程序在未结束前将会在这两个状态之间进行切换。之后随着更多因素的加入,模型将会变得更为具体。
img_59971c1fba5b6db2b2a9dfb5a553fe76.png

两状态进程模型将维护一个 FIFO 队列,其中保存未在运行的进程的数据结构。分派程序(监控程序的一部分)每次从中选择一个进程执行。

img_2e591cb0267a973ca40487ea57b06f5f.png

2、进程的创建和终止

进程的创建

当一个进程添加到那些正在被管理的进程集合中去时,操作系统需要建立用于管理该进程的数据结构(之后介绍),并在内存中给他分配地址空间。

导致进程被创建的条件:

  • 新建的批处理作业
  • 交互登录
  • 操作系统因为提供一项服务而创建
  • 由现有的进程派生

导致进程被结束的原因:

  • 正常完成

  • 超过时限

  • 运行错误

  • 无可用内存
  • 越界
  • 算数溢出
  • 指令错误
  • 父进程终止

  • 操作员或系统干涉

  • 父进程请求子进程结束

3、五状态模型

​ 两状态模型中的待执行队列采用先进先出的方式(不考虑优先级)从未运行的进程中选择下一个被执行的进程。但是这种模型是不合适的。当队列中有正在等待事件的被阻塞的进程时,这个进程可能会因为不能执行而再次被放回队尾,等到他解除阻塞时,他可能排在了新加入进程的后面,所以这种策略不会每次选择在队列中存在时间最久的进程。为解决这种情况的一种比较自然的方法是将未运行状态分成两个状态:就绪态 和 阻塞态。另外还可以增加两个有用的状态,新建态和退出态。

状态图

img_d8710469b712767e3c2d272c54a5e30c.png

五种状态

  • 运行态
  • 就绪态:进程做好了准备,有机会就开始执行
  • 阻塞/等待态:进程在某些事件发生前不能执行,比如 I/O 操作完成
  • 新建态:刚刚创建的进程,操作系统还没有把他加入到可执行进程组中。通常是进程控制块已经创建但是还没有加载到内存中的新进程
  • 退出态:操作系统从可执行进程组中释放出的进程,或自身停止,或者是因为某种原因被取消

基于新的模型的新的进程队列:

img_6ba058466cf2a6943c02b46c0e557891.png

采用这种模型,分派器每次不在是采用先进先出的方式从就绪队列中选取进程,而是搜索整个就绪队列,查找在队列中未被阻塞且等待时间最久的进程来执行。

多级队列

​ 对于之前简单的例子,上图给出了各个状态的转化关系。基于这种模型,系统会维护两个队列。一个为就绪进程队列,另一个为阻塞进程队列。当操作系统打算选择一个进程运行时,便从就绪进程队列中选取一个执行。相应的,阻塞队列放置正在等待事件的被阻塞的进程。当一个事件发生时,所有等待该事件的处于阻塞中的进程都被转换到就绪队列中。这种方案意味着,如果一个事件发生,那么操作系统必须搜索整个阻塞队列来寻找等待这个事件的进程。在大型操作系统中,队列中可能有几百个甚至几千个进程,因此拥有多个队列将会很有效,一个事件可以对用一个队列。

被挂起的进程

交换的需要

现在在之前模型的基础上考虑再加入一种新的状态——挂起态。

img_29dd43587267256806860def5c7a5107.png

事实证明加入这一状态是很有必要的。暂时假设操作系统未采用虚拟内存技术,由于 CPU 速度比 I/O 快很多,所以很常见的一种情况是所有的进程都已经执行到了等待 I/O 的指令并处于阻塞状态,此时内存中全部是阻塞中的进程而没有可执行的进程,CPU 因此处于空闲状态(降低了计算机效率)。

一种解决方案是采用更大的内存以装载更多的进程从而尽量避免这种情况的发生,但是这种方案有两种缺陷。首先是内存的价格在达到兆和千兆位后价格会随之增加。另一方面是,程序对内存空间需求的增长速度要比内存价格下降的速度要块。因此,更大的内存往往导致更大的进程,而不是更多的进程。

另一种解决方案是采用__交换__,将内存中某一进程的一部分或者全部移动到磁盘中,并维护一个挂起队列,从而使内存空间有更多的空余以容纳新的进程加入。

新加入的状态

​ 当操作系统已经执行了一个挂起操作,此时内存中有了空余的空间,操作系统将有两个选择来利用这块空间。一是容纳一个新建的进程,二是调入一个以前被挂起的程序。显然,通常比较倾向于调入一个以前被挂起的进程,给他提供服务,而不是增加系统中的负载总数。

但是这种方案也带来了一个问题,所有的被挂起的进程在挂起时都处于阻塞态。很明显,这时如果将一个被阻塞的进程放回内存没有任何意义。所以我们需要更新之前的模型,新加入两个状态:

  • 阻塞/挂起态:进程在外存中等待一个事件
  • 阻塞/就绪态:进程在外存中,只要被载入内存就可以执行

新的状态转换图:

img_77a6f35c4b3b678f304b543eae6a1beb.png

各个状态之间的转换原则暂且不表(: P)。

导致进程挂起的原因
  • 交换:操作系统需要释放足够多的内存空间,以调入并执行处于就绪态的进程

  • 其他 OS 原因:操作系统可能挂起后台进程或工具进程,或者怀疑导致问题的进程

  • 交互式用户请求:用户可能希望挂起一个程序的执行,目的是为了调试,检查并修改程序数据,或者与一个资源的使用进行连接。

  • 定时:一个进程可能周期性的执行,而且可能在等待下一个时间间隔时被挂起

  • 父进程请求:父进程可能会希望挂起后代进程的执行,以检查或修改挂起的进程,或者协调不同后代进程之间的行为

进程描述

操作系统的控制结构

​ 操作系统为了管理进程和资源,必须掌握关于每个进程和资源的当前状态的信息。普遍使用的方法是:操作系统构造并维护他所管理的每个实体的信息表。比如内存表,I/O 表,文件表和进程表。

进程控制结构

进程的位置

​ 操作系统在管理和控制进程时,首先必须知道进程的位置,再者,它必须知道在管理时所必须的进程属性(比如进程ID,进程状态)。

一个进程的物理表示体现为在内存中的一块数据,保存着进程的程序(代码)和数据。此外,程序的执行还涉及到用于跟踪过程调用和过程间参数传递的栈。最后,与每个进程相关联的还有操作系统用于控制进程的许多属性。通常,这些属性的集合称为进程控制块。

程序、数据、栈和属性的集合称作进程映像。

进程映像中的典型元素:

  • 用户数据:用户空间中可修改部分,可包括程序数据、用户栈区域和可修改的程序
  • 用户程序:将被执行的程序
  • 系统栈:每个进程有一个或多个后进先出(LIFO)系统栈。栈用于保存参数、过程调用地址和系统调用地址
  • 进程控制块:操作系统控制进程所需要的数据

进程映像的位置依赖于使用的内存管理方案。最简单的情况就是它位于内存中的一块连续的空间。考虑其他更为复杂的内存管理方案时进程映像的位置和物理组织将随之发生变化(比如虚拟内存只载入进程的一部分到内存,分页使得进程在内存中分布不一定连续等)。

进程属性

​ 复杂的多道程序设计系统需要关于每个进程 的大量信息,如前所述,这些信息可以保存在进程控制块中。

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

  • 进程标识信息
  • 处理器状态信息
  • 进程控制信息

关于处理器状态信息

​ 处理器状态信息包括处理器寄存器的内容。当一个进程正在运行时,其信息储存在处理器中,当进程被中断时,所有的寄存器信息都必须保存起来,使得进程恢复执行时这些信息都可以被恢复。

具体包括:

  • 标识符
  • 用户可见寄存器
  • 控制和状态寄存器
  • 栈指针
  • 调度和状态信息
  • 数据结构
  • 等等

关于标识符

​ 实际上在所有的操作系统中,对于进程标识符,每个进程都被分配了一个唯一的__数字标识符__。进程标识符可以简单标识为主进程表中的一个索引(直接映射)。否则,必须有一个映射,使得操作系统可以根据进程标识符定位相应的表(间接映射)。这个标识符在其他地方也很有用,操作系统控制的许多其他表可以使用进程标识符__交叉引用进程表__。

进程控制块的作用

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

这其实也带来了一个重要的设计问题,我们访问进程控制块很容易,但是保护他们不被错误的修改确是个难题。具体表现为下面两个方面。

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

进程控制

执行模式

​ 在继续讨论操作系统管理进程的方式之前,需要区分通常与操作系统相关联的以及与用户程序相关联的__处理器执行模式__。通常处理器提供至少两种执行模式——特权态,非特权态。

特权态通常被称为内核态,而非特权态通常被称为用户态。

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

那么,处理器是如何知道他正在处于什么模式下执行以及如何改变这一模式的?

答案是 程序状态字。程序状态字(处理器中的一个寄存器)中有一位用于表示执行模式。当程序以用户态运行时,程序状态字被设置从而使处理器为用户态,相应的内核态也是如此。

进程创建

​ 之前提过了创建一个进程的事件和与进程相关的数据结构,现在将简单的描述实际创建进程时包含的步骤。

步骤如下:

  • 分配进程标识符 此时,在主进程表中增加一个新表项,表中的每个新表项对应一个进程。
  • 给进程分配空间
  • 初始化程序控制块
  • 设置正确的连接
  • 创建或扩充其他数据结构
进程切换

​ 从表面上看,进程切换的功能是很简单的。在某一时刻,有个正在运行的进程被中断,操作系统指定另一个进程为运行态,并把控制权交给这个进程。但是这会引发若干问题。首先,什么事件触发进程的切换?其次,模式切换和进程切换之间有什么的区别?最后,为实现进程的切换,操作系统必须对他控制的各种数据结构做些什么?

何时切换进程

​ 进程切换可以在操作系统从当前正在运行的进程中获得控制权的任何时刻发生。

首先考虑中断:

  • 时钟中断 时间片用尽
  • I/O 中断 进程状态的切换(考虑五种进程状态模型)导致的进程切换
  • 内存失效 内存失效的进程可能会被设置为阻塞态,操作系统有可能切换另一个程序执行

实际上,大多数操作系统区分两种类型的系统中断。一种称为中断,另一种称为__陷阱__。前者与当前正在运行的进程无关的某种类型的外部事件相关。比如 I/O 信号。后者与当前正在运行的进程所产生的错误或异常条件相关。

对于陷阱而言,操作系统将根据错误的严重性决定将进程转换为退出态,切换其他的进程执行,还是报告一条警告消息继续执行。

模式切换

中断阶段是指令周期的一部分(即循环的出现)。在中断阶段,处理器检查是否发生了中断,这通过中断信号来表示。如果没有,处理器继续取指令周期,即当前进程中的下一条指令。如果存在一条未处理的中断,处理器需要做以下工作:

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

当中断发生后,被中断的进程上下文保存在被中断的进程控制块中。环境上下文必须包括称作处理器状态信息的进程控制块部分,这包括程序计数器(执行到了哪条指令)、其他处理器寄存器和栈信息。以实现进程的恢复。

操作系统的执行

基于之前讨论过的事实:

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

我们将讨论操作系统在计算机中执行的方式。

无进程的内核

这种模式下,操作系统是作为所有的进程之外被执行的。操作系统有自己的内存区和系统栈,用于控制过程调用和返回,处理中断,进程调度。操作系统是作为一个在特权模式下工作的实体被执行。这种模式一般存在于许多老的操作系统中。

在用户进程中执行

在较小的机器的操作系统中,常见的方法是在用户进程的上下文中执行几乎所有的操作系统软件。其观点是操作系统从根本上说是用户调用的一组例程。在这种模式下,每个进程都包含操作系统,每个进程映像包括内核程序的程序、数据和栈区域(以共享内存的形式)。

基于进程的操作系统

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

目录
相关文章
|
6月前
|
缓存 负载均衡 Linux
内核:进程与调度机制(笔记)
内核:进程与调度机制(笔记)
130 0
|
存储 缓存 Shell
【深入理解操作系统】第一章:计算机系统漫游 | A tour of Computer Systems | 阅读笔记
【深入理解操作系统】第一章:计算机系统漫游 | A tour of Computer Systems | 阅读笔记
115 0
|
3月前
|
Linux
Linux源码阅读笔记10-进程NICE案例分析2
Linux源码阅读笔记10-进程NICE案例分析2
|
3月前
|
Linux
Linux源码阅读笔记09-进程NICE案例分析1
Linux源码阅读笔记09-进程NICE案例分析1
|
5月前
|
算法 Linux 编译器
技术笔记:LINUX2.6.32下的进程分析
技术笔记:LINUX2.6.32下的进程分析
30 0
|
3月前
|
Linux
Linux源码阅读笔记13-进程通信组件中
Linux源码阅读笔记13-进程通信组件中
|
3月前
|
消息中间件 安全 Java
Linux源码阅读笔记13-进程通信组件上
Linux源码阅读笔记13-进程通信组件上
|
3月前
|
存储 Linux API
Linux源码阅读笔记08-进程调度API系统调用案例分析
Linux源码阅读笔记08-进程调度API系统调用案例分析
|
3月前
|
Linux API
Linux源码阅读笔记07-进程管理4大常用API函数
Linux源码阅读笔记07-进程管理4大常用API函数
|
3月前
|
Linux 调度
Linux源码阅读笔记05-进程优先级与调度策略-实战分析
Linux源码阅读笔记05-进程优先级与调度策略-实战分析