第三章 处理机的调度与死锁
第三章 处理机的调度与死锁
处理机调度的层次和调度算法的目标
调度的基本概念
在多道程序系统中,调度的实质是一种资源分配,处理机调度是对处理机资源进行分配。处理机调度算法是指根据处理机分配策略所规定的处理机分配算法。
在多道程序系统中,进程的数量远远多于处理机的个数,因此进程争用处理机的情况在所难免。处理机调度是对处理机进行分配,即从就绪队列中按照一定的算法(公平、高效)选择一个进程并将处理机分配给它运行,以实现进程的并发执行。
处理机调度的层次
高级调度
高级调度又称长程调度或作业调度,它调度的对象是作业。其主要功能是根据某种算法,决定将外存上处于后备队列中的哪几个作业调入内存,为它们创建进程、分配必要的资源,并将其放入就绪队列。
高级调度主要用于多道批处理系统中,在分时系统和实时系统中不设置高级调度。高级调度的执行频率较低,通常为几分钟一次。
低级调度
低级调度又称为进程调度或短程调度,其所调度的对象是进程或内核级线程。其主要功能是根据某种算法决定就绪队列中哪个进程应获得处理机,并由分派程序将处理机分配给被选中的进程。
进程调度是最基本的一种调度,在多道批处理、分时系统和实时系统三种类型的OS中都必须设置这种调度。进程调度的频率很高,一般几十毫秒一次。
中级调度
中级调度又称内存调度。引入中级调度的目的主要是提高内存的吞吐率和系统吞吐量。为此应把那些暂时不能运行的进程调至外存等待,此时进程的状态称为就绪驻外存状态(或挂起状态)。当它们已具备运行条件且内存又稍有空闲时,由中级调度来决定把外存上那些已具备运行条件的就绪进程再重新调入内存,并修改其状态为就绪状态,挂在就绪队列上等待。中级调度实际上就是存储器管理中的对换功能。
三级调度的联系
- 高级调度为进程活动做准备,低级调度使进程正常活动起来,中级调度将暂时不能运行的进程挂起,中级调度处于高级调度和低级调度之间。
- 高级调度的次数较少,中级调度的次数略多,低级调度的频率最高。
- 低级调度是最基本的,不可或缺。
处理机调度算法的目标
处理机调度算法的共同目标
- 资源利用率。为提高系统的资源利用率,应使系统中的处理机和其它所有资源都尽可能地保持忙碌状态,其中最重要的处理机利用率的计算公式为:CPU的利用率 = CPU有效工作时间 / (CPU有效工作时间 + CPU空闲等待时间)。
- 公平性。公平性是指应使诸进程都获得合理的CPU时间,不会发生进程饥饿现象。公平性是相对的,对相同类型的进程应获得相同的服务;对于不同类型的进程,由于其紧急程度或重要性不同,则应提供不同的服务。
- 平衡性。由于系统中可能存在多种不同类型的进程(CPU密集型和I/O密集型),为使系统中的CPU和外设都能经常处于忙碌状态,调度算法应尽可能保持系统资源使用的平衡性。
- 策略强制执行。对所制定的策略(如安全策略等),只要需要,就必须予以准确地执行。
批处理系统的目标
- 平均周转周期短。周转时间是指:作业从被提交给系统开始,到作业完成为止的这段时间间隔,计算公式为:周转时间 = 作业完成时间 - 作业提交时间。平均周转时间是指多个作业周转时间的平均值,计算公式为:平均周转时间 = (作业1的周转时间 + 作业2的周转时间 + ... + 作业n的周转时间)。带权周转时间是指作业周转时间与作业实际运行时间的比值,计算公式为:带权周转时间 = 作业周转时间 / 作业实际运行时间。平均带权周转时间是指多个作业的带权周转时间的平均值,计算公式为:平均带权周转时间 = (作业1的带权周转时间 + ... + 作业n的带权周转时间) / n。作业的周转时间包括四部分:1. 作业在外存后备队列上等待作业调度的时间;2. 进程在就绪队列上等待进程调度的时间;3. 进程在CPU上执行的时间;4. 进程等待I/O操作完成的时间。
- 系统吞吐量高。由于吞吐量是指单位时间内系统所完成的作业数,所以它与批处理作业的平均长度有关。若要提高系统吞吐量,应该更多地选择短作业运行。
- 处理机利用率高。处理机的利用率是衡量系统性能的十分重要的指标,而调度方式又对处理机的利用率起着十分重要的作用。若要提高处理机利用率,应该尽量多地选择计算量大的作业运行。
分时系统的目标
- 响应时间快。响应时间快是选择分时系统中进程调度算法的重要准则。所谓响应时间是指用户通过键盘提交一个请求开始,直到屏幕上显示出处理结果为止的一段时间间隔,有三部分组成:1. 请求信息从键盘输入开始,直至将其传送至处理机的时间;2. 处理机对请求信息进行处理的时间;3. 将所形成的响应信息回送到终端显示器的时间。
- 均衡性。所谓均衡性,是指系统响应时间的快慢应与用户所请求服务的复杂性相适应。
实时系统的目标
- 截止时间的保证。所谓截止时间,是指某任务必须开始执行的最迟时间,或必须完成的最迟时间。对于实时系统而言,调度算法的一个主要目标是保证实时任务对截止时间的要求。
- 可预测性。在实时系统中,可预测性非常重要。
作业与作业调度
批处理系统中的作业
作业和作业步
- 作业:作业是一个比程序更广泛的概念,它不仅包含了程序和数据,而且还应配有一份作业说明书,系统根据说明书对程序的运行进行控制。在批处理系统中,是以作业为基本单位从外存调入内存的。
- 作业步:通常,每个作业都必须经过若干个相互独立又相互关联的顺序加工步骤才能得到结果,每一个加工步骤称为作业步。各作业步之间相互联系,往往是上一个作业步的输出作为下一个作业步的输入。
作业控制块JCB
为了管理和调度作业,在多道批处理系统中,为每个作业设置一个作业控制块JCB,它是作业在系统中存在的标志,其中保存了系统对作业进行管理和调度所需的全部信息,例如作业标识、用户名称、用户账号、作业类型(CPU繁忙型、I/O繁忙型、批量型、终端型)、作业状态、调度信息(优先级、作业运行时间)、资源需求(预计运行时间、要求内存大小等)、资源使用情况等。
每当一个作业进入系统时,由“作业注册”程序为该作业建立一个JCB,再根据作业类型放到相应的后备队列中等待调度。调度程序根据一定的调度算法来调度它们,被调度的作业将被装入内存。在作业运行期间,系统根据JCB中的信息和作业说明书对作业进行控制。当一个作业执行完毕后,系统负责回收分配给它的资源,并撤销该JCB。
作业运行的三个阶段和三种状态
- 收容阶段:操作员把作业输入到硬盘上,再为该作业建立JCB,并把它放到后备队列中。此时作业的状态为后备状态。
- 运行阶段:作业被作业调度程序选中后,便为它分配必要的资源和建立进程,并将它放入就绪队列。一个作业从第一次进入就绪状态开始,直到运行结束前,在此期间都处于运行状态。
- 完成阶段:当作业运行完成或异常终止时,作业进入完成阶段,此时作业的状态为完成状态。此时系统中的“终止作业”程序会回收已分配给该作业的JCB和所有资源,并将作业运行结果信息形成输出文件后输出。
作业调度的主要任务
作业调度的主要任务是根据JCB中的信息,检查系统中的资源是否满足作业对资源的需求,以及按照一定的调度算法,从外存的后备队列中选取某些作业调入内存,并为它们创建进程、分配必要的资源,然后将新创建的进程排在就绪队列上等待调度。因此也把作业调度称为“接纳调度”。在每次执行作业调度时,都需要决定接纳多少个作业和接纳哪些作业。
进程调度
调度的时机、切换与过程
进程调度和切换的程序是操作系统的内核程序。请求调度的事件发生后,才可能运行进程调度程序,调度了新的就绪进程后,才会进行进程间的切换。在实际设计中,操作系统的内核程序运行时,若发生了引起进程调度的因素,也不一定能够马上进行进程调度与切换。
不能进行进程调度与切换的原因有:
- 在处理中断的过程中:中断的处理过程复杂,在实现上很难做到进程切换。中断处理是系统工作的一部分,逻辑上不属于某一进程。
- 进程在操作系统内核程序临界区中:进程进入临界区后需要独占式地访问共享数据,理论上必须加锁以防其他并行程序进入。在解锁前不应切换到其他进程运行,以加快共享数据的释放。
- 其他需要完全屏蔽中断的原子操作过程中:如加锁、解锁、中断现场保护、回复等原子操作。
若上述过程中发生了引起调度的条件,不能马上进行进程的调度与切换,应置系统的请求调度标志,直到上述过程结束后再进行相应的调度与切换。
应该进行进程的调度与切换的情况如下:
- 发生引起调度条件且当前进程无法继续执行时:此时可以马上进行进程的调度与切换。若操作系统只在这种情况下进行进程调度,则是非剥夺调度。
- 中断处理结束或自陷处理结束后,返回被中断的进程的用户态程序执行现场前:此时若有请求调度标志则可以马上进行进程的调度与切换。若操作系统支持这种情况下的运行调度程序,则实现了剥夺方式的调度。
进程切换往往在调度完成后立刻发生。
进程调度方式
进程调度方式是指某个进程正在处理机上执行时,若有某个更为重要或紧迫的进程需要处理,即有更高优先权的进程进入就绪队列,此时应该如何分配处理机。
进程调度通常有非剥夺调度方式和剥夺调度方式两种方式。
非剥夺调度方式
又称非抢占方式,是指当一个进程正在处理机上执行时,即使有某个更为重要或紧迫的进程进入就绪队列,仍然让正在执行的进程继续执行,直到该进程完成或发生某种事件而进入阻塞状态时,才把处理机分配给更为重要或紧迫的进程。在非剥夺调度方式下,一旦把CPU分配给一个进程,该进程就会保持CPU直到终止或转换到等待态。
非剥夺调度方式适合大多数的批处理系统,但不能用于分时系统和大多数的实时系统。
剥夺调度方式
又称抢占方式,是指当一个进程在处理机上执行时,若有某个更为重要或紧迫的进程需要使用处理机,则立即暂停正在执行的进程,将处理机分配给这个更为重要或紧迫的进程。
采用剥夺调度方式,对提高系统吞吐率和响应效率都有明显的好处,但“剥夺”必须遵循一定的原则,主要有优先权、短进程优先、时间片原则等。
调度的基本准则
- CPU利用率:应尽可能使CPU处于“忙”状态,使这一资源利用率最高。
- 系统吞吐量:表示单位时间内CPU完成作业的数量。
- 周转时间:包括单个作业的周转时间、平均周转时间、带权周转时间、平均带权周转时间等。
- 等待时间:等待时间指进程处于等待处理机状态的时间之和。
- 响应时间:响应时间指从用户提交到系统首次产生响应所用的时间。
典型的调度算法
先来先服务(FCFS)
FCFS调度算法既可以用于作业调度,又可以用于进程调度。在作业调度中, 算法每次从后备队列中选择最先进入该队列的一个或几个作业,将它们调入内存,分配必要的资源,创建进程并放入就绪队列。
在进程调度中,FCFS调度算法每次从就绪队列中选择最先进入该队列的进程,将处理机分配给它,直到进程完成或因某种原因而阻塞才释放处理机。
FCFS调度算法属于不可剥夺算法。若一个长作业先到达系统,就会使后面许多短作业等待很长时间,因此它不能作为分时系统和实时系统的主要调度策略。但它常被结合在其它调度策略中使用,如在使用优先级作为调度策略的系统中,往往对多个具有相同优先级的进程按FCFS处理。
特点:
- 算法简单,但效率低;
- 对长作业有利,对短作业不利;
- 有利于CPU繁忙型作业,不利于I/O繁忙型作业。
短作业优先(SJF)、短进程优先(SPF)
短作业优先调度算法是对短作业优先调度的算法。短作业优先算法从后备队列中选择一个或若干估计运行时间最短的进程,将它们调入内存执行;短进程优先算法从就绪队列中选择一个估计运行时间最短的进程,将处理机分配给它,直到完成或发生某事件而阻塞时,才释放处理机。
特点:
- 对长作业不利:SJF调度算法中长作业的周转时间会增加,长作业可能长期得不到调度(“饥饿”现象);
- 完全未考虑作业的紧迫程度:SJF调度算法不能保证紧迫性作业会被及时处理;
- 不一定真正做到短作业优先:作业的长短是根据用户所提供的估计执行时间而定的,用户有可能有意或无意地缩短其作业的估计运行时间,致使该算法不一定真正做到短作业优先;
- 平均等待时间、平均周转时间最小。
优先级调度算法
优先级调度算法又可称为优先权调度算法,既可以用于作业调度,又可以用于进程调度。该算法中的优先级用于描述作业的紧迫程度。在作业调度中,优先级调度算法每次从后备作业队列中选择优先级最高的一个或几个作业,将它们调入内存,分配必要的资源,创建进程并放入就绪队列。在进程调度中,优先级调度算法每次从就绪队列中选择优先级最高的进程,将处理机分配给它。
该算法按照更高优先级的进程是否抢占正在执行的进程可分为两种:
- 非剥夺式优先级调度算法:当一个进程在处理机上运行时,即使有某个更为重要或紧迫的进程进入就绪队列,仍然让正在运行的进程继续运行,直到由于其自身的原因而主动让出处理机时(任务完成或等待事件),才把处理机分配给更为重要或紧迫的进程。
- 剥夺式优先级调度算法:当一个进程正在处理机上运行时,若有某个更为重要或紧迫的进程进入就绪队列,则立即暂停正在运行的进程,将处理机分配给更为重要或紧迫的进程。
根据进程的优先级是否改变,可将进程的优先级分为以下两种:
- 静态优先级:优先级是在创建进程时确定的,且在进程的整个运行期间保持不变。确定静态优先级主要依据是进程类型、进程对资源的要求、用户要求。
- 动态优先级:在进程运行过程中,根据进程情况的变化动态调整优先级。动态调整优先级的主要依据有进程占有CPU时间的长短、就绪进程等待CPU时间的长短。
一般来说,进程优先级的设置可以参照以下原则:
- 系统进程 > 用户进程;
- 交互型进程 > 非交互型进程(前台进程 > 后台进程);
- I/O型进程 > 计算型进程。
高相应比优先调度算法
高响应比优先调度算法主要用于作业调度,是对FCFS调度算法和SJF调度算法的一种综合平衡,同时考虑了每个作业的等待事件和估计的运行时间。在每次进行作业调度时,先计算后备队列中每个作业的响应比,从中选出响应比最高的作业投入运行。
响应比的计算公式为:响应比Rp = (等待时间 + 要求服务时间)/ 要求服务时间
特征:
- 作业的等待时间相同时,要求服务时间越短,响应比越高,有利于短作业。
- 要求服务时间相同时,作业的响应比由其等待时间决定,等待时间越长,响应比越高,因而它实现的是先来先服务。
- 对于长作业,作业的响应比可随等待时间的增加而增长,等待时间足够长时,其响应比可以升到很高。因此,该调度算法解决了SJF调度算法中长作业的“饥饿”问题。
时间片轮转调度算法
时间片轮转调度算法主要用于分时系统。系统将所有就绪进程按到达时间的先后次序排成一个队列,进程调度程序总是选择就绪队列中的第一个进程执行,即先来先服务原则,但仅能运行一个时间片(如100ms)。在使用完一个时间片后,即使进程未完成其运行,它也必须释放处理机给下一个就绪的进程,而被剥夺处理机的进程返回就绪队列的末尾重新排队,等候再次运行。
时间片的大小对性能影响很大。若时间片足够大以至于所有进程都能在一个时间片内执行完毕,则该算法就退化为先来先服务调度算法;若时间片很小,则处理机将在进程间过于频繁地切换,使处理机的开销增大,而真正用于运行用户进程的时间将减少。
时间片的长短通常由以下因素确定:
- 系统的响应时间;
- 就绪队列中的进程数;
- 系统的处理能力。
多级反馈队列调度算法(融合了前几种算法的优点)
多级反馈队列调度算法是时间片轮转调度算法和优先级调度算法的综合与发展。多级反馈队列调度算法可以兼顾多方面的系统目标,如为提高系统吞吐量和缩短平均周转时间而照顾短进程;为获得较好的I/O设备利用率和缩短响应时间而照顾I/O型进程;不必事先估计进程的执行时间。
该算法的实现思想如下:
- 设置多个优先级不同的就绪队列,其中第一级队列的优先级最高,第二级队列次之,其余队列的优先级逐次降低。
- 赋予各个队列中进程执行的时间片大小各不相同。在优先级越高的队列中,每个进程的运行时间片越小。例如,第二级队列的时间片要比第一级队列的时间片长1倍,第i + 1级队列的时间片要比第i级队列的时间片长1倍。
- 一个新进程进入内存后,首先将它放入第1级队列的末尾,按FCFS原则排队等待调度。当轮到该进程执行时,如它能在该时间片内执行完成,便可准备撤离系统;若未执行完成,调度程序便将该进程转入第二级队列的末尾,再同样按照FCFS原则等待调度。若它在第二级队列中运行一个时间片后仍未完成,则再以同样的方式放入第三级队列。以此类推,当一个长进程从第一级队列依次降级到第n级队列后,在第n级队列中采用时间片轮转的方式运行。
- 仅当第1~(i - 1)级队列为空时,调度程序才调度第i级队列中的进程运行;若处理机正在执行第i级队列中的进程,这时又有新的进程进入优先级较高的队列,则此时新进程将抢占正在运行的进程的处理机,即由调度程序把正在运行的进程放回第i级队列的队尾,把处理机分配给新到的优先级更高的进程。
优点:
- 终端型作业用户:短作业优先;
- 短批处理作业用户:周转时间短;
- 长批处理作业用户:经过前面几个队列得到部分执行,不会长期得不到处理。
死锁
死锁的概念
死锁是指多个进程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。
死锁产生的原因
- 系统资源的竞争:系统中拥有的不可剥夺资源(如打印机、磁带机等)因其数量不足以满足多个进程运行的需要,使得进程在运行过程中会因争夺资源而陷入僵局。注:对可剥夺的资源的竞争不会产生死锁。
- 进程推进顺序非法:进程在运行过程中,请求和释放资源的顺序不当也同样会导致死锁。信号量使用不当也会造成死锁(进程因等待对方的资源而产生死锁)。
死锁产生的四个必要条件
- 互斥条件:进程要求对所分配的资源(如打印机)进行排他性控制,即在一段时间内某资源仅为一个进程占有。此时若有其他进程请求该资源,则请求资源的进程只能等待。
- 不剥夺条件:进程所获得的资源在未使用完之前不能被剥夺,即资源只能由获得该资源的进程自己来释放(只能是主动释放)。
- 请求并保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已经被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
- 循环等待条件:存在一种进程资源的循环等待链,链中的每个进程已获得的资源同时被链中下一个进程所请求,即存在一个处于等待态的进程集合{P1, P2, ..., Pn},其中Pi等待的资源被Pi+1(i = 0, 1, ..., n - 1)占有,Pn等待的资源被P0所占有。注:资源分配图含圈而系统又不一定有死锁的原因是同类资源数大于1。但若系统中每类资源都只有一个,则资源分配图含圈就变成了系统含有死锁的充分必要条件。
死锁的处理策略
为使系统不发生死锁,必须破坏死锁的四个必要条件之一,或允许死锁产生,但当死锁发生时能检测出死锁,并有能力实现恢复。
死锁预防
设置某些限制条件,破坏产生死锁的四个必要条件中的一个或几个,以防止发生死锁。
避免死锁
在资源的动态分配过程中,用某种方法防止系统进入不安全状态,从而避免死锁。
死锁的检测及解除
无需采取任何限制性措施,允许进程在运行的过程中发生死锁,通过系统的检测机构及时地检测出死锁的发生,然后采取某种措施解除死锁。
死锁的预防和避免都属于事先预防策略,预防死锁的限制条件比较严格,实现起来较为简单,但往往导致系统的效率低,资源利用率低;避免死锁的限制条件比较宽松,资源分配后需要通过算法来判断是否进入不安全状态,实现起来比较复杂。
死锁的几种处理策略如下:
资源分配策略 | 各种可能模式 | 主要优点 | 主要缺点 | |
---|---|---|---|---|
死锁预防 | 保守,宁可资源闲置 | 一次请求所有资源,资源剥夺,资源按序分配 | 适用于突发式处理的进程,不必进行剥夺 | 效率低,进程初始化时间延长;剥夺次数过多,不便灵活申请资源 |
死锁避免 | 是“预防”和“检测”的折中(在运行时判断是否可能死锁) | 寻找可能的安全允许顺序 | 不必进行剥夺 | 必须知道将来的资源需求;进程不能被长时间阻塞 |
死锁检测 | 宽松,只要允许就分配资源 | 定期检查死锁是否已经发生 | 不延长进程初始化时间,允许对死锁进行现场处理 | 通过剥夺解除死锁,造成损失 |
死锁预防
防止死锁的发生只需要破坏死锁产生的4个必要条件的其中之一即可。
破坏互斥条件
特点:把只能互斥使用的资源改造成允许共享使用,则系统就不会进入死锁状态。如操作系统使用SPOOLing技术把独占设备在逻辑上改造成共享设备。
缺点:并不是所有的资源都可以改造成可以共享使用的资源,并且为了系统安全,很多地方还必须保持这种互斥性。因此,很多时候都无法破坏这种互斥条件。
破坏不剥夺条件
特点:
- 方案一:当一个已保持了某些不可剥夺资源的进程请求新的资源而得不到满足时,它必须释放已经持有的全部资源,待以后需要时再重新申请。也就是说,一个进程已占有的资源会被暂时释放,或者说是被剥夺,从而破坏了互斥条件。
- 方案二:当某个进程需要的资源被其他进程所占有的时候,可以由操作系统协助,将想要的资源强行剥夺。这种方案一般需要考虑进程的优先级。
缺点:
- 实现起来比较复杂;
- 释放已获得的资源可能会造成前一阶段工作的失效,因此这种方法一般只适用于状态易于保存和恢复的资源,如CPU;
- 反复地申请和释放资源会增加系统的开销,降低系统的吞吐量;
- 若采用方案一,意味着只要暂时得不到某个资源,之前获得的那些资源就必须放弃,以后再重新申请。如果这种情况一直发生,就可能导致进程饥饿。
破坏请求并保持条件
特点:可采用静态分配方法,即进程运行之前一次申请完它所需要的全部资源,在它的资源尚未得到满足前,不把它投入运行。一旦投入运行,这些资源就一直归它所有,不再提出申请其他资源的请求。这种方法实现起来简单。
缺点:有些资源可能只需要使用很短的时间,因此如果进程的整个运行期间都一直保持着所有资源,就会造成严重的资源浪费,资源利用率极低。另外,该策略也有可能导致某些进程饥饿。
破坏循环等待条件
特点:采用顺序资源分配法,首先给系统中的资源编号,规定每个进程都必须按照编号递增的顺序请求资源,同类资源一次申请完。也就是说,只要进程提出申请分配资源Ri,则该进程在以后的资源申请中就只能申请编号大于Ri的资源(一个进程只有已占有小编号的资源时,才有资格申请更大编号的资源)。按此规则,已持有更大编号资源的进程不可能逆向地申请小编号的资源,从而就不会产生循环等待现象。
缺点:
- 不方便增加新类型的设备,因为可能需要重新分配所有的编号;
- 进程实际使用资源的顺序可能和编号递增顺序不一致,会导致资源的浪费;
- 必须按照规定次序申请资源,必然会给用户的编程带来麻烦。
死锁避免
系统安全状态
所谓安全序列,就是指如果系统按照这种序列分配资源,则每个进程都能顺利完成。只要找出一个安全序列,系统就是安全状态。安全序列可能有多个。
如果分配了资源之后,系统找不到安全序列,则系统就进入了不安全状态。这就意味着之后可能所有进程都无法顺利执行下去。当然,如果有进程提前归还了一些资源,那系统也有可能提前回到安全状态。
如果系统处于安全状态,那么就一定不会发生死锁;如果系统进入不安全状态,就可能发生死锁(处于不安全状态未必就发生了死锁,但发生死锁时系统一定处于不安全状态)。因此可以在资源分配前预先判断这次分配是否会导致系统进入不安全状态,以此决定是否答应资源分配请求。
银行家算法
设Requesti是进程Pi的请求向量,Requesti[j] = K表示进程Pi需要j类资源K个。当Pi发出资源请求后,系统按照如下步骤进行检查:
- 若Requesti[j] <= Need[i, j],则转向步骤2,否则认为出错,因为它所需要的资源数已超过它所宣布的最大值。
- 若Requesti[j] <= Available[j],则转向步骤3,否则表示尚无足够资源,Pi需等待;
- 系统试探着把资源分配给进程Pi,并修改下面数据结构中的数值:
Available = Available - Request; Allocation[i, j] = Allocation[i, j] + Requesti[j]; Need[i, j] = Need[i, j] - Requesti[j]
; - 系统执行安全性算法,检查此次资源分配后系统是否处于安全状态。若安全,才正式将资源分配给Pi;若不安全,则将本次的试探分配作废,恢复原来的资源分配状态,让进程Pi等待。
安全性算法描述如下:
- 初始时安全序列为空;
- 从Need矩阵中找出符合下面条件的行:该行对应的进程不在安全序列中,而且该行小于等于Work向量。找到后,把对应的进程加入安全序列;若找不到,则执行步骤4;
- 进程Pi进入安全序列后,可顺利执行,直至完成,并释放分配给它的资源,因此执行
Work = Work + Allocation[i]
,其中Allocation[i]表示进程Pi代表的在Allocation矩阵中对应的行,返回步骤2; - 若此时安全序列中已经有所有的进程,则系统处于安全状态,否则系统处于不安全状态。
死锁的检测与解除
若系统在为进程分配资源时不采取任何措施,则应该提供死锁检测与解除的手段。
资源分配图
系统死锁可用资源分配图描述。如下图所示,圆圈代表一个进程,框代表一类资源,由于一种资源可能有多个,所以用框中的一个圆代表一类资源中的一个资源。从进程到资源的有向边称为请求边,表示该进程申请一个单位的该类资源;从资源到进程的边称为分配边,表示该类资源已经有一个资源分配给了该进程。
image死锁定理
通过简化资源分配图可以检测系统状态S是否为死锁状态。简化方法如下:
- 在资源分配图中找出既不阻塞又不是孤点的进程Pi(即找出一条有向边与它相连,且该有向边对应资源的申请数量小于等于系统中已有的空闲资源数量。若所有连接该进程的边均满足上述条件,则这个进程能继续运行直至完成,然后释放它所占有的全部资源)。消去它所有的请求边和分配边,使之成为孤立的结点。
- 进程Pi所释放的资源可以唤醒某些因等待这些资源而阻塞的进程,原来的阻塞进程可能变为非阻塞进程。根据1中的方法进行一系列简化后,若能消去图中所有的边,则称该图是可完全简化的。S为死锁的条件是当且仅当S状态的资源分配图是不可完全简化的,该条件称为死锁定理。
死锁的解除
- 资源剥夺法:挂起某些死锁进程,并抢占它们的资源,将这些资源分配给其他的死锁进程,但应防止被挂起的进程长时间得不到资源而处于饥饿状态;
- 撤销进程法:强制撤销部分甚至全部死锁进程并剥夺它们的资源。撤销的原则可以按进程优先级和撤销进程代价的高低进行;
- 进程回退法:让一个或多个进程回退到足以避免死锁的地步,进程回退时自愿释放资源而非被剥夺。这就要求系统记录进程信息,并设置还原点。