OS-死锁

死锁

  • 死锁:在并发环境下,各进程互相等待对方手里的资源,导致各进程都阻塞,无法继续运行的现象。(至少有两个及以上的进程同时死锁,一定处于阻塞态
  • 饥饿进程长期得不到想要的资源,导致无法继续向前运行的现象。(可能只有一个进程饥饿,可能是阻塞态或就绪态
  • 死循环:进程执行过程中一直跳不出某个循环。(程序逻辑Bug

死锁产生的必要条件

  • 这四个条件是死锁的必要条件,而不是充分条件。也就是说,如果死锁发生了,这四个条件一定同时存在;但即使这四个条件都存在,也不一定立刻发生死锁
  • 只要有一个条件不满足,就不会发生死锁。

互斥条件

  • 只有对必须互斥使用的资源(同时只能有一个进程占有)争抢才会导致死锁。
  • 资源是独占的。即在一段时间内,某资源只能被一个进程占用。 如果此时还有其他进程请求该资源,则请求者只能等待,直到占有资源的进程释放。

不可剥夺条件

  • 进程所获得的资源在未使用完成之前不能被其他进程抢占,只能主动释放
  • 进程已获得的资源,在未使用完之前,不能被强行剥夺(抢占),只能由该进程在使用完毕后自愿释放。

请求保持条件

  • 进程已经保持(占有)了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有。此时,请求进程阻塞,但对自己已获得的资源保持不放。

循环等待条件

  • 存在一个进程资源的循环等待链。即:进程 P1 等待 P2 占有的资源,P2 等待 P3 占有的资源,……,Pn 等待 P1 占有的资源,形成一个闭环。

死锁的处理策略

鸵鸟算法

  • 鸵鸟算法是指操作系统面对死锁问题时,不采取任何预防、避免或检测措施。简单来说就是:“假装没看见,当它不存在”
  • 发生死锁时,系统不做任何事情,通过简单重启系统来解决死锁问题
  • 在个人计算机中基本上都采用鸵鸟算法,因为:
    • 在个人计算机由于资源的竞争相对不是很激烈,所以发生死锁的概率较小,而且发生死锁时的影响较小(只会影响当前一个用户)。
    • 死锁预防、死锁避免、死锁检测/解除算法都有较大的系统开销

预防死锁

  • 破坏死锁产生的四个必要条件

破坏互斥条件

  • 把互斥使用的资源改造为允许共享使用,则不会进入死锁状态。(SPOOLing技术
  • 缺点:不是所有的资源都可以改造成共享资源,为了系统安全很多地方还需要保持互斥性。

破坏不可剥夺条件

  • 当进程请求新的资源得不到满足时,立即释放保持的所有资源,待需要时重新申请。(即使某些资源未用完,也需要主动释放)
  • 缺点:可能出现饥饿现象。
  • 进程想要的资源被其他进程占有,可以由操作系统协助,将想要的资源强行剥夺,一般要考虑优先级。
  • 缺点:实现复杂,释放已获得的资源可能造成前一阶段的工作失效,反复地申请释放资源增加系统开销

破坏请求保持条件

  • 使用静态分配方法进程在运行前一次申请所需要的全部资源,资源未满足前不运行。运行后,不申请其他任何资源。
  • 缺点:资源利用率低(进程运行期间一直保持所有资源,包括低频使用的资源),可能导致进程饥饿。

破坏循环等待条件

  • 使用顺序资源分配法,首先将系统资源编号,每个进程必须按照编号递增的顺序请求资源,同类资源一次申请完。
  • 一个进程只有已经占有小编号的资源时,才有资格申请大编号的资源。所以有大编号资源的进程不会逆向申请小编号的资源,不会出现循环等待。
  • 缺点:不方便增加新的设备,会导致资源浪费,用户编程麻烦

避免死锁

  • 避免系统进入可能产生死锁的不安全状态
  • 安全状态:系统处于“安全状态”,意味着系统能够按照某种特定的顺序(安全序列)为每个进程分配其所需的资源,直至所有进程都能顺利完成并归还资源,而不会发生死锁。
  • 不安全状态: 如果系统无法找到任何一个安全序列,则称系统处于不安全状态。
  • 不安全状态 \(\not =\) 死锁,不安全状态可能会发生死锁(存在死锁风险)

银行家算法 (Banker's Algorithm)

核心逻辑

当一个进程申请资源时,系统执行以下步骤:

  1. 检查: 进程请求的资源是否超过了它声明的最大需求?是否超过了系统当前可用的资源?
  2. 试探性分配: 系统假定把资源分配给该进程,并修改系统状态数据。
  3. 安全性检查 (Safety Algorithm): 在这个“假定”的新状态下,运行安全性算法,检测是否存在一个安全序列。
  4. 决策:
    • 如果存在安全序列 → 分配资源(正式修改状态)。
    • 如果不存在安全序列 → 拒绝/推迟分配(恢复原状,进程阻塞)。

关键数据结构

假设有 \(n\) 个进程和 \(m\) 类资源,我们需要以下矩阵/向量:

  • Available (可用资源向量): 长度为 \(m\)\(Available[j]=k\) 表示资源 \(R_j\)​ 现有 \(k\) 个可用。
  • Max (最大需求矩阵): \(n×m\) 矩阵。\(Max[i,j]\) 表示进程 \(P_i\)​ 需要资源 \(R_j\)​ 的最大数量。
  • Allocation (已分配矩阵): \(n×m\) 矩阵。\(Allocation[i,j]\) 表示进程 \(P_i\)​ 当前已持有资源 \(R_j\)​ 的数量。
  • Need (需求矩阵): \(n×m\) 矩阵。\(Need[i,j]\) 表示进程 \(P_i\)​ 还需要多少资源 \(R_j\)​ 才能完成任务。

\[Need[i,j] = Max[i,j] - Allocation[i,j] \]

  • Request_i(请求向量):长度 \(m\),表示进程\(i\)本次申请的各种资源量。\(Request[j]\)表示向资源\(j\) 请求的资源数。

判断流程

  • 判断是否满足 \(Request_i[j] \le Need[i,j]\),不满足则超过了它声明的最大需求
  • 判断是否满足 \(Request_i[j] \le Availiable[j]\),不满足则无足够的资源分配,出错。
  • 试探性分配(将资源分配后的状态):
    • 可用资源数-请求资源数,已分配资源数+请求资源数,所需资源数-请求资源数

\[Available = Availabe - Request \]

\[Allocation[i,j] = Allocation [i,j] + Request[j] \]

\[Need[i,j] = Need[i,j] - Request[j] \]

  • 检查当前状态是否安全(找安全序列):检查当前可用资源是否能满足某个进程的需求,可以就将其加入安装序列,把该进程持有的资源全部回收。

银行家算法实例

9xHzFyPTcOZWQaI.png

  • 判断T1时刻的安全性(Work向量相当于暂时的Avalibale向量的副本)
    R8YfsKgrE5ilvxQ.png

死锁的检测和解除

  • 允许死锁发生系统负责检测出死锁并解除

死锁检测-资源分配图

kPHms3JdTQYa4SU.png

  • 节点:

    • 进程节点(对应一个进程),
    • 资源节点(对应一类资源):一类资源用一个矩形,一类资源的多个资源用矩形内的小圆表示
  • 边:

    • 请求边:进程节点->资源节点 :表示进程想申请的资源(每条边代表一个)
    • 分配边:资源节点->进程节点 :表示进程已占有的资源(每条边代表一个)
  • 如果系统中剩余可用资源数足够满足进程的需求,那这个进程不会阻塞,可以顺利执行下去。

  • 如果这个进程执行完后把资源归还(删去与这个进程节点相邻的所有边),就有可能使得其他正在等待的进程得到满足,唤醒执行。重复上述过程。

Z4WxYu8mj2cFht3.png

  • 如果最终能消除所有边,那么这个资源分配图是可完全简化的,一定没有发生死锁。
  • 否则(不能消除所有边),此时发生了死锁,最终连着边的进程就是发生死锁的进程。
  • 即:依次消除与不阻塞进程相连的边,直到无边可消。
    ViftSU3BHxOedc9.png

CLdjqvo6fQ9JpE4.png

死锁解除

  • 资源剥夺法挂起某些死锁进程,抢占它的资源,分配给其他死锁进程。应防止被挂起的进程长时间得不到资源而饥饿。
  • 撤销进程法强制撤销部分甚至全部死锁进程,剥夺这些进程的资源。付出代价可能很大,因为有些进程一旦被终止后还得从头再来。
  • 进程回退法让一个或多个进程回退到足以避免死锁的地步。
posted @ 2026-01-10 16:57  NightRainLone  阅读(5)  评论(0)    收藏  举报