CoderMrWu

生活诚可期,爱情价更高!

进程的状态与转换以及进程控制块

并发程序和顺序程序的执行有本质上的差异,为了能更好地描述程序的并发执行,实现操作系统的并发性和共享性,引入“进程”的概念。以下部分介绍进程的概念、进程状态及状态转换、描述进程属性的数据结构——进程控制块,以及对进程可实施的主要操作

一、进程的定义

进程是具有一定独立功能的程序在某个数据集合上的一次运行活动,是系统进行资源分配和调度的一个独立单位。 从操作系统角度来看,可将进程分为系统进程用户进程两类。系统进程执行操作系统程序,完成操作系统的某些功能。用户进程运行用户程序,直接为用户服务。系统进程的优先级通常高于一般用户进程的优先级。

1.进程与程序的联系和区别

进程和程序既有联系又有区别

(1)进程和程序的联系

程序是构成进程的组成部分之一,一个进程的运行目标是执行它所对应的程序,如果没有程序,进程就失去了其存在的意义。从静态的角度看,进程是由程序、数据和进程控制块(PCB)三部分组成的

(2)进程和程序的区别

程序是静态的,而进程是动态的。

进程是程序的一个执行过程。程序的存在是永久的(这里不讨论人为删除程序等行为)。而进程是为了程序的一次执行而暂时存在的。进程有生命周期,有诞生,亦有消亡。

一个进程可以包括若干程序的执行,而一个程序亦可以产生多个进程。比如,一个编译进程在运行时就要执行词法分析、语法分析、代码生成和优化等若干个程序。而一个编译程序有时也需要同时生成几个编译进程,为几个用户服务。

进程具有创建其他进程的功能。被创建的进程称为子进程,而创建者称为父进程,从而构成了进程家族

2.可再入程序

一个程序不是任何条件下都可以产生多个进程的。一个能被多个用户同时调用的程序,在执行中自身不能改变。

如果一个程序在执行中会发生变化,那么,这个程序的功能就是可能改变的。显然该程序就不能以同样的方式为每个用户服务。甚至同一个用户以同样的要求在不同时刻调用它时,也可能得到不同的结果,这样的程序就不能同时为多个用户服务。

一个能够被多个用户同时调用的程序称作是“可再入”的程序。可再入程序必须是“纯”代码的程序,程序在执行中不会修改自身的代码。换句话说,可再入程序必须和有关的数据区分离。可再入程序的操作对象,即数据,是与程序相分离的,要求调用者提供专用工作区,以保证程序以同样方式为各用户服务。现代的操作系统及编译程序都是属于可再入 程序,它们能同时被不同用户调用而形成不同的进程。

3.进程的特征

使用进程的概念能很好地描述程序的并发执行,并且能够揭示操作系统的内部特性。事实上,操作系统的并发性和共享性正是通过进程的活动体现出来的。

进程具有两个基本属性。首先,进程是一个可拥有资源的独立单位;其次,进程同时又是一个可以独立调度和分派的基本单位。正是由于进程具有这两个基本属性,才使之成为个能独立运行的基本单位,从而也构成了进程并发执行的基础。

进程具有以下特性。

(1)并发性

一个进程可以同其他进程一道向前推进,即一个进程的第一个动作可以在另一个进程的最后一个动作结束之前就开始。

(2)动态性

进程对应着程序的执行过程。进程的动态性体现在两方面。首先,进程有其生命周期,有产生,也有消亡。其次,在进程的生命周期内,进程的状态是不断变化的。

(3)独立性

一个进程是一个相对完整的资源分配单位。

(4)交往性

一个进程在运行过程中可能会与其他进程发生直接的或间接的相互作用。

(5)异步性

每个进程按照各自独立的、不可预知的速度向前推进。

(6)结构性

一个进程由程序、数据和进程控制块三部分组成

二、进程的状态与转换

进程在从创建到终止的全过程中一直处于一个不断变化的过程。为了刻画进程的这个变化过程,所有操作系统都把进程分成若干种状态,约定各种状态间的转换条件。对进程状态的刻画也经历了一个不断精确化的过程。下面我们就讨论进程的状态模型。

1.三状态进程模型

运行中的进程可以处于以下三种状态之一:运行、就绪、等待

(1)运行状态( Running)

是指进程已获得处理器,并且在处理器上执行的状态。显然,在一个单处理器系统中,最多只有一个进程处于运行态

(2)就绪状态( Ready)

是指一个进程已经具备运行条件,但由于没有获得处理器而不能运行所处的状态。一旦把处理器分配给它,该进程就可运行。处于就绪状态的进程可以是多个。

(3)等待状态( Waiting)

也称阻塞状态或封锁状态。是指进程因等待某种事件发生而暂时不能运行的状态。例如,当两个进程竞争使用同一个资源时,没有占用该资源的进程便处于等待状态,它必须等到该资源被释放后才可以去使用它。引起等待的原因一旦消失,进程便转为就绪状态,以便在适当的时候投入运行。系统中处于等待状态的进程可以有多个。

在任何时刻,任何进程都处于且仅处于三种状态之一。进程在运行过程中,由于它自身的进展情况和外界环境条件的变化,三种基本状态可以相互转换。这种转换由操作系统完成,对用户是透明的。它也体现了进程的动态性。图3-2表示了三种基本状态之间的转换及其典型的转换原因。

1)就绪→运行。处于就绪状态的进程,它已具备了运行的条件,但由于未能获得处理器,故仍然不能运行。对于单处理器系统而言,因为处于就绪状态的进程往往不止一个,同一时刻只能有一个就绪进程获得处理器。进程调度程序根据调度算法(如优先级或时间片)把处理器分配给某个就绪进程,建立该进程运行状态标记,并把控制转入该进程的启动程序,/就绪把它由就绪状态变为运行状态。这样进程就投入运行。

2)运行→就绪。这种状态变化通常出现在分时操作系统中。正在运行的进程,由于规定的运行时间片用完而使系统发出超时中断请求,超时中断处理程序把该进程的状态修改为就绪状态,根据其自身的特征而插入就绪队列的适当位置,保留进程现场信息,收回处理器并转入进程调度程序。于是,正在运行的进程就由运行状态变为就绪状态。

3)运行→等待。处于运行状态的进程能否继续运行,除了受时间限制外,还受其他种种因素的影响。例如,运行中的进程需要等待文件的输入(或其他进程同步操作的影响)时,控制便自动转入系统控制程序,通过信息管理程序及设备管理程序进行文件输入;在输入过程中这个进程并不恢复到运行状态,而是由运行变成等待(此时,标记等待原因,并保留当前进程现场信息),然后控制转入进程调度程序。进程调度程序根据调度算法把处理器分配给原已处于就绪状态的进程。

4)等待→就绪。等待的进程在其被阻塞的原因获得解除后,并不能立即投入运行,因为处理器满足不了进程的需要,于是将其状态由等待变成就绪,仅当进程调度程序把处理器再次分配给它时,才可恢复现场继续运行

2.五状态进程模型

五状态进程模型中,进程状态被分成下列五种状态。进程在运行过程中主要是在就绪、运行和阻塞三种状态间进行转换。创建状态和退出状态描述进程创建的过程和进程退出的过程

1)运行状态( Running):进程占用处理器资源,处于此状态的进程的数目小于等于处理器的数目。在没有其他进程可以执行时(如所有进程都在阻塞状态),通常会自动执行系统的空闲进程。

2)就绪状态( Ready):进程已获得除处理器外的所需资源,等待分配处理器资源,只要分配处理器就可执行。如图33所示,就绪进程可以排成一个就绪队列,也可以按多个优先级来划分队列。例如:当一个进程由于时间片用完而进入就绪状态时,排入低优先级队列;当进程由于I/O操作完成而进入就绪状态时,排入高优先级队列。

3)阻塞状态( Blocked):由于进程等待I/O操作或进程同步等条件而暂停运行时处于阻塞状态。在条件满足之前,即使把处理器分配给该进程,也是无法继续执行的。如图3-3所示,所有处于阻塞状态的进程排成一个阻塞队列,也可以按等待事件的不同分成多个子队列

4)创建状态(New):进程正在创建过程中,还不能运行。操作系统在创建状态要进行的工作包括分配和建立进程控制块表项、建立资源表格(如打开文件表),并分配资源,加载程序并建立地址空间表等。

5)结束状态(Exit):进程已结束运行,回收除进程控制块之外的其他资源,并让其他进程从进程控制块中收集有关信息(如记账和将退出代码传递给父进程)。

操作系统中多个进程的并发执行通过进程交替进入运行状态来实现的。在五状态进程模型中进程的主要状态交替循环有两个,一个由调度与超时这两个转换构成。当一个正处于运行状态的进程超时后放入就绪队列,并修改进程状态为就绪状态;然后通过调度选择另一个就绪进程进入运行状态,完成一次运行进程的交替。另一个由调度、等待事件和事件出现这三个转换构成。当一个正处于运行状态的进程需要等待某事件发生时主动放弃处理器,进入阻塞状态;然后通过调度选择另一个就绪进程进入运行状态;当进程所等待的事件出现时,相应进程被从阻塞队列中取出,并放入就绪队列,同时进程状态从阻塞状态转换成就绪状态,等待进入运行状态。这样就完成了一次从运行到阻塞,再到就绪的状态循环。

下面是五状态进程模型中的主要状态转换。

1)创建新进程:创建一个新进程,以运行一个程序。创建新进程的可能原因包括用户登录、操作系统创建以提供某项服务、批处理进程等。

2)提交( Admit):完成一个新进程的创建过程,新进程进入就绪状态。由于性能、内存、进程总数等原因,系统会限制并发进程总数。

3)调度运行( Dispatch):从就绪进程表中选择一个进程,进入运行状态。

4)释放( Release):由于进程完成或失败而终止进程运行,进入结束状态。为了简洁,状态变迁图中只画出了运行状态到结束状态间的释放转换;但实际上,还存在从就绪状态或阻塞状态到结束状态的释放转换。运行到结束的转换可分为正常退出(Exit)和异常退出( Abort);其中异常退出是指进程执行超时、内存不够、非法指令或地址访问、I/O操作失败、被其他进程所终止等原因而退出。可能导致从就绪或阻塞到结束转换的可能是由于多种原因引发的,如父进程可在任何时间终止子进程。

5)超时( Timeout)或被抢占:由于用完时间片或由于高优先级进程就绪被抢占等原因导致进程暂停运行。

6)事件等待( Event Wait):进程要求的事件未出现而进入阻塞;可能的原因包括申请系统服务或资源、通信、I/O操作等。

7)事件出现( Event occurs):进程等待的事件出现;如操作完成、申请成功等。

3.七状态进程模型

五状态进程模型没有区分进程地址空间位于内存还是外存,而在操作系统中引人虚拟存储管理技术后,需要进一步区分进程的地址空间状态。该问题的出现是由于进程优先级的引入,一些低优先级进程可能等待较长时间,从而被对换至外存。这种做法可得到下列好处。

1)提高处理器效率:就绪进程表为空时,有空闲内存空间用于提交新进程,可提高处理器效率

2)可为运行进程提供足够内存:资源紧张时,可把某些进程对换至外存。

3)有利于调试:在调试时,挂起被调试进程,可方便对其地址空间进行读写。

如图34所示,与五状态进程模型相比,七状态进程模型把原来的就绪状态和阻塞状态进行了细分,增加了就绪挂起和阻塞挂起两个状态。这时原来的就绪状态和阻塞状态的意义也发生了一些变化。下面列出的是在挂起进程模型中的四种意义有变化或新的状态。

1)就绪状态( Ready):进程在内存且可立即进入运行状态。

2)阻塞状态( Blocked):进程在内存并等待某事件的出现。

3)阻塞挂起状态( Blocked, Suspend):进程在外存并等待某事件的出现。

4)就绪挂起状态( Ready, Suspend):进程在外存,但只要进入内存,即可运行。

在七状态进程模型中,新引入的状态转换有挂起和激活两类,意义有变化的转换有事件出现和进程提交两类。

1)挂起( Suspend):把一个进程从内存转到外存;可能有以下几种情况。

阻塞到阻塞挂起:没有进程处于就绪状态或就绪进程要求更多内存资源时,会进行这种转换,以提交新进程或运行就绪进程。

就绪到就绪挂起:当有高优先级阻塞(系统认为会很快就绪的)进程和低优先级就绪进程时,系统会选择挂起低优先级就绪进程。

运行到就绪挂起:对抢先式分时系统,当有高优先级阻塞挂起进程因事件出现而进入就绪挂起时,系统可能会把运行进程转到就绪挂起状态。

2)激活( Activate):把一个进程从外存转到内存;可能有以下几种情况。

就绪挂起到就绪:就绪挂起进程优先级高于就绪进程或没有就绪进程时,会进行这种转换。

阻塞挂起到阻塞:当一个进程释放足够内存时,系统会把一个高优先级阻塞挂起进程激活,系统认为会很快出现该进程所等待的事件。

3)事件出现( Event occurs):进程等待的事件出现;如操作完成、申请成功等;可能的情况如下。

阻塞到就绪:针对内存进程的事件出现。

阻塞挂起到就绪挂起:针对外存进程的事件出现。

4)提交( Admit):完成一个新进程的创建过程,新进程进入就绪状态或就绪挂起状态。进入就绪挂起的原因是系统希望保持一个大的就绪进程表(挂起和非挂起)。

三、进程控制块

为了便于系统控制和描述进程的活动过程,在操作系统核心中定义了一个专门的数据结构,称为进程控制块( Process Control block,PCB)。

操作系统利用PCB来描述进程的基本情况以及进程的运行变化过程PCB是进程存在的唯一标志,当系统创建一个进程时,为进程设置一个PCB,再利用PCB对进程进行控制和管理。撤销进程时,系统收回它的PCB,进程也随之消亡

1.PCB的内容

进程控制块的内容可以分成调度信息现场信息两大部分。调度信息供进程调度时使用,描述了进程当前所处的状况,它包括进程名、进程号、地址空间信息、优先级、当前状态、资源清单、“家族”关系、消息队列指针、进程队列指针和当前打开文件等

在调度信息中,进程名和进程号的作用是标识一个进程,一个进程的进程号必须是唯一的,用以说明进程的存在并区分各个进程

系统为每个进程分配了一个地址空间,相关信息记录在地址空间信息内。

优先级,确定了进程本身的优先级别,供调度时使用。

当前状态,指明了该进程处于进程基本状态中的哪一种,是就绪的,还是等待的,还是正在运行。

资源清单,列出了该进程运行所需的各种资源。

“家族”关系,指明该进程是一个父进程,还是被别的进程所创建的子进程。

消息队列指针,指明了进程所属的消息队列所在的地址,消息队列的作用是与其他进程进行通信。

进程队列指针,指出进程在队列中的位置。

当前打开文件,记录了进程正在使用的文件的情况。

现场信息刻画了进程的运行情况,由于每个进程都有自己专用的工作存储区,其他进程运行时不会改变它的内容,所以,PCB中的现场信息只记录那些可能会被其他进程改变的

寄存器,如程序状态字、时钟、界地址寄存器等。一旦中断进程的运行,必须把中断时刻的

上述内容记入进程控制块的现场信息。

需要指出的是,PCB的内容和大小随系统不同而异,它不仅和具体系统的管理及控制方法有关,也和系统规模的大小有关。

2.进程的组成

进程由程序、数据和进程控制块三部分组成。PCB是进程的“灵魂”,由于进程控制块中保存有进程的地址信息,通过PCB可以得到进程程序的存储位置,也可以找到整个进程。程序和数据是进程的“躯体”,程序部分描述了进程要实现的功能,而数据则是程序操作的对象。

3.PCB组织

为了便于管理,系统把所有的PCB用适当方式组织起来。一般说来,大致有以下三种组织方式。

(1)线性方式

将所有的PCB不分状态组织在一个连续表(称PCB表)中。该方式的优点是简单,且不需要额外的开销,适用于进程数目不多的系统;但缺点是往往需要扫描整个PCB表,才能找到需要的PCB。如图3-5a所示。

(2)索引方式

对于具有相同状态的进程,分别设置各自的PCB索引表,表目为每个PCB在该PCB表(线性表)中的地址。于是就构成了就绪索引表和等待索引表。另外,在内存固定单元设置三个指针,分别指示就绪索引表和等待索引表的起始地址以及执行态PCB在PCB表中的地址,如图35b所示。

(3)链接方式

对于具有相同状态进程的PCB,通过PCB中的链接字构成一个队列。链接字指出本队列下一PCB在PCB表中的编号(或地址),编号为0表示队尾,队首由内存固定单元中相应的队列指针指示,如此便形成就绪队列和等待队列。

等待队列可以有多个,对应于不同的等待原因,如等待I/O操作完成,等待分配内存,等待接收消息等。就绪队列的排队原则跟调度策略有关,可以按优先数据排序,也可以按“先进先出”的原则出队等。另外,还可以将PCB表中的各空表目连接起来构成一个自由队列,若队列指针为0,表示该队列为空,如图35c所示。

4.进程的队列

为了实现对进程的管理,系统将所有进程的PCB排成若干个队列。通常,系统中进程队列分成如下三类。如图36所示。

(1)就绪队列

整个系统所有处于就绪状态的进程都按照某种原则排在该队列中。进程入队和出队的次序与处理器调度算法有关。在有些系统中,就绪队列可能有若干个。

(2)等待队列

每一个等待事件一个队列。当进程等待某一事件时,进入与该事件相应的等待队列。当某事件发生时,与该事件相关的一个或多个进程离开相应的等待队列。

(3)运行队列

在单处理器系统中整个系统有一个运行队列。实际上,一个运行队列中只有一个进程,可用一个指针指向该进程。

5.进程队列的组成

进程队列可以用进程控制块的链接来形成。常用链接的方式有两种:单向链接和双向链接

在单向链接中,同一队列中的进程,通过进程控制块中的队列指针联系起来,前一进程的进程控制块中的指针值为它的下一个进程的进程控制块的地址,队列中最后一个进程的进程控制块中的指针值置为“0”。

在双向链接中,设置两个指针,称为前向指针和后向指针,分别指出它的前一个或后个进程的进程控制块地址。另外,系统还为每个队列设置一个队首指针,指出该队列的第一个和最后一个进程的进程控制块地址,以便进行双向搜索。

一个刚刚被创建的进程,它的初始状态是“就绪态”。因此,它应该置于就绪队列中。

当一个就绪进程能被选中占用处理器时,就从就绪队列中退出成为“运行态”。进程在运行过程中,可能要求读磁盘上的信息而进入等待队列。可见,进程在执行过程中,随着状态的变化经常要从一个队列退出后再进入另一个队列,直至进程工作结束。

一个进程从所在的队列中退出称为“出队”;一个进程排入到指定的队列中称“入队”;

一个进程插入到某个进程队列中间的指定位置称“插队”;系统中负责进程入队和出队的工作称“队列管理”。

现在以双向链接的队列讨论进程的“出队”和“入队”。假定某个指定进程要退出队列,则首先找到该队列的队首指针,沿链查找到要出队的进程,找到后只要修改与该进程相邻进程的前向或后向队列指针值就行了。

下面列出进程出队的具体过程。根据要出队的进程原先在队列中的位置,可以分成三种情况。

(1)队首进程出队

队首进程出队时,只要把该进程的后继进程的前后指针值修改成“0”,把出队进程的后向指针值送入队首指针单元中。于是,原先在队列中的第二个进程成了队首,而原来的队首进程已与队列脱钩了。

(2)非队首(或队尾)进程出队

如果欲出队的进程既不是队首进程也不是队尾进程,则可以假定进程B要出队,进程B的前一个进程是进程A,而后一个是进程C。于是,进程B的出队过程是:把进程B的前向指针值送入进程C的前向指针位置;把进程B的后向指针值送入进程A的后向指针位置。于是,进程A的后向指针就指向进程C,进而程C的前向指针就指向进程A,进程B就从队列中退了出来。

(3)队尾进程出队

按照队尾进程的前向指针值找到它的前一个进程的进程控制块,把该进程的后向指针值修改成“0”成为当前的队尾进程,原先的队尾进程已经退出了队列。

如果某进程要加入到一个队列中去,若队列原来是空的,则入队的进程就成为该队列的第一个进程。若原队列非空,则应先找到入队进程应插入的位置,再根据链接要求修改相邻进程的队列指针值。

进程入队时,根据应插入的位置也可分成三种:从队首人队成为新的队首进程;从队尾入队成为新的队尾进程;插入到队列中某两个进程之间。这里简要地叙述进程插入到队列中某两个进程之间的过程。

进程入队的位置既不是队首也不是队尾,假定进程B要插入队列,进程B的前一个进程是进程A,而后一个是进程C。插入进程的过程如下:在进程B的前向指针单元中记入进程C的前向指针位置,在进程B的后向指针单元记入进程A的后向指针位置,这样进程B的前后指针就分别指向了进程A和进程C。接着还要修改进程A的后向指针和进程C的前向指针,于是进程B进程控制块的地址送入进程A的后向指针位置,再把进程B进程控制块的地址送入进程C的前向指针位置。于是,进程A的后向指针就指向进程B,而进程C的前向指针就指向进程B,进程B就插入队列了。

从队首入队和从队尾入队的过程请读者自行练习总结。

四、进程控制

我们已经知道,进程有一个从创建到消亡的生命周期,这就需要对进程在整个生命周期中各种状态之间的转换进行有效的控制,称为进程控制进程控制是通过进程控制原语来实现的

所谓原语,是由若干条指令所组成的一个指令序列,用来实现某个特定的操作功能。这个指令序列的执行是连续的,具有不可分割性,在执行时也不可间断,直至该指令序列执行结束。原语又可称为原子操作。

原语是操作系统核心(由一组程序模块所组成的、完成操作系统中基本功能)的一个组成部分。原语必须在管态下执行,并且常驻内存。

原语和系统调用都可以被进程所调用,两者的差别在于原语有不可中断性,它是通过在其执行过程中关闭中断实现的,而且原语往往是被系统进程所调用。

许多系统调用的功能都可用目态下运行的系统进程完成,而不一定要在管态下完成。例如文件的建立、打开、关闭、删除等系统调用,都是借助中断进入管态,然后转交给相应的进程,最终由进程实现其功能。下面介绍几个常用的原语。

1.进程控制原语

用于进程控制的原语一般有:创建进程、撤销进程、挂起进程、激活进程、阻塞进程、唤醒进程以及改变进程优先级等。

(1)创建原语 一个进程可以使用创建原语创建一个新的进程,前者称为父进程,后者称为子进程。子进程又可以创建新的子进程,构成新的父子关系,从而整个系统可以形成一个树形结构的进程家族。

创建一个进程的主要任务是建立进程控制块PCB。

创建进程的具体操作过程是:先申请一空闲PCB区域,然后将有关信息填入PCB,置该进程为就绪状态,最后把它插入就绪队列中。

(2)撤销原语

当一个进程完成任务后,就应当撤销它,以便及时释放它所占用的资源。撤销进程的实质是撤销PCB。一旦PCB撤销,进程就消亡了。

撤销进程的具体操作过程是:找到要被撤销进程的PCB,将它从所在队列中消去,撤销属于该进程的一切“子孙进程”,释放被撤销进程所占用的全部资源,并消去被撤销进程的PCB。

(3)阻塞原语

若某个进程的执行过程中,需要执行I/O操作,则由该进程调用阻塞原语把进程从运行状态转换为阻塞状态。

具体的操作过程是:由于进程正处于运行状态,因此首先应中断处理器的执行,把处理器的当前状态保存在PCB的现场信息中,然后把进程的当前状态置为等待状态,并把它插入到该事件的等待队列中去。

(4)唤醒原语

一个进程因为等待某事件的发生而处于等待状态,当该事件发生后,就用唤醒原语将其转换为就绪状态。

具体操作过程是:在等待队列中找到该进程,将进程的当前状态置为就绪状态,然后将它从等待队列中撤出并插入到就绪队列中排队,等待调度执行。

2.UNIX操作系统的进程创建操作fork

在UNⅨX类操作系统中,父进程通过调用fork函数创建子进程。典型的步骤如下。

1)为子进程分配一个空闲的proc结构(即进程描述符)。

2)赋予子进程唯一的标识PID。

3)以一次一页的方式复制父进程用户地址空间。

4)获得子进程继承的共享资源的指针,如打开的文件和当前工作目录等。

5)子进程就绪,加入调度队列。

6)对子进程返回标识符0;向父进程返回子进程的PID

以上步骤说明新创建的子进程基本与父进程相同:子进程得到与父进程用户地址空间相同的一份复制,包括文本、数据和bss段、堆以及用户栈;子进程还获得与父进程任何打开文件描述符相同的复制,这就意味着当父进程调用fork函数时,子进程可以读写父进程中打开的任何文件。父进程和新建子进程的区别在于它们有不同的PID。

fork函数执行的特点是:只被调用一次,却会返回两次:一次是在调用进程(父进程)中,一次是在新创建的子进程中。在父进程中,fork函数返回子进程的PID。在子进程中,fork函数返回0。因为子进程的PID总是非零的,通过返回值就可以区分程序是在父进程还是在子进程中执行fork函数复制了一个自己,但是,创建了子进程并非要运行另一个与父进程一模一样的进程,绝大部分子进程需要运行不同于父进程的程序代码,这时我们需要调用exec函数来替换原父进程的代码。而exec函数为子进程运行不同于父进程的代码提供了一条途径。

执行exec函数,其典型的步骤如下。

1)在原进程空间装入新程序的代码、数据、堆和栈。

2)保持进程ID和父进程ID等。

3)继承控制终端。

4)保留所有文件信息,如目录、文件模式和文件锁等。

在UNⅨ中,父进程与子进程的执行是异步的,因此,父进程可能早于子进程结束,如此一来,子进程的资源,例如内存,就有可能无法归还给父进程,引起内存泄漏等问题。

wait函数就是父进程用来获取子进程的结束状态并回收资源的,父进程调用wait函数自我阻塞,等候子进程结束发来信号,该信号唤醒父进程后由父进程回收子进程的各项资源、清理表格及回收内存等;若子进程先于父进程结束,此时,子进程会暂时变为“僵尸”状态,继续占有部分资源,直到父进程运行wait函数时资源才被回收(此时父进程不需要阻塞)。

如果子进程不等父进程回收,而是在程序末尾直接调用exit函数退出,那么,在标准UNⅨ系统上,由于子进程调用了exit函数,会刷新关闭所有标准I/O流,包括标准输出。

虽然这是由子进程执行的,但却是在父进程的地址空间中进行的,所以所有受到影响的标准I/O对象都是在父进程中的。当父进程再调用标准输出时,标准输出已被关闭了,于是出错返回-1。因此一般子进程不使用exit函数,而是用内核的exit函数,并等待父进程回收(注:改进后的 Linux操作系统与UNIX不同,有兴趣的读者可以自行查阅相关手册)。

信号( Signal)函数是UNX处理异步事件的经典方法。信号可以说是进程控制的一部分。信号的产生如下。

1)当用户按某些终端键时,产生信号。

2)硬件异常产生信号:除数为0、无效的存储访问等。

3)进程用kl函数可将信号发送给另一个进程或进程组。

4)用户可用kl函数将信号发送给其他进程。

5)当检测到某种软件条件已经发生,并将其通知有关进程。

该文章转载于自考库,如有侵权,请联系删除

点赞