注:本文主要参考<<现代操作系统>>6.1,6.2节
何为死锁
先给大家讲一个关于死锁的笑话:
某天张三去面试,面试官问他你知道什么是死锁吗?张三说:你先给我offer我就告诉你.面试官说你先告诉我,我再给你offer.张三说:不行,你先给我offer,我再高速你.面试官说:不行,你先告诉我,我再给你offer.于是这场面试持续进行…
哈哈哈!
在计算机系统中,某些资源在同一时刻最多只能被一个进程使用,例如打印机.如果两个进程在同一时刻使用打印机,那打印出的一份文档上可能交叉打印出两个进程准备打印的内容,这显然会造成混乱.因此操作系统必须提高某种解决方案保证这类资源的排他性访问.操作系统可能使用类似互斥量的方式保存打印机在任一时刻最多被一个进程使用.
通过实现一些必要资源的排他性访问,我们避免了多个进程同时获取该资源可能造成的混乱,但同时也为其他问题埋下了隐患,这便是死锁.考虑如下问题,两个进程需要完成相同的工作,将扫描后的文档刻录到CD上.操作系统能够确保扫描仪和CD刻录机的排他性访问.假设某一时刻进程A获取到了扫描仪的访问权,进程B获取到了CD刻录机的访问权,此时进程A需要获取刻录机时会失败,因此刻录机正被进程B占用.而进程B准备获取扫描仪时也会失败,因此其正被进程A占用.由于进程A在无法获取CD刻录机的情况下被阻塞,进程B也因无法获取扫描仪而被阻塞.此时两个进程无法释放已占用的资源,导致互不相让,因而引发死锁.
在上例中,我们访问的资源由操作系统提供了排他性访问,在一些情形下,程序员可能访问由自己添加了排他性条件的资源,例如在生产者-消费者问题中,我们通过添加互斥量保证同一时刻进程一个能够访问缓冲区中的内容.在此情形下,多个进程间也有可能引发死锁.
死锁的规范定义如下:
如果一个进程集合中的每个进程都在等待只能由该进程集合中的其他进程才能引发的事件,那么,该进程集合就是死锁的。
由于所有的进程都在等待,所以没有一个进程能引发可以唤醒该进程集合中的其他进程的事件,这样,所有的进程都只好无限期等待下去。在这一模型中,我们假设进程只含有一个线程,并且被阻塞的进程无法由中断唤醒,无中断条件使死锁的进程不能被时钟中断等唤醒,从而不能引发释放该集合中的其他进程的事件。
死锁的必要条件
Coffman等人(1971)总结了发生(资源)死锁的四个必要条件:
1)互斥条件。每个资源要么已经分配给了一个进程,要么就是可用的。
(在前例中,扫描仪与CD刻录机均是排他性访问的)
2)占有和等待条件。已经得到了某个资源的进程可以再请求新的资源。
(在前例中,进程A在获得扫描仪的访问权后,还可以继续请求CD刻录机,同样,进程B在获得刻录机的权限后,还可以请求扫描仪)
3)不可抢占条件。已经分配给一个进程的资源不能强制性地被抢占,它只能被占有它的进程显式地释放。
(在前例中,进程A获得的扫描仪无法被其他进程抢占,只能在进程A结束使用后主动释放)
4)环路等待条件。死锁发生时,系统中一定有由两个或两个以上的进程组成的一条环路,该环路中的每个进程都在等待着下一个进程所占有的资源。
(进程A与进程B构成一条环路)
死锁发生时,以上四个条件一定是同时满足的。如果其中任何一个条件不成立,死锁就不会发生。因此,我们可以通过何时的方式破坏其中的某些条件从而避免死锁.
使用资源分配图进行死锁建模
Holt(1972)指出如何用有向图建立上述四个条件的模型。在有向图中有两类节点:用圆形表示的进程,用方形表示的资源。从资源节点到进程节点的有向边代表该资源已被请求、授权并被进程占用。如果资源分配图中存在一条环形路径,则表明发生死锁.
在下图中,有A,B,C三个进程,R,S,T三个资源.若进程调度执行顺序如左侧文本框中所示,则会构成死锁.
死锁问题解决方案
有四种处理死锁的策略:
1)忽略该问题。也许如果你忽略它,它也会忽略你。
2)检测死锁并恢复。让死锁发生,检测它们是否发生,一旦发生死锁,采取行动解决问题。
3)仔细对资源进行分配,动态地避免死锁。
4)通过破坏引起死锁的四个必要条件之一,防止死锁的产生。
下一篇中我们逐一分析死锁的四种解决方案.