一、同步与互斥
(一)两种形式的制约关系
间接相互制约关系(互斥)。若某一进程要求使用某种资源,而该资源正被另一进程使用,并且该资源不允许两个进程同时使用,那么该进程只好等待已占用资源的进程释放资源后再使用。这种制约关系源于多个同种进程需要互斥地共享某种系统资源,互斥是设置在同种进程之间以达到互斥地访问资源的目的。
直接相互制约关系(同步)。某一进程若收不到另一进程给它提供的必要信息就不能继续运行下去,这种情况表明了两个进程之间在某些点上要交换信息,相互交流运行情况。这种制约主要源于进程间的合作,同步设置在不同进程之间以达到多种进程间的同步。
区分互斥与同步,只要是同类进程即为互斥关系,不同类进程即为 同步关系。
(二)临界资源与临界区
临界资源即为同时仅允许一个进程使用的资源。一般临界资源的访问过程可以分成4部分,分别为:进入去、临界区、退出区和剩余区。
临界区即为进程中用于访问临界资源的代码,又称临界段。临界区是属于对应进程的,每个进程的临界区代码可以不相同,只与每个进程对临界资源进行怎样的操作,这和临界资源及互斥同步管理无关。
(三)互斥的概念与要求
根据互斥的定义,当一个进程进入临界区使用临界资源时,另一个进程必须等待,知道占用该临界资源的进程退出临界区后,才允许新的进程访问该临界资源。具有以下准则:空闲让进、忙则等待、有限等待、让权等待。
(四)同步的概念与实现机制
一般来说,一个进程相对另一个进程的运行速度是不确定的。也就是说,进程间是在异步环境下运行的。但是相互合作的就能成需要在某些关键点上协调它们的工作。所谓进程同步,是指多个相互合作的进程在一些关键点上可能需要相互等待或相互交换信息,这种相互制约关系成为同步。可以用信号量实现同步。
(五)信号量实现同步
信号量是一个确定的二元组(s,q),其中s是一个具有非负初值的整形变量,q是一个初始状态为空的队列。一个信号量的建立必须经过说明,即应该准确说明s的意义和初值。每个信号量都有一个队列,在建立信号量时队列为空。
设s是一个信号量,P(s)执行时,主要完成以下动作:先执行s=s-1;若s>=0,则该进程继续运行;若s<0,则阻塞该进程,并将它插入该信号量的等待队列中。
V(s)执行时主要完成以下操作:先执行s=s+1;若s>0,则该进程继续执行;若s<=0,则从该信号量等待队列中移出第一个进程,使其变为就绪状态并插入就绪队列,然后再返回原进程继续执行。
实现同步互斥的P、V操作必须成对出现,先进行P操作进入临界区,后进行V操作退出临界区。在有多个信号量同时存在的情况下,P操作往往是不能颠倒顺序的,必须先对资源信号量进行P操作,再对互斥信号量进行P操作,这样可以在占有信号量访问权时保证有资源可以使用,否则会产生占用使用权而无资源可用的“死等”现象。
并发的若干进程之间存在同步与互斥关系。多个进程操作同一个临界资源就是互斥问题,多个进程按照某种固定顺序执行就是同步问题。对于互斥问题,要在互斥的若干进程的临界区前后设置互斥信号量;对于同步问题,要在前趋进程中加入释放操作(对某信号量进行V操作)用于唤醒后继进程,在后继进程中加入申请操作(对相同信号量进行P操作)以保证该进程在前趋进程之后执行。
(六)经典同步问题
1、生产者-消费者为题
2、读者-写者问题
3、哲学家进餐问题
4、理发师问题
二、 死锁
(一)死锁的概念
当多个进程因竞争系统资源或相互通信而处于永久阻塞状态时,若无外力作用,这些进程都将无法向前推进。这些进程中的每一个进程,均无限期地等待此组进程中某个其他进程占有的、自己永远无法得到的资源,这种现象成为死锁。
(二)死锁产生的原因
死锁产生的原因是竞争资源。系统资源不足是产生死锁的根本原因。进程推进顺序不当是产生死锁的重要原因。
死锁产生的必要条件有以下4条:①互斥条件。进程要求对所分配的资源进行排他性控制,即在一段时间内某种资源仅为一个进程占有。②不剥夺条件。进程所获得的资源在未使用完毕前,不能被其他进程强行夺走,即只能由获得该资源的进程自己来释放。③请求与保持条件。进程每次申请它所需的一部分资源。在等待分配新资源的同时,进程继续占有已经分配到的资源。请求于报纸条件也成为部分分配条件。环路等待条件。存在一种进程资源的循环等待链,而链中的每一个进程已经获得的资源同时被链中的下一个进程所请求。
要产生死锁,这4个条件缺一不可,因此可以通过破坏其中的一个或几个条件来避免死锁的产生。
(三)处理死锁的基本方法
处理死锁主要有以下4种方法:
1、鸵鸟算法。对死锁视而不见,即不理不睬。
2、预防死锁。通过设置某些限制条件,去破坏产生死锁的4个必要条件中的一个或几个来预防死锁的发生。
3、避免死锁。在资源的动态分配过程中,用某种方法防止系统进入不安全状态,从而避免死锁的产生。
4、检测及解除死锁。通过系统的检测机构及时检测出死锁的发生,然后采取某种措施避免死锁。
(四)利用银行家算法避免死锁
假设系统中有n个进程(P1, P2, ... , Pn)、m类资源(R1, R2, ... , Rm),银行家算法的数据结构如下:
1、可利用资源向量Available。这是一个含有m个元素的数组,其中Available[i]的值表示第i类资源的现有空闲数量,其初始值为系统中所配置的该类资源的数目。
2、最大需求矩阵Max。这是一个n*m的矩阵,它定义了系统中每一个进程对m类资源的最大需求数。Max[i][j]的值表示第i个进程对第j类资源的最大需求数。
3、分配矩阵Allocation。这是一个n*m的矩阵,它定义了系统中每一类资源当前已经分配给每一个进程的资源数目。Allocation[i][j]的值表示第i个进程当前拥有的第j类资源的数量。
4、需求矩阵Need。这是一个n*m的矩阵,它定义了系统中每个进程还需要的各类资源数目。Need[i][j]的值表示第i个进程还需要的第j类资源的数量。向量Needi是矩阵Need的第i行,是进程i的需求资源向量。
5、上述的n*m的“矩阵三兄弟”具有关系:Need[i][j] = Max[i][j] - Allocation[i][j]。
银行家算法描述如下:
1、定义Requesti向量:Requesti表示第i个进程向系统提出一次申请,申请的各类资源的数量就是该向量的各个分量。
2、当进程Pi向系统发出资源请求后,系统进行如下操作:
3、若Requesti<=Needi,则继续向下执行,否则报错,因为进程Pi申请的资源数不应该超过它的需求数。
4、若Requesti<=Availablei,则继续向下执行,否则Pi进程需要等待,因为可用资源不够。
5、对Pi进程所请求的资源进行预分配,修改向量:Available=Available-Requesti ,Allocationi=Allocationi+Requesti ,Needi=Needi-Requesti。
6、对于修改后的向量调用安全性算法。若安全性算法返回系统处于安全状态,则按Requesti表示的资源数量给Pi进程分配资源;若安全性算法返回系统处于不安全状态,则不分配给Pi进程任何资源,让Pi进程等待,并恢复第5步中所改变的向量。
安全性算法描述如下:
1、简历长度为m的向量Work和长度为n的向量Finish,并对它们进行初始化:Work = Available;Finish[i] = false;(i=1,2, ... , n)
2、查找满足如下两个条件的i:Finish[i]=false; Needi <= Work; 若没有这样的i存在,则跳到第4步执行。
3、Work = Work + Allocationi; Finish[i]=true; 返回第2步执行。
4、如果对于所有的i,都有Finish[i]=true,那么系统处于安全状态,否则系统处于不安全状态。