操作系统 死锁预防
如果我们用四条腿支撑的桌子来模拟死锁,那么我们也可以用同时发生时导致死锁的四个条件来模拟四条腿。
然而,如果我们打破桌子的一条腿,那么桌子肯定会倒下。同样,如果我们能够违反四个必要条件中的一个,并阻止它们同时发生,那么我们就可以预防死锁。
让我们看看如何预防每个条件。
1. 互斥
从资源的角度来看,互斥是指一个资源不能被多个进程同时使用,这是公平的,但这也是造成死锁的主要原因。如果一个资源可以被多个进程同时使用,那么进程就不需要等待任何资源。
然而,如果我们能够违反资源以互斥方式行为的规则,就可以预防死锁。
输入输出缓冲
对于像打印机这样的设备,可以使用输入输出缓冲。打印机有一个与之关联的内存,将每个进程的作业存储其中。稍后,打印机收集所有作业,并按照先来先服务的顺序打印每一个作业。通过使用这种机制,进程不需要等待打印机,可以继续进行其他工作。等到产生输出时,再收集输出。
尽管Spooling可以是实施违反互斥性的一种有效方法,但它存在两种问题。
- 这不适用于每个资源。
- 在某个时间点后,进程之间可能会发生竞争条件,以获取spool中的空间。
我们不能强制一个资源被多个进程同时使用,因为这样不够公平,并且在性能上可能会出现一些严重的问题。因此,从实际上来说我们不能违反进程的互斥性。
2. 持有并等待
当一个进程持有资源并等待其他资源完成其任务时,会出现持有并等待的条件。发生死锁是因为可能有多个进程持有一个资源,并在循环顺序中等待其他资源。
但是,我们必须找到某种机制,使得一个进程要么不持有任何资源,要么不等待。也就是说,在执行开始之前,必须为一个进程分配所有必要的资源。执行开始后,一个进程不能等待任何资源。
!(持有并等待) = !持有 或者 !等待(持有并等待的否定是,要么你不持有,要么你不等待)
如果一个进程最初声明了所有的资源,这是可以实际实现的。然而,这在计算机系统中无法做到,因为进程不能初始确定必要的资源。
进程是由CPU执行的一组指令。每个指令可能多次需求多个资源。操作系统无法固定需求。
该方法存在的问题是:
- 实际上不可能。
- 由于某个进程可能长时间持有一个资源,导致饥饿的可能性增加。
3. 无剥夺
死锁是因为一旦进程启动就无法停止。然而,如果我们从导致死锁的进程中取走资源,则可以防止死锁。
这绝对不是一种好方法,因为如果我们取走了进程正在使用的资源,那么到目前为止完成的所有工作都可能变得不一致。
考虑一个进程正在使用打印机。如果我们将打印机从该进程中取走,并分配给其他进程,则已打印的所有数据可能变得不一致和无效,并且无法从离开时重新开始打印,这会导致性能低下。
4. 循环等待
为了违反循环等待,我们可以为每个资源分配一个优先级号。一个进程不能请求一个优先级较低的资源。这样确保没有任何一个进程可以请求正在被其他进程使用的资源,也不会形成循环。
在所有的方法中,违反循环等待是唯一一种能够实际实施的方法。