一、死锁概念
1. 概念
- 通俗解释:每个人都占有一个资源,同时又在等待别人占有的资源
- 并发环境下,各进程因竞争资源而造成的一种互相等待对方占有资源,导致进程阻塞都无法推进的现象。
2.死锁、饥饿、死循环的区别
- 死锁:各进程互相等待对方占有资源,导致进程都阻塞,无法推进的现象。
- 饥饿:某进程长期得不到想要的资源,而无法向前推进的现象。
- 死循环:某进程执行过程中一直跳不出某个循环的现象。
名称 | 联系 | 区别 |
死锁 | 都是进程无法向前推进的现象(故意设计的死循环不算) | 死锁是“等待对方手中占有的资源”,所以至少有两个或两个以上的进程才会同时发生死锁,发生死锁的线程一定是阻塞态 |
饥饿 | 饥饿只会有一个进程发生,它既可以是阻塞态(得不到I/O设备),也可能是就绪态(得不到处理机) | |
死循环 | 死锁和饥饿是操作系统分配资源的策略不合理导致的,而死循环是由于代码逻辑错误导致的。 |
3.死锁发生条件:同时满足以下四个条件,若其中条件之一不成立,那就不会发生死锁情况
- 互斥条件:一个资源每次只被一个进程使用
- 请求与保持条件:因请求而阻塞,且不释放已获得的资源
- 不剥夺条件:已获得的资源在未使用完之前,不能被强行剥夺,只能主动释放
- 循环等待:若干进程形成循环等待资源关系
4.死锁处理策略
- 预防死锁。破坏四个条件中的一个或几个。
- 避免死锁。用某种算法防止系统进入不安全状态,比如银行家算法。
- 死锁的检测和解除。允许死锁的发生,但操作系统会负责检测死锁的发生,然后采取措施解除死锁。
二、死锁处理策略——预防死锁
1. 破坏互斥条件
- 把互斥使用的资源改造为允许共享使用的资源,系统则不会进入死锁状态。
- 缺点:并不是所有资源都可以改造,并且为了系统安全,很多地方都必须保护这种互斥性。
2. 破坏请求和保持请求条件
- 采用静态分配方法,即进程在运行前一次申请完所有需要的资源,在资源未满足之前,不让它投入使用。
- 缺点:资源利用率低,可能导致某些进程饥饿。
3. 破坏不剥夺条件
- 方案一:当某个进程请求新的资源得不到满足时,他必须立即释放保持的所有资源,待以后需要时重新申请。
- 方案二:当某个进程需要的资源被其他进程占用,可以由操作系统协助,强行剥夺资源。但须考虑进程优先级。
- 缺点:实现复杂;一般只适用于易保存和恢复的资源,因为释放已获得的资源可能造成前一阶段工作的失效;反复申请和释放资源会增加系统开销,降低系统吞吐量;采用方案一,意味着只要得不到某资源,之前获得的资源就会被释放,之后再重新申请,如此反复,会导致进程饥饿。
4. 破坏循环等待条件
- 采用顺序资源分配法,给紫铜资源编号,每个进程必须按标号递增顺序请求资源,这样保证一个进程只有占有小号资源时才能申请大号资源,并且不会逆向申请,这样就避免了循环等待。
- 缺点:不方便增加新设备,会影响已分配的编号;进程实际使用资源的顺序和编号递增顺序不一致,会导致资源浪费;必须按规定次序申请资源,用户编程较麻烦。
三、死锁处理策略——避免死锁
1. 安全序列、不安全状态、死锁的联系
- 安全序列指系统按照该种方式分配资源,则每个进程都能顺利完成。
- 只要能找出一种安全序列,则系统处于安全状态,此状态下不会发生死锁。
- 安全序列不止一种,可能存在多个。
注:当系统处于不安全状态时,如果有进程提前归还资源,那么系统也会重新回到安全状态。这意味着,处于不安全状态下的系统未必会发生死锁,但是发生死锁时,一定处于不安全状态。
2. 银行家算法
- Dijkstra为银行系统设计,确保银行在发放现金贷款时,不会发生不能满足的情况。后用于操作系统避免死锁。
- 核心思想:在进程提出资源申请时,先预判该分配是否会导致系统进入不安全状态,如果会,就暂时不答应该次请求,并让此进程阻塞等待。
(1)假设系统中有n个进程,m种资源。
(2)数据结构:
- 长度为m的一维数组Available,表示剩余的可用资源。
- n*m矩阵Max,表示各进程对资源的最大需求。
- n*m矩阵Allocation,表示对各进程已分配的资源数。
- Need = Max - Allocation矩阵,表示各进程还需要的资源数。
- 长度为m的一维数组Requesti,表示进程此次申请所需的资源数。
(3)银行家算法步骤:
- 如果Requesti[ j ] ≤ Need[ i, j ] (0<=j<=m) 便转向;否则认为出错。
- 如果Requesti[ j ] ≤ Available[ j ] (0<=j<=m),便转向;否则表示尚无足够资源,Pi必须等待。
- 系统尝试着把资源分配给进程Pi,并修改相应的数据,并非真的分配,修改数值只是为了做预判:
- Available = Available - Requesti;Allocation[ i, j ] = Allocation[ i, j ] + Requesti[ j ];Need[ i, j ] = Need[ i, j ] - Requesti[ j ]。
- 操作系统执行安全性算法,检查此次资源分配后,系统是否处于安全状态。若安全才正式分配;否则恢复相应数据,让进程阻塞等待。
(4)安全性算法步骤:
- 检查当前的剩余可用资源是否能满足某个进程的最大需求,如果可以,就把该进程加入安全序列,并把该进程持有的资源全部回收。如果不行那么就无法构成安全序列。
- 不断重复上述过程,看最终是否能让所有进程都加入安全序列。
四、死锁处理策略——死锁的检测和解决
1. 死锁的检测
(1)资源分配图:依次消除不与阻塞进程相连的边,直到无边可消。
- 两种节点:进程节点(对应一个进程),资源节点(对应一类资源,一类资源可能有多个)
- 两种边:进程节点-->资源节点(请求边,表示进程想申请几个资源,一条边代表一个),资源节点-->进程节点(分配边,表示已经为进程分配了几个资源,一条边代表一个)
(2)死锁定理:如果某时刻系统的资源分配图是不可完全简化的,那么此时系统死锁。
2. 死锁的解除
(1)资源剥夺法:挂起某些死锁进程,并抢占其资源,将这些资源分配给其他死锁进程。但要防止被挂起的进程长时间得不到资源而进程饥饿。
(2)撤销进程法:亦称终止进程法,强制撤销部分或全部死锁进程,并剥夺其资源。这种方式代价较高。
(3)进程回退法:让一个或多个死锁进程回退到足以避免死锁的地步,这要求系统记录进程的历史信息,设置还原点。