实验和练习
实验 同步机制之生产者和消费者问题【操作系统】
实验 Linux死锁现象及分析【操作系统】
第三章 进程调度和锁
前言
在多道程序环境下,内存中存在着多个进程,其数目往往多于处理机数目。这就要求系统能按某种算法,动态地将处理机分配给处于就绪状态的一个进程,使之执行。分配处理机的任务是由处理机调度程序完成的。对于大型系统运行时的性能,如系统吞吐量、资源利用率、作业周转时间或响应的及时性等,在很大程度上都取决于处理机调度性能的好坏。因而,处理机调度便成为OS中至关重要的部分。
1.处理机调度的层次和调度算法的目标
1.1 处理机调度的层次
1高级调度
高级调度又称长程调度或作业调度,它的调度对象是作业。其主要功能是根据某种算法,决定将外存上处于后备队列中的哪几个作业调入内存,为它们创建进程、分配必要的资源,并将它们放入就绪队列。高级调度主要用于多道批处理系统中,而在分时和实时系统中不设置高级调度。
2低级调度
低级调度又称为进程调度或短程调度,其所调度的对象是进程(或内核级进程)。其主要功能是,根据某种算法,决定就绪队列中的哪个进程应获得处理机,并由分派程序将处理机分配给被选中的进程。进程调度是最基本的一种调度,在多道批处理、分时和实时三种类型的OS中,都必须配置这级调度。
3中级调度
中级调度又称为内存调度。引入中级调度的主要目的是,提高内存利用率和系统吞吐量。为此应把那些暂时不能运行的进程,调至外存等待,此时进程的状态称为就绪驻外存状态(或挂起状态)。当它们已具备运行条件且内存又稍有空闲时,由中级调度来决定,把外存上的那些已具备运行条件的就绪进程再重新调入内存,并修改其状态为就绪状态,挂在就绪队列上等待。
1.2 处理机调度算法的目标
1处理机调度算法的共同目标
(1) 资源利用率。
(2) 公平性。
(3) 平衡性。
(4) 策略强制执行。
2批处理系统的目标
(1) 平均周转时间短。
(2) 系统吞吐量高。
(3) 处理机利用率高。
3分时系统的目标
(1) 响应时间快。
(2) 均衡性。
4实时系统的目标
(1) 截止时间的保证。
(2) 可预测性。
2.作业与作业调度
2.1 批处理系统中的作业
1作业和作业步
(1) 作业(Job)。作业是一个比程序更为广泛的概念,它不仅包含了通常的程序和数据,而且还应配有一份作业说明书,系统根据该说明书来对程序的运行进行控制。在批处理系统中,是以作业为基本单位从外存调入内存的。
(2) 作业步(Job Step)。通常,在作业运行期间,每个作业都必须经过若干个相对独立,又相互关联的顺序加工步骤才能得到结果。我们把其中的每一个加工步骤称为一个作业步,各作业步之间存在着相互关系,往往是上一个作业步的输出作为下一个作业步的输入。例如,一个典型的作业可分成:“编译”作业步,“链接装配”作业步和“运行”作业步。
2作业控制块(Job Control Block, JCB)
为了管理和调度作业,在多道批处理系统中,为每个作业设置一个作业控制块JCB,它是作业在系统中存在的标志,其中保存了系统对作业进行管理和调度所需的全部信息。
3作业运行的三个阶段和三种状态
(1) 收容阶段。
(2) 运行阶段。
(3) 完成阶段。
2.2 作业调度的主要任务
作业调度的主要任务是,根据JCB中的信息,检查系统中的资源能否满足作业队资源的需求,以及按照一定的调度算法,从外存的后备队列中选取某些作业调入内存,并为它们创建进程、分配必要的资源。然后再将新创建的进程排在就绪队列上等待调度。因此,也把作业调度称为接纳调度。每次执行作业调度时,都需做出以下两个决定。
1接纳多少个作业
在每一次进行作业调度时,应当从后备队列中选取多少作业调入内存,取决于多道和 序度(Degree of Multiprogramming),即允许多少个作业同时在内存中运行。对系统来说,希望装入较多的作业,有利于提高CPU的利用率和系统的吞吐量。但如果内存中同时运行的作业太多时,进程在运行时因内存不足所发生的中断就会急剧增加。这将会使平均周转 时间显著延长,影响到系统的服务质量。因此,多道程序度的确定是根据计算机的系统规模、运行速度、作业大小,以及能否获得较好的系统性能等情况作出适当的抉择的。
2接纳哪些作业
应选择后备队列中的哪些作业调入内存,取决于所采用的调度算法。最简单的是先来 先服务调度算法,它是将最早进入外存的作业优先调入内存。较常用的一种算法是短作业 优先调度算法,是将外存上所需执行时间最短的作业优先调入内存。另一种较常用的是基 于作业优先级的调度算法,该算法是将外存上作业优先级最高的作业优先调入内存。比较 好的一种算法是“响应比高者优先”的调度算法。我们将在后面对上述的几种算法作较详细的介绍。
在批处理系统中,作业进入系统后,总是先驻留在外存的作业后备队列上,因此需要 有作业调度,以便将它们分批地装入内存。然而在分时系统中,为了做到及时响应,用户 通过键盘输入的命令或数据等都被直接送入内存,因而无需配置上述的作业调度机制,但 也需要有某种接纳控制措施来限制进入系统的用户数目。即如果系统尚有能力处理更多的 任务,将会接纳授权用户的请求,否则,便拒绝接纳。类似地,在实时系统中也不需要作业调度,而必需具有接纳控制措施。
2.3 先来先服务(FCFS)和短作业优先(SJF)调度算法
1先来先服务调度算法
FCFS是最简单的调度算法,该算法既可用于作业调度,也可用于进程调度。当在作业调度中采用该算法时,系统将按照作业到达的先后次序来进行调度,或者说它是优先考虑在系统中等待时间最长的作业。
2短作业优先调度算法
SJF算法是以作业的长短来计算优先级,作业越短,其优先级越高。作业的长短是以作业所要求的运行时间来衡量的。SJF算法可以分别用于作业调度和进程调度。在把短作业优先调度算法用于作业调度时,它将从外存的作业后备队列中选择若干个估计运行时间最短的作业,优先将它们调入内存运行。
2.4 优先级调度算法和高相应比优先调度算法
1优先级调度算法(priority-scheduling algorithm, PSA)
在优先级调度算法中,是基于作业的紧迫程度,由外部赋予作业相应的优先级,调度算法是根据该优先级进行调度的,这样就可以保证紧迫性作业优先运行。
2高相应比优先调度算法(Highest Response Ratio Next, HRRN)
高相应比优先调度算法是既考虑了作业的等待时间,又考虑作业运行时间的调度算法,因此既照顾了短作业,又不致使长作业的等待时间过长,从而改善了处理机制调度的性能。
该优先级可表示为:
3.进程调度
3.1 进程调度的任务、机制和方式
1进程调度的任务
(1) 保证处理机的现场信息
(2) 按某种算法选取进程。
(3) 把处理器分配给进程。
2进程调度机制
为了实现进程调度,在进程调度机制中,应具有如下三个基本部分,如下图。
(1) 排队器。为了提高进程调度的效率,应事先将系统中的所有就绪进程按照一定的策略排成一个或多个队列,以便调度程序能最快地找到它。以后每当有一个进程转变为就绪状态时,排队器便将它插入到相应的就绪队列。
(2) 分派器。分派器依据进程调度程序所选定的进程,将其从就绪队列中取出,然后进行从分派器到新选出进程间的上下文切换,将处理器分配给新选出的进程。
(3) 上下文切换器。在对处理机进行切换时,会发生两对上下文的切换操作:①第一对上下文切换时,OS将保存当前进程的上下文,即把当前进程的处理机寄存器内容保存到该进程的进程控制块内的相应单元,再装入分派程序的上下文,以便分派程序运行;②第二对上下文切换是移出分派程序的上下文,而把新选进程的CPU现场信息装入到处理机的各个相应寄存器中,以便新选进程运行。
3进程调度方式
(1) 非抢占方式(NonPreemptive Mode)
在采用这种调度方式时,一旦把处理机分配给某进程后,就一直让它运行下去,决不会因为时钟中断或任何其他原因去抢占当前正在运行进程的处理机,直至该进程完成,或发生某事件而被阻塞时,才把处理机分配给其他进程。
(2) 抢占方式(Preemptive Mode)
这种调度方式允许调度程序根据某种原则,去暂停某个正在执行的进程,将已分配给该进程的处理机重新分配给另一个进程。
“抢占”不是一种任意性行为,必须遵循一定的原则。主要原则有:
①优先权原则
②短进程优先原则
③时间片原则
3.2 轮转调度算法
在分时系统中,最简单也是较常用的是基于时间片的轮转(round robin, RR)调度算法。该算法采取了非常公平的处理机分配方式,即让就绪队列上的每个进程每次仅运行一个时间片。如果就绪队列上有n个进程,则每个进程每次大约都可获得1/n的处理机时间。
1轮转法的基本原理
在轮转(RR)法中,系统将所有的就绪进程按FCFS策略排成一个就绪队列。系统可设置每隔一定时间(如30ms)便产生一次中断,去激活进程调度程序进行调度,把CPU分配给队首进程,并令其执行一个时间片。当它运行完毕后,又把处理机分配给就绪队列中新的队首进程,也让它执行一个时间片。这样,就可以保证就绪队列中的所有进程在确定的时间段内,都能获得一个时间片的处理机时间。
2进程切换时间
在RR调度算法中,应在何时进行进程的切换,可分为两种情况:①若一个时间片尚未用完,正在运行的进程便已经完成,就立即激活调度程序,将它从就绪队列中删除,再调度就绪队列中队首的进程运行,并启动一个新的时间片。②在一个时间片用完时,计时器中断处理程序被激活。如果进程尚未运行完毕,调度程序将把它送往就绪队列的末尾。
3时间片大小的确定
一个较为可取的时间片大小是略大于一次经典的交互所需的时间,使大多数交互式进程能在一个时间片内完成,从而可以获得很小的响应时间。
3.3 优先级调度算法
1优先级调度算法的类型
优先级进程调度算法,是把处理机分配给就绪队列中优先级最高的进程。这时,又可进一步把该算法分成如下两种。
(1) 非抢占式优先级调度算法。
(2) 抢占式优先级调度算法。
2优先级的类型
优先级调度算法的关键在于:应如何确定进程的优先级,以及确定是使用静态优先级还是动态优先级。
(1) 静态优先级
确定优先级大小的依据有如下三个:
①进程类型。
②进程对资源的需求
③用户要求
(2) 动态优先级
动态优先级是指在创建进程之初,先赋予其一个优先级,然后其值随进程的推进或等待时间的增加而改变,以便获得更好的调度性能。
3.4 多队列调度算法
该算法将系统中的进程就绪队列从一个拆分为若干个,将不同类型或性质的进程固定分配在不同的就绪队列,不同的就绪队列采用不同的调度算法,一个就绪队列中的进程可以设置不同的优先级,不同的就绪队列本身也可以设置不同的优先级。
在多处理机系统中,该算法由于安排了多个就绪队列,因此,很方便为每个处理机设置一个单独的就绪队列。这样,不仅对每个处理机的调度可以实施各自不同的调度策略,而且对于一个含有多个线程的进程而言,可以根据其要求将其所有线程分配在一个就绪队列,全部在一个处理机上运行。再者,对于一组需要相互合作的进程或进程而言,也可以将它们分配到一组处理机所对应的多个就绪队列,使得它们能同时获得处理机并行执行。
3.5 多级反馈队列(multileved feedback queue)调度算法
1调度机制
多级反馈队列调度算法的调度机制可描述如下:
(1) 设置多个就绪队列。
(2) 每个队列都采用FCFS算法。
(3) 按队列优先级调度。
2调度算法的性能
在多级反馈队列算法中,如果规定第一个队列的时间片略大于多数人机交互所需处理时间时,便能较好地满足各种类型用户的需要。
(1) 终端型用户。
(2) 短批处理作业用户。
(3) 长批处理作业用户。
3.6 基于公平原则的调度算法
1保证调度算法
保证调度算法是另外一种类型的调度算法,它向用户所做的保证并不是优先运行,而是明确的性能保证,该算法可以做到调度的公平性。一种比较容易实现的性能保证是处理机分配的公平性。如果在系统中有n个相同类型的进程同时运行,为公平起见,须保证每个进程都获得相同的处理机时间1/n。在实施公平调度算法时系统中必须具有这样的一些功能:
(1) 跟踪计算每个进程自创建以来已经执行的处理时间。
(2) 计算每个进程应获得的处理机时间,即自创建以来的时间除以n。
(3) 计算进程获得处理机时间的比率,即进程实际执行的处理时间和应获得的处理机时间之比。
(4) 比较各进程获得处理机时间的比率。
(5) 调度程序应选择比率最小的进程将处理机分配给它,并让该进程一直运行,直到超过最接近它的进程比率为止。
2公平分享调度算法
在该调度算法中,调度的公平性主要是针对用户而言,是所有用户能获得相同的处理机时间,或所要求的时间比例。然而调度又是以进程为基本单位,为此,必须考虑到每一个用户所拥有的进程数目。例如系统中有两个用户,用户1有4个进程A、B、C、D,用户2只有1个进程E。为保证两个用户能获得相同的处理机时间,则必须执行如下所示的强制调度序列:AEBECEDEAEBECEDE…
如果希望用户1所获得的处理机时间是用户2的两倍,则必须执行如下所示的强制调度序列:ABECDEABECDEABECDE…
各种调度算法总结比较:
4.实时调度
4.1 实现实时调度的基本条件
1提供必要的信息
(1) 就绪时间。
(2) 开始截止时间和完成截止时间
(3) 处理时间
(4) 资源要求
(5) 优先级
2系统处理能力强
在实时系统中,若处理机的处理能力不够强,则有可能因处理机忙不过,而致使某些实时任务不能得到及时处理,从而导致发生难以预料的后果。
3采用抢占式调度机制
在含有HRT任务的实时系统中,广泛采用抢占机制。这样便可满足HRT任务对截止时间的要求。
4具有快速切换机制
为保证硬实时任务能及时运行,在系统中还应具有快速切换机制,使之能进行任务的快速切换。该机制具有如下两方面的能力:
(1) 对中断的快速响应能力。
(2) 快速的任务分派能力。
4.2 实时调度算法的分类
可以按不同方式对实时调度算法加以分类:①根据实时任务性质,可将实时调度算法分为硬实时调度算法和软实时调度算法;②按调度方式,则可分为非抢占调度算法和抢占调度算法。
1非抢占式调度算法
(1) 非抢占式轮转调度算法。
(2) 非抢占式优先调度算法。
2抢占式调度算法
(1) 基于时钟中断的抢占式优先调度算法。
(2) 立即抢占的优先级调度算法。
4.3 最早截止时间优先EDF算法
该算法是根据任务的截止时间确定任务的优先级,任务的截止时间愈早,其优先级愈高,具有最早截止时间的任务排在队列的队首。调度程序在选择任务时,总是选择就绪队列中的第一个任务,为之分配处理机。最早截止时间优先算法既可用于抢占式调度方式中,也可用于非抢占式调度方式中。
1非抢占式调度方式用于非周期实时任务
2抢占式调度方式用于周期实时任务
4.4 最低松弛度优先LLF(Least Laxity First)算法
该算法在确定任务的优先级时,根据的是任务的紧急(或松弛)程度。任务紧急程度愈高,赋予该任务的优先级就愈高,以使之优先执行。如一任务在400ms时必须完成,它本身需要运行150ms,则其松弛程度为250ms。在实现该算法时要求系统中有一个按松弛度排序的实时任务就绪队列,松弛度最低的任务排在最前面,调度程序选择队列中的队首任务执行。
该算法主要用于可抢占调度方式中。假如在一个实时系统中有两个周期性实时任务A和B,任务A要求每20ms执行一次,执行时间为10ms,任务B要求每50ms执行一次,执行时间为25ms。由此可见,任务A和B每次必须完成的时间分别为A1、A2、A3、···和B1、B2、B3、···,如下图。为保证不遗漏任何一次截止时间,应采用最低松弛度优先的抢占调度策略。
在刚开始是(t1=0),A1必须在20ms时完成,而它本身运行又需10ms,可算出A1的松弛度为10ms。B1必须在50ms时完成,而它本身运行就需25ms,可算出B1的松弛度为25ms,故调度程序应先调度A1执行。在t2=10ms时,A2的松弛度可按下式算出:
A2的松弛度=必须完成时间-其本身的运行时间-当前时间
= 40ms - 10ms - 10ms = 20ms
类似地,可算出B1的松弛度为15ms,故调度程序应选择B1运行。在t3=30ms时,A2的松弛度已减为0(即40-10-30),而B1的松弛度为15ms(即50-5-30),于是调度程序应抢占B1的处理机而调度A2运行。在t4=40ms时,A3的松弛度为10ms,而B1的松弛度为5ms,故又重新调度B1执行。在t5=45ms时,B1执行完成,而此时A3的松弛度为5ms,而B2的松弛度为30ms,于是又调度A3执行。在t6=55ms时,任务A尚未进入第4周期,而任务B已进入第2周期,故再调度B2执行。在t7=70ms时,A4的松弛度为0ms,而B2的松弛度为20ms,故此时调度程序又应抢占B2的处理机而调度A4执行。下图示出了具有两个周期性实时任务的调度情况。
4.5 优先级倒置(priority inversion problem)
1优先级倒置的形成
当前OS广泛采用优先级调度算法和抢占方式,然而在系统中存在着影响进程运行的资源而可能产生“优先级倒置”的现象,即高优先级进程(或线程)被低优先级进程(或线程)延迟或阻塞。举个例子,假如有三个完全独立的进程P1、P2和P3,P1的优先级最高,P2次之,P3最低。P1和P3通过共享的一个临界资源进行交互。
假如P3最先执行,在执行了P(mutex)操作后,进入到临界区CS-3。在时刻a,P2就绪,因为它比P3的优先级高,P2抢占了P3的处理机而运行。在时刻b,P1就绪,因为它又比P2的优先级高,P1抢占了P2的处理机而运行。在时刻c,P1执行P(mutex)操作,视图进入临界区CS-1,但因为相应的临界资源已被P3占用,故P1将被阻塞。有P2继续运行,直到时刻d运行结束。然后由P3接着运行,到时刻e时P3退出临界区,并唤醒P1。因为它比P3的优先级高,故它抢占了P3的处理机而运行。
根据优先级原则,高优先级进程应当能优先执行,但在此例中,P1和P3共享着“临界资源”,而出现了不合常理的现象,高优先级进程P1因P3进程被阻塞了,又因为P2进程的存在而延长了P1被阻塞的时间,而且被延长的时间是不可预知和无法限定的。由此所产生的“优先级倒置”的现象是非常有害的,它不应该出现在实时系统中。
2优先级倒置的解决方法
一种简单的解决方法是规定:假如进程P3在进入临界区后P3所占用的处理机就不允许被抢占。由上图可以看出,P2即使优先级高于P3也不能执行。于是P3就有可能会较快地退出临界区,不会出现上述情况。如果系统中的临界资源都较短且不多,该方法是可行的。反之,如果P3临界区非常长,则高优先级进程P1仍会等待很长时间,其效果是无法令人满意的。
一个比较实用的方法是建立在动态优先级继承基础上的。该方法规定,当高优先级进程P1要进入临界区,去使用临界资源R,如果已有一个低优先级进程P3正在使用该资源,此时一方面P1被阻塞,另一方面由P3继承P1的优先级,并一直保持到P3退出临界区。这样做的目的在于不让比P3优先级稍高,但比P1优先级低的进程如P2进程插进来。导致延缓P3退出临界区。如下图示出了采用动态优先级继承方法后,P1、P2、P3三个进程的运行情况。
5.死锁概述
5.1 资源问题
在系统中有许多不同类型的资源,其中可以引起死锁的主要是需要采用互斥访问方法的、不可以被抢占的资源,即在前面介绍的临界资源。系统中这类资源有很多,如打印机、数据文件、队列、信号量等。
1可重用性资源和消耗性资源
(1) 可重用性资源
(2) 可消耗性资源
2可抢占性资源和不可抢占性资源
(1) 可抢占性资源
(2) 不可抢占性资源
5.2 计算机系统中的死锁
死锁的起因,通常是源于多个进程对资源的争夺,不仅对不可抢占资源进行争夺时会引起死锁,而且对可消耗资源进行争夺时,也会引起死锁。
1竞争不可抢占性资源引起死锁
通常系统中所拥有的不可抢占性资源其数量不足以满足多个进程运行的需要,使得进程在运行过程中,会因争夺资源而陷入僵局。例如,系统中有两个进程P1和P2,它们都准备写两个文件F1和F2,而这两者都属于可重用和不可抢占性资源。此时如果在P1打开F1的同时,P2去打开F2,每个进程都占有一个打开的文件,此时就出现死锁问题。
2竞争可消耗资源引起死锁
如下图,P1一方面产生消息m1发给P2,另一方面接收P3发的m3;P2一方面产生消息发给P3,另一方面接收P1发的m1;P3一方面产生消息发给P1,另一方面接收P2发的m2。
如果三个进程按下列顺序进行:
则可以正常运行下去,但如果按下面这种顺序执行:
则这三个进程就永远阻塞在它们的receive操作上,等待一条永远不会发出的消息,于是发生了死锁。
3进程推进顺序不当引起死锁
(1) 进程推进顺序合法
(2) 进程推进顺序非法
5.3 死锁的定义、必要条件和处理方法
1死锁的定义
如果一组进程中的每一个进程都在等待仅由该组进程中的其它进程才能引发的事件,那么该组进程就是死锁的(Deadlock)。
2产生死锁的必要条件
(1) 互斥条件
(2) 请求和保持条件
(3) 不可抢占条件
(4) 循环等待条件
3处理死锁的方法
(1) 预防死锁
(2) 避免死锁
(3) 检测死锁
(4) 解除死锁
6.预防死锁
6.1 破坏“请求和保持”条件
为了能破坏“请求和保持”条件,系统必须保证做到:当一个进程在请求资源时,它不能持有不可抢占资源。该保证可通过如下两个不同的协议实现:
1第一种协议
该协议规定,所有进程在开始运行之前,必须一次性地申请其在整个运行过程中所需的全部资源。
2第二种协议
该协议是对第一种协议的改进,它运行一个进程只获得运行初期所需的资源后,便开始运行。进程运行过程中再逐步释放已分配给自己的、且已用毕的全部资源,然后再请求新的所需资源。
6.2 破坏“不可抢占”条件
为了能破坏“不可抢占”条件,协议中规定,当一个已经保持了某些不可被抢占资源的进程,提出新的资源请求而不能得到满足时,它必须释放已经保持的所有资源,待以后需要时再重新申请。
6.3 破坏“循环等待”条件
一个能保证“循环等待”条件不成立的方法是,对系统所有资源类型进行线性排序,并赋予不同的序号。在对系统所有资源类型进行线性排序后,便可采用这样的预防协议:规定每个进程必须按序号递增的顺序请求资源。
7.避免死锁
7.1 系统安全状态
在死锁避免方法中,把系统的状态分为安全状态和不安全状态。当系统处于安全状态时,可避免发生死锁。反之,当系统处于不安全状态时,则可能进入到死锁状态。
1安全状态
在该方法中,允许进程动态地申请资源,但系统在进行资源分配之前,应先计算此次资源分配的安全性。若此次分配不会导致系统进入不安全状态,才可将资源分配给进程,否则,令进程等待。
2由安全状态向不安全状态的转换
如果不按照安全序列分配资源,则系统可能会由安全状态进入不安全状态。
在建立了系统安全状态的概念后便可知道避免死锁的基本思想,就是确保系统始终处于安全状态。一个系统开始是处于安全状态的,当有进程请求一个可用资源时,系统需对该进程的请求进行计算,若将资源分配给进程后系统仍处于安全状态,才将该资源分配给进程。
7.2 利用银行家算法避免死锁
1银行家算法中的数据结构
为了实现银行家算法,在系统中必须设置这样四个数据结构,分别用来描述系统中可利用的资源、所有进程对资源的最大需求、系统中的资源分配,以及所有进程还需要多少资源的情况。
(1) 可利用资源向量Available。这是一个含有m个元素的数组,其中的每一个元素代表一类可利用的资源数目,其初始值是系统中所配置的该类全部可用资源的数目,其数值随该类资源的分配和回收而动态地改变。如果Available[j]=K,则表示系统中现有Rj类资源K个。
(2) 最大需求矩阵Max。这是一个n×m的矩阵,它定义了系统中n个进程中的每一个进程对m类资源的最大需求。如果Max[i,j]=K,则表示进程i需要Rj类资源的最大数目为K。
(3) 分配矩阵Allocation。这也是一个n×m的矩阵,它定义了系统中每一类资源当前已分配给每一进程的资源数。如果Allocation[i,j]=K,则表示进程i当前已分得Rj类资源的数目为K。
(4) 需求矩阵Need。这也是一个n×m矩阵,用以表示每一个进程尚需的各类资源数。如果Need[i,j]=K,则表示进程i还需要Rj类资源K个方能完成其任务。
上述三个矩阵间存在下述关系:
Need[i,j]=Max[i,j]-Allocation[i,j]
2银行家算法
设Requesti是进程Pi的请求向量,如果Requesti[j]=K,表示进程Pi需要K个Rj类型的资源。当Pi发出资源请求后,系统按下述步骤进行检查:
(1) 如果Requesti[j]≤Need[i,j],便转向步骤(2);否则认为出错,因为它所需要的资源数已超过它所宣布的最大值。
(2) 如果Requesti[j]≤Available[j],便转向步骤(3);否则,表示尚无足够资源,Pi须等待。
(3) 系统试探着把资源分配给进程Pi,并修改数据结构中的数值。
(4) 系统执行安全性算法,检查此次资源分配后系统是否处于安全状态。若安全,才正式将资源分配给进程Pi,以完成本次分配;否则,将本次的试探分配作废,恢复原来的资源分配状态,让进程Pi等待。
3安全性算法
系统所执行的安全性算法可描述如下:
(1) 设置两个向量:①工作向量Work,它表示系统可提供给进程继续运行所需的各类资源数目,它含有m个元素,在执行安全算法开始时,Work=Available;②Finish:它表示系统是否有足够的资源分配给进程,使之运行完成。开始时先做Finish[i]=false;当有足够资源分配给进程时,再令Finish[i]=true。
(2) 从进程集合中找到一个能满足下述条件的进程:
①Finish[i]=false;
②Need[i,j]≤Work[j];
若找到,执行步骤(3),否则,执行步骤(4)。
(3) 当进程Pi获得资源后,可顺利执行,直至完成,并释放出分配给它的资源,故应执行:
Work[j] = Work[j] + Allocation[i,j];
Finish[i] = true;
go to step 2;
(4) 如果所有进程的Finish[i]=true都满足,则表示系统处于安全状态;否则,系统处于不安全状态。
8.死锁的检测与解除
8.1 死锁的检测
为了能对系统中是否已发生了死锁进行检测,在系统中必须:①保存有关资源的请求和分配信息;②提供一种算法,它利用这些信息来检测系统是否已进入死锁状态。
1资源分配图(Resource Allocation Gragh)
系统死锁,可利用资源分配图来描述。该图是由一组结点和一组边E所组成的一个对偶G=(N, E)。
图中,P1进程已经分得了两个R1资源,并又请求一个R2资源;P2进程分得一个R1和一个R2资源,并又请求R1资源。
2死锁定理
我们可以利用把资源分配图加以简化的方法,来检测当系统处于S状态时,是否为死锁状态。简化方法如下:
(1) 在资源分配图中,找出一个既不阻塞又非独立的进程结点Pi。在顺利的情况下,Pi可获得所需资源而继续运行,直至运行完毕,再释放其所占有的全部资源,这相当于消去Pi的请求边和分配边,使之成为孤立的结点。如下图(a)中,将P1的两个分配边和一个请求边消去,边形成图(b)所示的情况。
(2) P1释放资源后,便可使P2获得资源而继续运行,直至p2完成后又释放出它所占有的全部资源,形成图©所示的情况,即将P2的两条分配和一条请求边消去。
(3) 在进行一系列的简化后,若能消去图中所有的边,使所有的进程结点都称为孤立结点,则称该图是可完全简化的;若不能通过任何过程使该图完全简化,则称该图是不可完全简化的。
S为死锁状态的充分条件是:当且仅当S状态的资源分配图是不可完全简化的。该充分条件被称为死锁定理。
3死锁检测中的数据结构
死锁检测中的数据结构类似于银行家算法中的数据结构:
(1) 可利用资源向量Available,它表示了m类资源中每一类资源的可用数目。
(2) 把不占用资源的进程(向量Allocation=0)记入L表中。
(3) 从进程集合中找到一个Requesti≤Work的进程做如下处理:①将其资源分配图简化,释放出资源,增加工作向量Work=Work+Allocationi。②将它记入L表中。
(4) 若不能把所有进程都记入L表中,便表明系统状态S的资源分配图是不可完全简化的。因此,该系统状态将发生死锁。
Work=Available;
L={Li|Allocationi=0⋂Requesti=0}
for(all Li∉ L) {
for (all Requesti≤Work) {
Work=Work+Allocationi;
Li⋃L;
}
}
deadlock=(L={P1, P2,···,Pn});
8.2 死锁的解除
常用的解除死锁的两种方法是:
(1) 抢占资源。从一个或多个进程中抢占足够数量的资源,分配给死锁进程,以接触死锁。
(2) 终止(或撤销)进程。终止(或撤销)系统中的一个或多个死锁进程,直至打破循环环路,使系统从死锁状态解脱出来。
1终止进程的方法
(1) 终止所有死锁进程
(2) 逐个终止进程
2付出代价最小的死锁解除算法
一个比较有效的方法是对死锁状态S做如下处理:从死锁状态S中先终止一个死锁进程P1,使系统状态由S演变成U1,将P1记入被终止进程的集合d(T)中,并把所付出的代价C1加入到Rc(T)中;对死锁进程P2、P3等重复上述过程,得到状态U1,U2,···,Ui,Un后,再按终止进程时所花费代价的大小,把它插入到由S状态所演变的新状态的队列L中。显然,队列L中的第一个状态U1是由S状态花最小代价终止一个进程所演变成的状态。在终止一个进程后,若系统仍处于死锁状态,则再从U1状态按照上述处理方式再依次地终止一个进程。这样,为把系统从死锁状态中解脱出来,所花费的代价可表示为:R(S)min = min{Cui} + min{Cuj} + min{Cuk} + ··· 。