在计算机系统中有很多独占性的资源,在任一时刻它们只能被一个进程使用。如打印机、磁带等。如果两个进程同时使用打印机可能会造成瘫痪。正因为如此,os都具有授权一个进程(临时)排他访问某一种资源的能力
在很多应用中,需要一个进程排他性地访问若干重资源而不是一种。例如,有两个进程准备分别将扫描的文档记录在光盘上。进程A请求使用扫描仪,并被授权使用。但进程B首先请求蓝光光盘,也被授权使用。现在A请求蓝光光盘,但该请求在B释放蓝光前会被拒绝。但是,进程B非但不放弃蓝光,而是去请求扫描仪。这时,两个进程都被阻塞,并且一直处于这样的状态,这种情况就是死锁。
除了请求独占式的IO设备之外,别的情况也可能引起死锁。例如,在一个数据库系统中,为了避免竞争,可对如果记录加锁。如果进程A对记录R1加了锁,进程B对记录R2加了锁,接着这两个进程又试图各自把对方的记录也加锁,这时就会产生死锁。
资源
在进程对设备、文件等取得了排他性访问权时,有可能会出现死锁。为了尽可能使关于死锁的讨论通用,我们把这类需要排他性使用的对象成为资源。简单来说,资源就是随着时间的推移,必须能获得,使用以及释放的任何东西。
可抢占资源和不可抢占资源
资源分为两类:可抢占和不可抢占的。可抢占资源可以从拥有它的进程中抢占而不会产生任何副作用,存储器就是一类可以抢占的资源。例如,一个系统拥有256MB的用户内存和一台打印机。如果有两个256MB内存的进程都想进程打印,进程A请求并获得了打印机,然后开始计算要打印的值。在它没有完成计算任务之前,它的时间片就已经用完并换出。
相反,不可抢占资源是指在不引起相关的计算失败的情况下,无法把它从占有它的进程处抢占过来。如果一个进程已经开始刻盘,突然将刻盘机分配给另一个进程,那么将损坏光盘。在任何时刻光盘都是不可被抢占的。
总的来说,死锁与不可抢占资源有关,有关抢占资源的潜在死锁可以通过在进程之间重新分配资源而化解。所以,我们的重点放在不可抢占资源上。
使用一个资源所需要的事件顺序可以用抽象的形式表示如下:
- 请求资源
- 使用资源
- 释放资源
为什么可抢占资源可以通过进程之间重新分配?
内存本身是一种可抢占资源,如果进程A被进程B抢占了,但进程拥有打印机这种不可抢占资源,而进程B占有了内存,两个进程都缺少另一个进程拥有的资源。可以通过把进程B换出内存、把进程A换入内存就可以实现抢占进程B的内存。这样,进程A继续执行并执行打印任务,然后释放打印机。在这个过程中不会产生死锁。
死锁简介
死锁的规范定义如下: 如果一个进程集合中的每个进程都在等待只能由该进程集合中的其他进程才能引发的事件,那么,该进程集合就是死锁的。
由于所有进程都在等待,所以没有一个进程引发可以唤醒该进程集合中的其他进程的事件,这样,所有的进程都只好无限等待下去。
在大多数情况下,每个进程所等待的事件是释放进程已经占有的资源。换言之,这一死锁进程集合中的每一个进程都在等待另一个死锁的进程已经占有的资源。但是由于所有进程都不能运行,它们中的任意一个都无法释放资源,所以没有一个进程可以被唤醒。这种死锁称为资源死锁。这是最常见的类型,但并不是唯一类型。
资源死锁的条件
Coffman等人总结了发生死锁的四个必要条件
- 互斥条件:每个资源要么已经分配给了一个进程,要么就是可以用的。
- 占有和等待条件: 已经得到了某个资源的进程可以再请求新的资源。
- 不可抢占条件: 已经分配给一个进程的资源不能强制性地被抢占,它只能被占有的线程显示地释放。
- 环路等待条件: 死锁发生时,系统中一定有由两个或两个以上的进程组成的一条环路,该环路中的每个进程都在等待下一个进程所占有的资源。
以上四个条件一定是同时满足的。如果其中一个不成立,死锁就不会产生。
死锁建模
有四种处理死锁的策略:
- 忽略该问题
- 检测死锁并恢复,检测它们是否发生,一旦发生死锁,采取行动解决问题。
- 仔细对资源进程分配,动态避免死锁
- 破坏引起死锁的四个条件之一,防止死锁的发生。
鸵鸟算法
最简单的解决办法就是鸵鸟算法:把头埋到沙子里,假装根本没有问题发生。如果死锁平均5年发生一次,而每个月系统都会因硬件故障、编译器或者操作系统故障而崩溃一次,那么大多数的工程师不会以性能损失和可用性的代价去防止死锁。
死锁检查和死锁恢复
两阶段加锁
一般情况下避免死锁和预防死锁并不是很有希望,但是有一些特殊的应用方面,有很多卓越的专用算法。例如,在很多数据库系统中,一个经常发生的操作是请求锁住一些记录,然后更新所有锁住的记录。当同时多个进程运行时,就会出现死锁的危险。
常用的方法是两阶段加锁。在第一个阶段,进程试图对所有需要的记录进程加锁,一次锁一个记录。如果第一阶段加锁成功,就开始第二阶段,完成更新然后释放锁。在第一阶段并没有做实际的工作。
如果在第一阶段某个进程需要的记录已经被加锁,那么该进程释放