【清华大学】操作系统 陈渝——Part11 死锁
死锁问题
-
交通死锁:都在抢占行车道会出现死锁线性。
- 流量只在一个方向
- 桥的每一个部分可以看作为一个资源
- 如果死锁,可能通过一辆车倒退可以解决(抢占资源和回滚)
- 如果发生死锁,可能几辆车必须都倒退
- 可能发生饥饿
-
计算机操作系统中,也会大量出现
-
一组阻塞的进程拥有一种资源等待获取另一个进程所占有的资源
-
例子:
1.两个进程P1和P2,P1拥有一个资源R1,需要另一个资源R2,R2被P2占有,P2需要P1的资源R1,谁也无法得到继续执行的资源。2.系统有2个磁带驱动器
-
-
为什么出现死锁?
程序的并发执行导致,如果只有一个进程,就不会出现死锁。因为程序中有多个进程,共享资源,不加以某种约束,出现了死锁。
系统模型
数学的角度来看,死锁中有两类需求方(进程),需求资源
- 资源类型:P1,R2…Rm
CPU cycles,Memory space,I/O devices - 每个资源类型Ri有Wi实例
- 每个进程使用资源如下:
- request/get <-- free resource
- use/hold <-- requested/used resourse
- relase <-- free resource
可重复使用的资源
- 在一个时间只能一个进程使用且不能被删除
- 进程获得资源,后来释放由其他进程重用
- 处理器,I/O通道,主副存储器,设备和数据结构,如文件,数据库和信号量都可以是资源。
- 如果每个进程拥有一个资源并请求其他资源,死锁可能发生。
使用资源
- 创建和销毁
- 在I/O 缓冲区的中断,信号,信息,消息
- 如果接收消息阻塞(申请的资源拿不到)可能会发生死锁
- 可能少见的组合事件会引起死锁
资源分配图
一组顶点V和边E的集合
-
V有两种类型:
- P = {P1, P2, …, Pn},集合包括系统中的所有进程。
- R = {R1 ,R2, …, Rm},集合包括系统中的所有资源类型。
-
一个进程 Pi 访问某一个资源 Rj
requesting/claiming edge - directed edge Pi -> Rj -
一个资源 Rj 被一个进程所使用 Pi
assignment/holding edge - directed edge Rj -> Pi
资源分配图例子
没有死锁
P1拥有R2,需要R1;
P2拥有R1和R2,需要R3;
P3拥有R3;
R4没被占用。
P3用完R3后,R3变成free后,可被P2使用,P2执行一段时间后,R1和R2会被释放掉,P1便可以执行。
有死锁
P1拥有R2,需要R1;
P2拥有R1和R2,需要R3;
P3拥有R3,需要R3
R4没被占用。
和上面的情况对比,P3需要等R2,因此有死锁。
有循环的资源分配图没有死锁
P1拥有R2,需要R1;
P2拥有R2;
P3拥有R1,需要R2;
P4拥有R2。
P2和P4不需要等待其他资源,运行一段时间后一定释放该资源,一旦释放了拥有的资源,便可供给其他需要该资源的进程。
小结
- 如果图中不包含循环 ⇒ 没有死锁
- 如果图中包括循环 ⇒
- 如果每个资源类只有一个实例,那么死锁
- 如果每个资源类有几个实例,可能死锁
死锁特征
出现死锁后的四个条件
死锁可能出现后四个条件同时成立,
- 互斥:出现死锁后,在一个时间一个资源只能被一个进程占用,其他进程不能再使用这个资源了。
Mutual exclusion condition: Each resource is either currently assigned to exactly one process or is available. - 持有并等待:进程保持至少一个资源正在等待获取其他进程持有的资源。
Hold and wait condition: Processes currently holding resources that were granted earlier can request new resources. - 无抢占:一个资源只能被进程自愿释放,进程已经完成了任务之后。
No preemption condition: Resources previously granted cannot be forcibly taken away from a process. They must be explicitly released by the process holding them. - 循环的等待:存在等待进程{P0,P1,…PN},P0正在等待P1所占用的资源,P1正在等待P2所占用的资源…PN-1 正在等待PN所占用的资源, PN正在等待P0所占用的资源。
Circular wait condition:There must be a circular chain of two or more processes, each of which is waiting for a resource held by the next member of the chain.
死锁处理方法
- 确保操作系统永远不会进入死锁状态
- 运行系统进入死锁状态,然后恢复
- 忽略这个问题,假装系统中从来没有发生死锁(鸵鸟算法The Ostrich Algorithm,最简单的办法);用于大多数操作系统,包括UNIX。
Deadlock Prevention(死锁预防)
让死锁不会出现,打破四个条件之一
限制申请方式
-
打破互斥 - 即允许进程同时访问某些资源。但是,有的资源是不允许被同时访问的,像打印机等等,这是由资源本身的属性所决定的。所以,这种办法并无实用价值。
-
打破持有并等待 - 可以实行资源预先分配策略。即进程在运行前一次性地向系统申请它所需要的全部资源。如果某个进程所需的全部资源得不到满足,则不分配任何资源,此进程暂不运行。只有当系统能够满足当前进程的全部资源需求时,才一次性地将所申请的资源全部分配给该进程。由于运行的进程已占有了它所需的全部资源,所以不会发生占有资源又申请资源的现象,因此不会发生死锁。但是,这种策略也有如下缺点:
- 在许多情况下,一个进程在执行之前不可能知道它所需要的全部资源。这是由于进程在执行时是动态的,不可预测的;
- 资源利用率低。无论所分资源何时用到,一个进程只有在占有所需的全部资源后才能执行。即使有些资源最后才被该进程用到一次,但该进程在生存期间却一直占有它们,造成长期占着不用的状况。这显然是一种极大的资源浪费;
- 降低了进程的并发性。因为资源有限,又加上存在浪费,能分配到所需全部资源的进程个数就必然少了。
-
打破无抢占条件 - 即允许进程强行从占有者那里夺取某些资源。就是说,如果一个进程已占有了某些资源,它又申请新的不能被立即分享的资源,则它必须释放当前占有的全部资源,以后再重新申请。它所释放的资源可以分配给其它进程。这就相当于该进程占有的资源被隐蔽地强占了。这种预防死锁的方法实现起来困难,会降低系统性能。
-
打破循环等待条件 - 实行资源有序分配策略。采用这种策略,即把资源事先分类编号,按号分配,使进程在申请,占用资源时不会形成环路。所有进程对资源的请求必须严格按资源序号递增的顺序提出。进程占用了小号资源,才能申请大号资源,就不会产生环路,从而预防了死锁。这种策略与前面的策略相比,资源的利用率和系统吞吐量都有很大提高,但是也存在以下缺点:
- 限制了进程对资源的请求,同时给系统中所有资源合理编号也是件困难事,并增加了系统开销;
- 为了遵循按编号申请的次序,暂不使用的资源也需要提前申请,从而增加了进程对资源的占用时间。
Deadlock Avoidance(死锁避免)
需要系统具有一些额外的先验信息提供判断是否会发生死锁
- 最简单和最有效的模式是要求每个进程声明它可能需要的每个类型资源的最大数目
- 资源的分配状态是通过限定提供与分配的资源数量,和进程的最大需求
- 死锁避免算法是动态检查的资源分配状态,以确保永远不会有一个环形等待状态。
- 当一个进程请求可用资源,系统必须判断立即分配是否能使系统处于安全状态。
- 系统处于安全状态指:针对所有进程,存在安全序列。
- 安全序列{P1,P2,…,Pn}是这样组成的:若对于每一个进程Pi,它需要的附加资源可以被系统中当前可用资源加上所有进程Pj当前占有资源之和所满足,则{P1,P2,…,Pn}为一个安全序列,这时系统处于安全状态,不会进入死锁状态。
- 虽然存在安全序列时一定不会有死锁发生,但是系统进入不安全状态(四个死锁的必要条件同时发生)也未必会产生死锁。当然,产生死锁后,系统一定处于不安全状态。
银行家算法
利用银行家算法避免死锁,保证安全性。
前提条件
- 多个资源实例
- 每个进程都必须能最大限度地利用资源
- 当一个进程请求一个资源,就不得不等待
- 当一个进程获得所有的资源就必须在一段有限的时间释放它们。
基于上述前提条件,银行家算法通过尝试寻找允许每个进程获得的最大资源并结束(把资源返还给系统)的进程请求的一个理想执行时序,来决定一个状态是否是安全的。
当一个进程申请使用资源的时候,银行家算法通过先 试探 分配给该进程资源,然后通过 安全性算法 判断分配后的系统是否处于安全状态,若不安全则试探分配作废,让该进程继续等待。
银行家算法数据结构
- n = 进程数量, m = 资源类型数量
- Max(总需求量) :n * m矩阵。如果Max[i,j] = k,表示进程Pi最多请求资源类型Rj的k个实例
- Available(剩余空闲量):长度为m的向量。如果Available[j] = k,有k 个类型Rj的资源实例可用
- Allocation(已分配量):n * m矩阵。如果Allocation[i,j] = k,则Pi当前分配了k个资源类型Rj的实例
- Need(未来需要量):n * m矩阵。如果Need[i,j] = k,则Pi可能需要至少k个资源类型Rj的实例。
- Need[i,j] = Max[i,j] - Allocation[i,j]
Deadlock Dectection(死锁检测)和 Recovery from Deadlock(死锁恢复)
一般来说,由于操作系统有并发,共享以及随机性等特点,通过预防和避免的手段达到排除死锁的目的是很困难的。这需要较大的系统开销,而且不能充分利用资源。为此,一种简便的方法是系统为进程分配资源时,不采取任何限制性措施,但是提供了检测和解脱死锁的手段:能发现死锁并从死锁状态中恢复出来。因此,在实际的操作系统中往往采用死锁的检测与恢复方法来排除死锁。
死锁检测与恢复是指系统设有专门的机构,当死锁发生时,该机构能够检测到死锁发生的位置和原因,并能通过外力破坏死锁发生的必要条件,从而使得并发进程从死锁状态中恢复出来。
- 允许系统进入死锁状态
- 死锁检测算法
- 恢复机制
死锁检测算法
开销很大
死锁恢复
一旦在死锁检测时发现了死锁,就要消除死锁,使系统从死锁状态中恢复过来。
- 最简单,最常用的方法就是进行系统的重新启动,不过这种方法代价很大,它意味着在这之前所有的进程已经完成的计算工作都将付之东流,包括参与死锁的那些进程,以及未参与死锁的进程。
- 撤消进程,剥夺资源。终止参与死锁的进程,收回它们占有的资源,从而解除死锁。这时又分两种情况:一次性撤消参与死锁的全部进程,剥夺全部资源;或者逐步撤消参与死锁的进程,逐步收回死锁进程占有的资源。一般来说,选择逐步撤消的进程时要按照一定的原则进行,目的是撤消那些代价最小的进程,比如按进程的优先级确定进程的代价;考虑进程运行时的代价和与此进程相关的外部作业的代价等因素。