主目录
之前的有博文对进程和线程的区别进行了详细的介绍,并且说明了引入进程的目的是为了能够使程序并发的执行,并且可以对并发执行的程序加以描述和控制。这篇博文,我们就来对进程的状态转换来进行详细的分析。本文除了进程的状态转换,还穿插着对进程的控制的说明。
注意:本博文中进程均是在传统操作系统中的进程,既是OS进行资源分配的基本单位,也是OS进行调度的一个独立单位。
在开始之前,我们对进程和程序来进行一个对比:
- 程序是静态的,而进程是动态的。
- 程序可以作为一种软件资源长期存在,而进程是有一定生命周期的。程序是永久的,进程是暂时的。
- 进程更能真实地描述并发,而程序不能;
- 进程具有创建其他进程的功能,而程序没有;
- 同一程序可以对应多个进程;
- 进程是系统分配资源和调度的独立单位,而程序不是。
因此,从上面我们可以得到进程的一些特征:
- 动态性(进程由创建而产生,由调度而执行,由撤销而消亡);
- 并发性(多个进程同存于内存中,可以并发的执行);
- 独立性(资源分派和调度的独立单位);
- 异步性(进程的执行以一种不可预知的速度向前推进)。
三种基本状态
上面我们讲到了,进程是具有生命周期的,由创建而产生,由调度而执行,由撤销而消亡,并且内存中可以同时存在多个进程,多个进程在并发执行的过程中共享系统资源,因为资源的争用或者其他原因,导致进程在运行的过程中呈现间断性(异步),因此,进程在其生命周期内就具有多种状态。
首先我们讲的是三种基本状态:就绪态、执行态、阻塞态。这三种基本状态是每个OS中都会有的,因此也称之为基本状态,下面先给出三种基本状态的定义:
- 就绪(Ready)状态:通过Ready我们可以看到,处于此状态的进程已经处于准备好要运行了,此时进程已经分配好除CPU外的所有必要资源,只需获得CPU(CPU如何进行分配,请关注后续有关处理机调度的博文),便可立即执行。用一句话来描述处于就绪态的进程:万事俱备,只欠CPU。另外还有一个就绪队列的概念,处于就绪态的进程都在此队列中,等待着调度程序的调度(分配CPU)。
- 执行(Running)状态:Running,运行中的,处于此状态的进程是已经获得CPU并且正在执行中。对于这一状态,在单CPU OS中,同一时刻只能有一个进程处于此状态,而在多CPU OS中,则可以有多个(不超过CPU的数量)进程同时处于执行状态。
- 阻塞(Block)状态:阻塞,百度释义为:障碍而不能通过,无法畅通。处于此状态的进程是因为在执行的过程中由于发生某种需要等待的事件(I/O请求、申请缓存失败、等待接收数据等),而暂时无法继续执行。我们知道在多道批处理系统中,此时需要进程把处理机释放,并选取新的就绪进程执行。与就绪队列相对应的,处于阻塞状态的进程都在阻塞队列中,某些OS中,出于提高效率的目的,根据阻塞的原因,会有多个阻塞队列。
上图是进程的三种基本状态的转换图,进程在运行的过程中会经常的发生状态的转换。从图中我们可以看到,就绪态和执行态是可以互相转换的,但是执行态到阻塞态是单向的,这是因为就算阻塞的进程"通畅"了,但是因为处理机已经分配给了别的进程,因此进程的状态也只能切换到就绪态,并且也是单项的,因为等待事件也只可以在执行中才能发生,下面说明下各个状态切换发生的原因:
- 就绪态—>执行态:进程获得CPU(被调度程序选中);
- 执行态—>阻塞态:向OS请求共享资源(互斥、同步)失败、等待某种操作完成、新数据尚未到达(I/O操作)、等待新任务的到达;
- 阻塞态—>阻塞态:上述的四类等待事件发生;
- 执行态—>就绪态:分配给进程的时间片执行完成(轮转调度算法)、高优先级的进程到达(抢占式调度)。
创建状态和终止状态
我们知道,进程创建时,需要对其分配除CPU外的所有的必要资源,但是,如果此时OS因为资源不足无法给进程分配资源,那么进程应该处于什么状态呢?为了满足上述问题,满足进程状态的完整性以及增强管理的灵活性,通常会在OS中为进程新增两种常见的状态:创建状态和终止状态。下面给出两种状态的定义:
- 创建状态:进程创建的过程中,所需的资源尚不能得到满足,此时创建工作尚未完成,进程无法被调度执行,进程此时就处于创建状态。
- 终止状态:进程正常运行结束或者出现导致进程终止的错误,或是被OS所终结,或是被父进程终结,则进入终止状态。进入终止状态的进程将不能再执行,但是在OS中依然保持一个记录,其中保存着状态码和计时统计数据,等待别的进程收集,一旦资源被收集完成,OS会立刻删除该进程。
包含创建状态和终止状态之后,进程的状态转换如上图所示。从图中我们可以看到,状态的转换多了创建态到就绪态,执行态到终止态,下面给出两种状态切换的原因:
- 创建态—>就绪态:进程所需的资源得到分配;
- 执行态—>终止态:进程正常运行结束或者出现导致进程终止的错误,或是被OS所终结,或是被父进程(或其他有终止权的进程)终结。
下面对进程创建和终止的过程来进行一下说明。首先是进程的创建,进程的创建是一个较为复杂的过程,首先需要由进程申请一个空白的进程控制块(Process Control Block, PCB,后面会详细的讲),并向PCB中填写用于控制和管理进程的信息;然后为该进程分配运行时必要的资源;最后把该进程转为就绪态并插入到就绪队列中。其次是进程的终止,进程的终止分为两个步骤:首先是等待OS进行善后处理(停止执行、终止子孙进程、归还资源等),然后将其PCB清零,并将PCB空间返还给系统。
从上述进程的创建和终止的过程中,我们可以得知,创建态就是处于进程创建的第一步完成时的状态,终止态就是进程无法被调度或执行,但是还需等待别的进程来收集信息,也就是终止态同样处于第一步完成的状态,只有当进程归还了PCB后,才算是真正的被OS删除,生命周期才算结束。
挂起、激活操作
在部分OS中,为了系统和用户观察和分析进程的需要,还引入了一个对进程的重要操作—挂起(Suspend),当对某个进程执行此操作是时,该进程将会被挂起,此时意味着该进程需要释放内存,调至到硬盘中(外存),也意味着此时该进程处于静止状态,无法接受调度或者执行。当该进程被执行激活(Active)操作时,才可将该进程从外存中重新导入到内存中。
首先我们来分析下进程被挂起的原因:
- 终端用户的请求:当终端用户在自己的程序运行期间发现有可疑问题时,希望暂停自己的程序的运行,使之停止下来,以便用户研究其执行情况或对程序进行修改;
- 父进程的请求:有时父进程希望挂起自己的某个子进程,以便考察和修改子进程,或者协调各子进程间的活动;
- 负荷调节的需要:当实时系统中的工作负荷较重,已可能影响到对实时任务的控制时,可由系统把一些不重要的进程挂起,以保证系统能正常运行;
- 操作系统的需要:操作系统有时希望挂起某些进程,以便检查运行中的资源使用情况或进行记账。
引入挂起之后,系统中的就绪状态和阻塞状态分别变为两种状态,这四种状态为:活动就绪、静止就绪、活动阻塞、静止阻塞,下面分别对这四种状态进行说明:
- 活动就绪(Readya):等价于就绪态,此时进程可以接受调度,获得处理机后可直接转为执行态;
- 静止就绪(Readys):进程被调至外存,无法接受调度;
- 活动阻塞(Blockeda):等价于阻塞态,当等待的事件发生后可以由活动阻塞变为活动就绪状态;
- 静止阻塞(Blockeds):进程仍然可以等待事件,当事件发生后,状态由静止阻塞变为静止就绪状态。
引入挂起操作后,OS中的状态变为7种,其中部分进程状态的变化如下:
- 活动就绪—>静止就绪:该进程被执行挂起原语Suspend;
- 活动阻塞—>静止阻塞:该进程被执行挂起原语Suspend;
- 执行态—>静止就绪:该进程被执行挂起原语Suspend;
- 静止就绪—>活动就绪:该进程被执行挂起原语Active;
- 静止阻塞—>活动阻塞:该进程被执行激活原语Active;
- 静止阻塞—>静止就绪:进程等待的事件发生;
- 活动阻塞—>活动就绪:进程等待的事件发生;
- 创建态—>活动就绪:在当前OS的性能和内存的容量均允许的情况下,完成进程的创建工作(三步);
- 创建态—>静止就绪:当前OS的当前资源状况和性能不允许,不分配给新建进程所需的资源(创建工作只完成第一步),此时进程处于静止就绪状态,被调至到外存中,不参与调度,此时创建工作尚未完成。
从上面的状态我们可以看到,处于执行态的进程一样会被挂起,而且引入了挂起操作后,进程不会长期的处于创建状态,如果能分配到资源,状态会变为活动就绪态,如果无法分配资源,则进入静止就绪状态,当资源充足后,在将进程调入内存并为之分配资源,完成创建工作。
进程控制块PCB
上面讲了这么多的进程的状态的切换,那么OS到底是根据什么来进行进程的控制和管理呢?比如进程的状态信息保存在哪里、进程对应的程序和数据是如何保存的、进程是如何保存和恢复CPU现场信息(上下文切换)的,还有其他等等操作是如何实现的?其实这些操作都需要依托于一种数据结构—进程控制块PCB。PCB是OS为了系统的描述和管理进程的运行,在内核中为每个进程专门定义的一个数据结构。PCB作为进程实体(程序、数据、PCB)的一部分,记录了OS所需的用于描述进程当前情况以及管理进程运行的全部信息,是OS中最重要的记录型数据结构。
**PCB的作用是使一个在多道程序环境下不能独立运行的程序(含数据),成为一个能独立运行的基本单位,一个能与其它进程并发执行的进程。**因此PCB的作用非常重要,我们讲进程的状态切换是必须要讲PCB的。首先,我们对PCB中存储的信息进行介绍:
- 进程标识符:用于唯一地标识进程。一个进程通常包含两种标识符:外部标识符和内部标识符。外部标识符一般由创建者(用户)提供,用来方便记忆;内部标识是为了方便OS对进程的使用,通常是Linux系统中查看到的Pid;
- CPU状态:CPU状态信息也称处理机的上下文,主要由CPU的各种寄存器(通用寄存器、指令计数器、程序状态字PSW、用户栈指针)中的内容组成的。进程在执行的过程中,正在处理的许多信息都放在寄存器中,如果需要发生切换,这些信息就需要保存在该进程的PCB中,以便可以再次执行时可以快速的恢复CPU的状态;
- 进程调度信息:在OS进行调度(从就绪队列中选取进程分配CPU)时,必须要知道进程的状态和相关的调度信息,主要包括:进程状态、优先级、进程调度所需的其他信息(等待时间、已经执行的时间等)、事件(等待发生的时间,即阻塞的原因);
- 进程的控制信息:只用于进程控制所必需的信息,主要包括:程序和数据的地址(所在内存或外存的首地址)、进程同步和通信机制、资源清单(进程运行期间所需的全部资源,CPU除外,还有一张已经分配给该进程的资源清单,主要用于避免死锁)、链接指针(指向本PCB所在队列中的下一个进程的PCB的首地址)。
通过PCB所保存的信息,我们是不是也就知道了上面讲到的问题(进程如何进行控制、程序和数据如何保存、CPU上下文如何切换和恢复)的答案了,都是需要使用PCB中保存的信息。下面我们根据PCB中存储的信息,来对PCB作用做一个具体的分析:
- 作为独立运行基本单位的标志:当一个程序(包含数据)配置了PCB后,就表示他成为了一个进程,一个能在多道程序环境下独立运行并合法的一个基本单位,也就具有了取得OS服务的权利。这也是为什么当系统创建一个进程时,需要为之创建一个PCB,并与进程一对一的绑定,系统是根据PCB来感知进程的,PCB被撤销归还给OS时,进程也就随之消亡了;
- 能实现间断运行的方式:这也是进程的一个基本特性—异步性,因为PCB中保存着进程在CPU上执行时的上下文信息,因此在进程再次被调度执行时,可以快速的恢复其CPU的现场信息。
- 提供进程管理所需要的信息:在进程的整个生命周期中,OS都是根据PCB实施对进程的控制和管理。
- 提供进程调度所需要的信息:OS根据PCB中存储的进程的状态信息来将继承插入到对应的队列中,并根据PCB中保存的优先级、等待事件等来进行调度的;
- 实现与其他进程的同步与通信:PCB中具有用与实现进程通信的区域或通信队列指针等。
通过上面的描述,我们也看到了PCB的重要之处,虽然一个进程中的PCB保存了如此多的信息,但是一个PCB的大小大约是1KB左右,体量小也可以保证内核中可以存放一定数量的PCB。
又到了分隔符以下,本文到此就结束了,本文内容全部都是由博主自己进行整理并结合自身的理解进行总结,如果有什么错误,还请批评指正,当然,如果有什么疑惑可以评论留言。
全文又是4700多字,原创不易,在写这篇博客的时候,真的是投入了大量的心血,翻看了许多的文档,如果本文对你有所帮助,还请留下个赞,以表支持。