死锁与饥饿
产生死锁的原因主要有两个,一是竞争资源,系统提供的资源数量有限,不能满足每个进程的需求;二是多道程序运行时,进程推进顺序不合理。由此可见,发生死锁时死锁进程的个数至少是两个。我们可以举一个最简单的例子来了解一下死锁:
P1 |
P2 |
... |
... |
Request(A) |
Request(B) |
Request(B) |
Request(A) |
Request(B) |
Request(A) |
Request(A) |
Request(B) |
假如双方都拥有部分资源(P1拥有A,P2拥有B,且A,B均只有一个),但这时P1还需要B,P2还需要A,于是P1与P2都会处在无限等待状态,发生了死锁。
由这个例子,我们可以归纳分析出产生死锁的必要条件:
(1)
资源是独占的且排他使用。即任意时刻一个资源只能给一个进程使用,其他申请者只有等待,直到资源被占有者释放。如例子中的A,B资源。
(2)
进程所获得的资源在未使用完毕之前,不能被其他进程强行剥夺,而只能由拥有该资源的进程自愿释放。如例子中P2 不能强占P1拥有的A资源,而P1也不能强占P2拥有的B资源。
(3)
进程每次申请他所需要的一部分资源,在申请新的资源的同时,继续占用已分配到的资源。如例子中P1申请B资源时继续占有A资源,P2申请A资源时继续占有B资源。
(4)
在发生死锁时,必然存在一个进程等待环路,环路中的每一个进程已占有的资源同时被另一个进程所申请。如例子中的P1和P2就是一个简单的等待环路。
系统发生死锁不仅浪费了大量的系统资源,甚至会导致整个系统的崩溃,因此如何解决死锁问题是操作系统设计中的一个重点。目前主要有两类方法,一类是不让死锁发生;另一类是让死锁发生,再加以解决。
死 锁预防就是力图不让死锁发生,它采用破坏“不可剥夺”条件,或破坏“请求保持”条件,或破坏“循环等待”条件来达到目的,但这种方法给系统加上了较强的限 制条件,严重的影响了系统性能。死锁避免则是不破坏死锁的必要条件,而是系统对进程发出的每一个系统能够满足的资源申请进行动态检验,并根据检验结果决定 是否分配资源,如果分配后系统可能发生死锁,则不分配,否则分配。
这种方法算法复杂,会消耗很多的系统时间。
由于饥饿和饿死与资源分配策略有关,因而解决饥饿与饿死问题可从资源分配策略的公平性考虑,确保所有进程不被忽视。如时间片轮转算法(RR)。它将CPU的处理时间分成一个个时间片,就绪队列中的诸进程轮流运行一个时间片,当时间片结束时,就强迫运行程序让出CPU,该进程进入就绪队列,等待下一次调度。同时,进程调度又去选择就绪队列中的一个进程,分配给它一个时间片,以投入运行。如此方式轮流调度。这样就可以在不考虑其他系统开销的情况下解决饥饿的问题。
最后,我们来比较的看一下死锁与饥饿。
死锁与饿死有一定相同点:二者都是由于竞争资源而引起的。但又有明显差别:
(1) 从进程状态考虑,死锁进程都处于等待状态,忙式等待(处于运行或就绪状态)的进程并非处于等待状态,但却可能被饿死;
(2) 死锁进程等待永远不会被释放的资源,饿死进程等待会被释放但却不会分配给自己的资源,表现为等待时限没有上界(排队等待或忙式等待);
(3) 死锁一定发生了循环等待,而饿死则不然。这也表明通过资源分配图可以检测死锁存在与否,但却不能检测是否有进程饿死;
(4) 死锁一定涉及多个进程,而饥饿或被饿死的进程可能只有一个。