在计算机系统中有很多独占性的资源,在任一时刻它们都只能被一个进程使用。常见的有打印机,但是如果同时让两个进程打印将造成混乱。当两个进程为了获取资源而被阻塞的话,那么这种状况就是死锁。
大部分死锁都和资源有关。那资源是什么呢?资源可以是硬件设备或是一组信息。资源分为两类:可抢占式和不可抢占式。可抢占式资源可以从拥有它的进程中抢占而不会产生任何副作用,存储器就是一类可抢占的资源。相反不可抢占资源是指在不引起相关计算失败的情况下,无法把它从占有它的进程处抢占过来,例如CD刻录机。使用一个资源所需要的事件顺序可以用抽象的形式表示如下:1)请求资源。2)使用资源。3)释放资源。
对于资源的获取可能的方法就是为每个资源配置一个信号量。
死锁的定义:如果一个进程集合中的每个进程都在等待只能由该进程集合中的其他进程才能引发的事件,那么,该进程集合就会死锁的。
死锁四个必要条件:
1)互斥条件。每个资源要么已经分配给了一个进程,要么就是可用的。
2)占有和等待条件。已经得到了某个资源的进程可以再请求新的资源。
3)不可抢占条件。已经分配给一个进程的资源不能强制性地被抢占,它只能被占有它的线程显地释放。
4)环路等待条件。死锁发生时,系统中一定有由两个或两个以上的进程组成的一条环路,该环路中的每个进程都在等待着下一个进程所占有的资源。
有四种处理死锁的策略:
1)忽略该问题。也许如果你忽略它,它也会忽略你。
2)检测死锁并恢复。让死锁发生,检测它们是否发生,一旦发生死锁,采取行动解决问题。
3)仔细对资源进行分配,动态地避免死锁。
4)通过破坏引起死锁的四个必要条件之一,防止死锁的产生。
关于四种解决方案
最简单的是鸵鸟算法。
第二种技术是死锁的检测和恢复。我们从最简单的例子开始,即每种类型只有一个资源。可以对这样的系统构造一张资源分配图(类似有向图这样),如果形成了环路,那么死锁就存在。算法如下:
1)对于有向图中的每个节点N,将N作为起始点执行下面5个步骤。
2)将L初始化为空表(L为列表集合),并清除所有有向边标记。
3)将当前节点添加到L的尾部,并检测该节点是否在L中已出现过两次。如果是,那么就形成了一个环,算法结束。
4)从给定的节点开始,检测是否存在没有标记的从该节点出发的弧(有向边)。如果存在的话,就执行5,不然跳到第6步。
5)随机选取一条没有标记的从该节点出发的弧(有向边),标记它。然后顺着这个弧线找到新的当前节点,返回第3步。
6)如果这一节点是起始节点,那么表明该图不存在任何环,算法结束。
此算法并不是最佳算法。
如果有多种相同资源存在,就需要采用另一种方法来检测死锁。我们需要提供一种基于矩阵的算法来检测从P1到Pn这n个进程中的死锁。假设资源的类型数为m,E1代表资源类型1,E2代表资源类型2,Ei代表资源类型i。E是现有资源向量,代表每种已存在的资源总数。比如:如果资源类型1代表磁带机,那么E1=2就表示系统有两台磁带机。
我们需要两个数组:C代表当前分配矩阵,R代表请求矩阵。Cij中的记录代表进程i所持有资源j的数量。同理,Rij代表进程i所需资源j的数量。
算法如下:
1)寻找一个没有标记的进程P,对于它而言R矩阵的第i行向量小于或等于A。
2)如果找到了这样一个进程,那么将C矩阵的第i行向量加到A中,标记该进程,并转到第1步。
3)如果没有这样的进程,那么算法终止。
算法结束时,所有没有标记过的进程都是死锁进程。
现在我们知道了如何检测死锁,但问题在于何时去检测它们。一种是每当有资源请求时去检测。另一种方法是每隔k分钟检测一次。
假设我们的死锁检测算法已成功地检测到了死锁,那么下一步该怎么办呢?第一是利用抢占恢复,在某些情况下,可能会临时将某个资源从它的当前所有者那里转移到另一个进程。第二是利用回滚恢复,如果系统设计人员以及主机操作员了解到死锁有可能发生,他们就可以周期性地对进程进行检查点检查。进程检查点检查就是将进程的状态写入一个文件以备以后重启。如果发现死锁,就从一个较早的检查点上开始,这样拥有所需资源的进程会回滚到一个时间点,在此时间点之前该进程获得了一些其他的资源。第三通过杀死进程恢复,这是最直接最简单的解决死锁的方法。
死锁避免
在讨论死锁检测时,我们假设当一个进程请求资源时,它一次就请求所有的资源。不过在大多数系统中,一次只请求一个资源。系统必须能够判断分配资源是否安全,并且只能在保证安全的条件下分配资源。那就是避免死锁,主要的算法是基于一个安全状态的概念。如果存在一个分配序列使得所有的进程都能完成,那么该状态就是安全的。反之为不安全的。而安全状态和不安全状态的区别是:从安全状态出发,系统能够保证所有的进程都能完成,而从不安全状态出发,就没有这样的保证了。
银行家算法
该模型基于一个小城镇的银行家,他向一群客户分别承诺了一定的贷款额度。算法要做的是判断对请求的满足是否会导致进入不安全状态。如果是,就拒绝请求;如果满足请求后系统仍然是安全的,就予以分配。银行家算法虽然很有意义但缺乏实用价值,因为很少有进程能够在运行前就知道其所需资源的最大值。
死锁避免从本质上来说是不可能的,因为它需要获知未来的请求,而这些请求时不可知的。那么实际的系统又是如何避免死锁呢?如果能够保证四个条件中至少有一个不成立,那么死锁将不会产生。破坏互斥条件,如果资源不被一个进程所独占,那么死锁肯定不会产生(使用假脱机技术)。破坏占有和等待条件,一种方法是所有进程在开始执行前请求所需的全部资源。另一种是当一个进程请求资源时,先暂时释放其当前占用的所有资源,然后再尝试一次获得所需的全部资源。破坏不可抢占条件,采用虚拟化的方式来避免发生这样的情况。破坏环路等待条件,一种方法就是将所有资源统一编号,按照资源顺序(升序)进行请求。