死锁
操作系统相关的内容可以分为内存管理、进程和线程、死锁三个部分。本篇文章记录死锁部分。
在计算机系统中,有很多独占性资源,在任一时刻,它们都只能被一个进程使用,如打印机,文件系统表中的表项。于是,操作系统都具有授权一个进程排他的访问某一种资源的能力。
很多应用中,需要一个进程排他性地访问若干资源而不是一种。举例如下,进程A和进程B都想将扫描的文档记录到蓝光光盘上,A请求扫描仪,被授权使用;B请求光盘刻录机,被授权使用;此时A请求光盘刻录机,该请求在B释放光盘刻录机之前会一直被拒绝,然而B此时非但不释放光盘刻录机,而是去请求扫描仪。这种情况下,两个进程都被阻塞,并一直处于这样的状态,称为死锁。
死锁也发生在机器之间。举例如下,办公室中用计算机连成局域网,扫描仪、光盘刻录机、打印机等设备也连接到局域网成为共享资源,这种情形会造成3个,4个,甚至更多个设备和用户发生死锁。
除了请求独占性I/O设备外,别的情况也可能引发死锁。如数据库系统,进程A对记录R1加锁,进程B对记录R2加锁,此时两个进程又想把对方记录加锁(访问对方记录),这时会产生死锁。因此,软硬件资源都有可能产生死锁。
1. 资源
大部分死锁都和资源相关,在进程对设备,文件等取得了排他性访问权时,有可能会出现死锁。为了尽可能使关于死锁的讨论通用,我们把这类需要排他性使用的对象成为资源。
1.1. 可抢占资源
可抢占资源可以从拥有它的进程中抢占而不会产生任何副作用,存储器就是一类可抢占资源。
在一台标准PC机中,内存中的页面总是可以置换到磁盘中并置换回来,故内存是可抢占的。有关可抢占资源的潜在死锁通常可以通过在进程之间重新分配资源而化解。
1.2. 不可抢占资源
不可抢占资源是指在不引起相关的计算失败的情况下,无法把它从占有它的进程出抢占过来。
死锁和不可抢占资源具有很强的相关性。使用一个资源的顺序可抽象为3个步骤:1. 请求资源。 2. 使用资源。3. 释放资源。
2. 死锁
如果一个进程集合中的每个进程都在等待只能由该集合中的其他进程才能引发的事件,那么,该进程集合就是死锁的。大多数情况下,每个进程所等待的事件是释放进程集合中其他进程所占有的资源,这种死锁称为资源死锁。
2.1. 资源死锁必要条件
- 互斥条件。每个进程要么已经分配给了一个进程,要么就是可用的。
- 占有和等待条件。已经获得了某个资源的进程可以再请求新的资源。
- 不可抢占条件。已经分配给一个进程的资源不能强制性的被抢占,它只能被占有它的进程显式地释放。
- 环路等待条件。死锁发生时,系统一定有由两个或两个以上的进程组成的一条环路,该环路中的每个进程都再等待着下一个进程所占有的资源。
死锁发生时,以上四个条件一定同时满足,任一条件不成立,死锁就不会发生。
2.2. 处理死锁的策略
- 忽略死锁。鸵鸟算法。
- 检测并恢复。允许死锁发生,一旦发生采取行动解决问题。
死锁恢复:
1. 利用抢占恢复
2. 利用回滚恢复
3. 通过杀死进程恢复
- 仔细对资源分配,动态避免死锁。
大多数系统,一次只请求一个资源,系统必须能够判断分配资源是否安全,并且只能在保证安全的条件下分配资源。问题是是否由一种算法总能做出正确的选择,从而避免死锁?
答案是肯定的,条件是必须事先获得一些特定的信息。1965年,Dijkstra提出了一种能够避免死锁的调度算法,称银行家算法。
- 破坏死锁四个必要条件。
死锁避免从本质上是不可能的,它需要获得未来的请求,而这些请求是不可知的。实际的系统中可以通过破坏死锁产生的四个条件中任意一个,来实现的。
破坏互斥条件(某个资源要么已经分配给某个进程,要么就是可用的)。如果不去限制资源只能被一个进程独占,死锁肯定不会发生。以打印机为例,允许两个进程同时使用打印机会造成混乱,不过假脱机打印机(spooling printer)技术可以允许若干进程同时产生输出。在该模型中,唯一真正请求物理打印机的是打印机守护进程,它不会请求别的资源,所以不会因打印机而产生死锁,一般不会设计为在所有输出进入假脱机之前就开始打印,而是会将守护进程设计成在完整的输出文件就绪后开始打印。
破坏占有并等待条件(已经占有某个资源的进程,可再请求新的资源)。如果禁止已持有资源的进程再等待其他资源便可以消除死锁。一种实现方法是规定所有进程再开始执行前请求所需的全部资源,这种方法的一个直接问题是很多进程直到运行时才知道自己需要哪些资源。实际上,如果进程能知道它需要哪些资源,就可以使用银行家算法进行资源分配,来避免死锁的发生。
破坏不可抢占条件(已经分配给某个进程的资源,不能强制性被抢占)。如进程A已分配到一台打印机,且正在进行打印输出,由于它需要的绘图仪无法获得而强制性地把它占有的打印机抢占掉。通过假脱机技术可以避免抢占造成的混乱。
破坏环路等待条件。消除环路等待有几种方法。一种是保证每一个进程在任何时刻只能占用一个资源,如果要请求另外一个资源,必须先释放第一个资源。另一种方法是将所有资源统一编号,进程可以在任意时刻提出资源请求,但所有请求必须按照资源编号的顺序提出。