操作系统学习笔记---进程

操作系统学习笔记---进程

背景

很早之前计算机大多都属于单道程序系统,内存中除了操作系统的程序外,只有一个用户程序在里面,并且系统中的其他资源(例如CPU、IO设备等)也都由这个程序单独使用,不与其他用户程序共享上述资源,因此单道程序就严格按照顺序方式执行。顺序程序活动的特点:顺序性(程序所规定的每个动作都在上个动作结束后才开始)、封闭性(程序本身的动作才能改变程序的运行环境)、可再现性(程序的执行结果与程序运行速度无关)。由于单道程序系统具有资源浪费、效率低下等明显缺点,如今的计算机系统广泛采用多道程序设计技术。多道程序设计是在内存中同时存放多道程序。由于CPU一次只能执行一个程序,因此它们在管理程序的控制下交替地在CPU上运行,从宏观上看,系统中的多个程序都“同时”得到执行,实现了程序的并发执行。多道程序设计具有提高系统资源利用率和增加作业吞吐量的优点,但是由于程序的并发执行和系统资源的共享使得操作系统的工作变得复杂。

正文

一、进程的概念

在多道程序设计中,不同的程序会因为执行的时间、顺序的不同出现“走走停停”的新状态,又由于“程序”本身是“静止”的,不能通过程序本身看出来这种“走走停停”的状态,因此引入进程这个概念,来描述多道程序设计中的程序执行过程的性质。通常可以这样去描述进程:一个具有独立功能的程序关于某个数据集合的一次运行活动。

进程的基本特性

  • 动态性:进程是程序的执行过程,有生,有亡,有活动,有停顿,可以处在不同的状态
  • 并发性:多个进程实体能共存同一个内存中,在一段时间内都得到运行,使得一个进程的程序与其他进程的程序得以并发运行
  • 调度性:进程是系统中申请资源的单位,也是被调度的单位。操作系统中有很多调度程序,它们根据各自的策略调度合适的进程,并为其运行提供条件。
  • 异步性:各进程向前推进的速度是不可预知的,即异步方式运行,这会导致程序间的相互制约,使得程序执行失去再现性。为保证个程序的协调运行,需要采取必要的措施。
  • 结构性:进程具有一定的结构,它由程序段,数据段和控制结构组成。

二、进程的状态和组成

1、进程的状态

进程大致可以分为一下几个状态:

  • 新建状态:指进程刚被创建,尚未放入就绪队列时的状态。处于这种状态的进程是不完全的。
  • 就绪状态:指进程已经具备运行条件,但因为其他进程正占用CPU,使得它暂时不能运行而处于等待分配CPU的状态
  • 运行状态:指当前进程已经分配到CPU,它的程序正在处理机上执行时的状态。
  • 阻塞状态:指进程因等待某种事件发生而暂时不能运行的状态
  • 终止状态:指进程完成自己的任务而正常终止或在运行期由于出现某些错误和故障而*终止的状态

在UNIX系统中进程可以分为一下几个状态:

  • 用户运行态:在CPU上运行用户程序
  • 核心运行态:在CPU上运行核心程序
  • 在内存中就绪:具备运行条件,只等核心调度它就可取得CPU控制权
  • 在内存中睡眠:尚不具备运行条件,在内存中等待某个事件的发生
  • 在外存中就绪:就绪进程被对换到外存上
  • 在外存中睡眠:睡眠进程被对换到外存上
  • 被剥夺:进程在返回用户态之前,被调度程序强行剥夺处理机后的进程状态。它实际上与“在内存中就绪”状态是一样的。目的是强调该进程从核心态返回到用户态时才会被剥夺
  • 创建态:新进程被创建,但尚未完毕的中间状态
  • 终止态:进程终止自己

操作系统学习笔记---进程

从上述几种状态可以看出,一个进程可在两种不同方式下运行:用户态与核心态。如果当前运行是用户程序,那么对应进程就在用户态下运行;如果发生系统调用或发生中断,就运行操作系统程序(核心程序),进程状态就变成核心态。对于“在外存中就绪”与“在外存中睡眠”是使处于基本状态的进程(就绪、运行、阻塞/睡眠)处于静止(非终止)状态。此时系统回收被这些进程占用的内存资源,将其实体复制到外村的进程交换区,这种操作也叫做挂起,挂起之后可以解挂(换入)重新分配内存。

2、进程的描述

进程的活动是通过在CPU上执行一系列程序和对相应数据进行操作来体现的,因此,程序和数据是组成进程的实体。但这两者仅是静态的文本,没有反映其动态特性。为此,还需要有一个数据结构描述进程当前的状态,本身的特性、对资源的占用及调度信息等。这种数据结构称为进程控制块(PCB)。此外,程序的执行过程必须包含一个或多个栈,用来保存过程调用和相互传递参数的踪迹。

2.1 进程控制块的组成

PCB含有进程的描述信息和控制信息,是进程动态特性的集中反映,是系统对进程施行识别和控制的依据。进程控制块一般含有如下内容:

  • 进程名
  • 特征信息:包括是系统进程还是用户进程,进程实体是否常驻内存等信息
  • 进程状态信息:表明进程的执行状态,是运行状态还是就绪状态或者是阻塞状态
  • 调度优先级:表示进程获取CPU的优先级别
  • 通信信息:反映该进程与哪些进程有什么样的通信关系,如等待哪个进程的信号
  • 现场保护区:但对应进程由于某种原因放弃使用CPU时,需要把它的一部分与运行环境有关的信息保存起来,以便在重新获得CPU资源后恢复正常运行。通常被保护的信息有程序计数器,程序状态字,各工作寄存器的内容等
  • 资源需求、分配和控制方面的信息:如进程所需要或占用的IO设备、磁盘空间、数据区等。
  • 进程实体信息:指出该进程的程序和数据的存储情况,在内存或外存的地址、大小等
  • 族系关系:反映父子进程的隶属关系
  • 其他信息:如文件信息、工作单元

2.2 进程控制块的作用

  • 每个进程有唯一的进程控制块,PCB是进程存在的唯一标识
  • 操作系统根据PCB对进程实施控制和管理。例如,当进程调度程序执行进程调度时,它从就绪进程的PCB中找出其调度优先级;按照某种策略从中选择一个进程,在根据该进程的PCB中保留的现场保护区信息,恢复该进程的运行现场;进程与其他进程的同步与通信,要使用PCB中的通信信息;

UNIX系统中,PCB的功能由proc和user两个结构实现。proc结构常驻在内存的系统区(即核心空间),这是进程映像中最常用的部分,它记录了进程的状态、优先级等直接与进程调度有关的内容。不管对应进程是否正在运行,核心都会访问这些数据,但是它仅占PCB的一小部分。user结构是proc结构的扩充部分,其中包括用户标识号,运行用时,信号处理方式,当前目录,用户打开文件表等。当进程不处于运行态时,核心不会对这部分信息进行查询和处理。因而非运行态进程的user结构可能被换到外存上,以后该进程被调度运行之前换入内存即可(在新版本中它也不被换出去) 

2.3 进程队列

一个系统中有很多个进程,处于就绪状态和处于阻塞状态的进程可以分别有很多个,而阻塞的原因又各不相同。为了对所有进程进行有效的管理,常将各个进程的PCB用适当的方式组织起来。一般来说,有线性方式、链接方式和索引方式。

2.3.1 线性方式

操作系统学习笔记---进程

如图所示,操作系统预先确定整个系统中同时存在的进程的最大数目,比如是n,静态分配空间时,把所有进程的PCB都放在这个队列表中。这种方式存在的问题是:限定了系统中同时存在的进程的最大数目。当很多用户同时上机时,会造成无法为用户创建新进程的情况,更加严重的情况是:在执行CPU调度时,为选择合理的进程投入运行,经常要对整个表进行扫描,降低了调度效率。

2.3.2 链接方式

操作系统学习笔记---进程

链接方式是常用的方式,其原理是:按照各进程不同状态分别将它们的PCB放在不同的队列中,在但CPU情况下,处于运行状态的进程只有一个,可用一个指针指向它的PCB。处于就绪状态的进程可以是若干个,它们排成一个(或多个)队列,通过PCB结构内部的拉链指针把同一队列的PCB链接起来。该队列的第一个PCB由就绪队列指针指向,最后一个PCB的拉链指针置为0,表示结尾。CPU调度程序把第一个PCB由该队列中摘下(设想仅有一个队列),令其投入运行。新加入就绪队列的PCB链在队列尾部(按照先进先出的策略)。阻塞队列可以有多个,对应不同的阻塞原因。当某个等待条件得到满足时,可把对应阻塞队列上的PCB送到就绪队列中,正在运行的进程如果缺少某些资源而无法继续运行时,就变为阻塞状态。加入到相应的阻塞队列中。其实就绪队列往往按进程优先级高低分成多个队列,具有同一优先级的进程其PCB排在一个队列上,现代的UNIX系统就采取这种方式。

2.3.3 索引方式

操作系统学习笔记---进程

索引方式利用索引表记载不同状态进程的PCB地址。就是说,系统建立几张索引表,各对应进程的不同状态,如就绪索引表,阻塞索引表等,状态相同的进程的PCB组织在同一索引表中,每个索引表的表目中存放一个PCB地址。各索引表在内存的起始地址放在专用的指针单元中。

三、进程管理

3.1 进程图

进程存在族系关系,即父进程创建子进程,子进程再创建子进程。通过执行相应的系统调用对进程实时管理,如创建,执行,终止,等待等。进程图是描述族系关系的有向树,族系图中每个结点的子节点所代表的进程,都是由该结点代表的进程创建的。

例如:在开机时,首先引导(Boot)操作系统,由引导程序将操作系统从硬盘装入到内存;之后生成第一个进程(在UNIX系统中称为0号进程),由它创建1号进程及其他核心进程,1号进程又为每一个终端创建命令解释进程(Shell进程);用户输入命令后又创建若干进程。这样便形成了一颗进程树。树的根节点是所有进程的祖先。上一个节点对应的进程是下一个节点对应进程的父进程。

3.2 进程创建

创建新进程时要执行创建进程的系统调用(如UNIX系统中的fork),其主要操作过程如下

  1. 申请一个空闲的PCB:在系统的PCB表中找到一个空闲的PCB项,并指定唯一的进程标识号PID
  2. 为新进程分配资源:根据调用者提供的所需内存大小,为新进程分配必要的内存空间,存放其程序和数据及工作区
  3. 将新进程的PCB初始化:初始化包括新的进程名,父进程标志,处理机初始化状态,进程优先级,本地开始地址等。
  4. 将新进程加入到就绪队列中

一个进程派生新进程后,有两种可能的执行方式

  • 父进程和子进程同时(并发)执行
  • 父进程等待它的某个或全部子进程终止

建立子进程的地址空间也有两种可能,首先解释一下进程的地址空间:内核除了管理本身的内存外,还必须管理用户空间中进程的内存。我们称这个内存为进程地址空间,也就是系统中每个用户空间进程所看到的内存

  • 子进程复制父进程的地址空间
  • 把程序装入子进程的地址空间

不同操作系统采用不同的实现方式来创建进程。例如UNIX中,每个进程有自己唯一的进程标识号(PID)。父进程利用fork系统调用才创建新进程。父进程创建子进程时,把自己的地址空间制作一个副本,其中包括user结构,正文段,数据段,用户栈和系统栈。这种机制使得父进程很容易与子进程通信。两个进程都可以继续执行fork系统调用后的指令,但有一个差别:fork的返回值(即子进程的PID)不等与0时,表示父进程正在运行;而返回时等于0时,表示子进程正在执行、Linux也是采取这种方式。

子进程被创建后,一般使用execlp系统调用------用一个程序(如可执行文件)取代原来的内存空间的内容,然后开始执行。为此,两个进程就各行其道了。父进程可以创建多个子进程。当子进程运行时,如果这个父进程无事可做,就执行wait系统调用,把自己插入到睡眠队列中,等待子进程的终止。

3.3 进程的终止

导致进程终止的原因很多,分为如下三种情况。

  • 正常终止:当一个进程完成自己的任务后,使用exit系统调用,要求操作系统删除它。
  • 异常终止:在进程运行过程中,如果出现某些错误或故障,会导致进程终止
  • 外部干扰:外部干扰包括操作员或操作系统的干预(由于某种原因----如出现死锁,操作员或操作系统终止该进程),父进程终止(当父进程终止时,操作系统会自动地终止其所有子孙进程),父进程请求(父进程有权终止它的任何子孙进程)。

一旦系统中出现要求终止进程的事件后,便执行终止进程的系统调用(如UNIX/Linux系统的exit)。通常,终止进程的主要操作过程如下:

  • 从系统的PCB表中找到指定进程的PCB。若他正处于运行状态,则立即终止该进程的运行。
  • 回收该进程所占用的全部资源
  • 若该进程有子孙进程,则还要终止其所有子孙进程,回收它们所占用的全部资源
  • 将被终止进程的PCB从原来队列中摘走,以后由父进程从中获取数据,并释放它

在UNIX系统中,用户进程主要执行命令运行单位,这些命令的代码都以系统文件形式存放。当命令执行完,该进程希望终止自己时,就在其程序末尾使用系统调用exit(status),其中status被称为终止码,它是终止进程向父进程传送的参数。父进程使用系统调用wait等待其子进程的终止。wait系统调用返回被终止子进程的标识号(PID),所以父进程可以告诉系统是哪个子进程终止了。如果父进程终止了,那么它的所有子进程就被赋予一个新的父进程,即init进程。

3.4 进程阻塞

一个进程经常需要与其他进程通信。正在运行的进程因为提出的服务请求未被操作系统立即满足,或者所需数据尚未达到等原因,只能转变为阻塞状态,等待相应事件出现后再把它唤醒。正在运行的进程通过调用阻塞原语(UNIX/Linux系统的sleep),主动把自己阻塞,进程阻塞的过程如下:

  1. 立即停止当前进程的执行
  2. 将现行进程的CPU现场送到该进程的PCB现场保护区中保存起来,以便重新运行时恢复此时的现场
  3. 把该进程PCB中的现行状态由“运行”改为“阻塞”,把该进程查到具有相同事件的阻塞队列中
  4. 转到进程调度程序,重新从就绪队列中挑选一个何时进程投入运行

在UNIX系统中,核心态运行的进程在所需条件不具备时,会调用sleep(chan,disp)进入睡眠,其中参数chan是睡眠地址,它是指向存放该进程所等待事件对应的核心编码单元的指针,用来表示睡眠原因。另一个参数disp是由核心指定的优先数,其值越小,对应的调度优先级越高。

3.5 进程唤醒

当阻塞进程所等待的事件出现时(如所需的数据已达到,或者等待IO操作已完成),则由另外的、与阻塞进程相关的进程(如IO操作的进程)调用唤醒原语(UNIX/Linux系统的wakeup),将等待该事件的进程唤醒。可见,阻塞进程不能唤醒自己。

唤醒原语执行过程如下:

  1. 把阻塞进程从相应的阻塞队列中摘下
  2. 将现行状态改为就绪状态,然后把该进程插入到就绪队列中
  3. 如果被唤醒的进程比当前进程的优先级更高,则设置重新调度标志

在UNIX系统中,运行的进程完成某一事件(如IO完成)后,往往利用wakeup(chan)的形式去唤醒睡眠进程,其中参数chan表示睡眠地址(即睡眠原因)。wakeup程序唤醒在同一原因上睡眠的所有进程。而不是一次只唤醒一个进程。这样,执行进程调度时,条件最佳的被唤醒进程得到运行。如果进程运行发现缺少先前等待的事件,它便重新进入睡眠状态。