admin 管理员组

文章数量: 887018

目录

  • 基本定义
  • 死锁产生的原因
    • 系统资源的竞争
    • 进程推进顺序非法
    • 死锁产生的必要条件
  • 死锁的处理策略
    • 死锁预防
      • 破坏互斥条件
      • 破坏不可剥夺条件
        • 方案一
        • 方案二
      • 破坏请求并保持条件
      • 破坏循环等待条件
    • 避免死锁
      • 系统安全状态
      • 银行家算法
        • 图解说明
    • 死锁检测及解除
      • 死锁检测
        • 资源分配图
        • 检测死锁的算法
      • 死锁消除


非科班👀 👉 👉 👉 👉 自学计算机6个月总结——不完全学习路线分享

早期的操作系统学习笔记📗

主要参考来源

操作系统_清华大学(向勇、陈渝)

Operating systems: internals and design principles

Operating System Concepts

还有其余的一些网络博文


博客记录

操作系统-Operating-System第一章:概述

操作系统-Operating-System第二章:启动、中断、异常和系统调用

操作系统-Operating-System第三章01:计算机体系结构及内存分层体系

操作系统-Operating-System第三章02:地址空间和地址生成

操作系统-Operating-System第三章03:内存管理方式(连续内存分配)

作为非科班,前两个月学习操作系统的时候由于很多知识点不是很了解,所以学习起来有点吃力。为打好基础,本人补习了一下《计算机组成原理》的相关知识,并在博客专栏中做了记录。事实证明,先学一点计组知识有助于理解《操作系统》这门课。

《计算机组成原理》笔记可参考:计算机组成原理,博客内容如下:

计算机组成原理-计算机硬件的基本组成

计算机组成原理-计算机的功能部件及层次结构

计算机组成原理-计算机性能指标

计算机组成原理-数制与编码(进制转换)

计算机组成原理-定点数的表示和运算

计算机组成原理-浮点数的表示与运算

计算机组成原理-算术逻辑单元ALU

计算机组成原理-存储系统

计算机组成原理-高速缓冲存储器

计算机组成原理-指令系统 (指令、操作码、地址码、指令寻址、数据寻址)

计算机组成原理-总线(系统总线、总线仲裁、总线操作和定时)

Tips: 个人感觉学习完《计算机组成》和《操作系统》再去看《Java虚拟机》,也许会有更清晰的思路,而不是像大多数非科班一样直接背面筋,效果并不是很好。

资源💾

王道考研 | 链接: https://pan.baidu/s/1Dsk6KjJEwBBgNGLFWrIQDQ 密码: nmo1

英文书籍| Operating System Concepts | Operating systems: internals and design principles

现在的操作系统学习笔记📗

操作系统-处理机调度(调度层次、基本准则、先来先服务、最短作业优先、高响应比、时间片轮转、优先级调度、多级反馈队列)

操作系统-进程同步与互斥及算法实现(单标志、双标志先检查、双标志后检查、中断屏蔽、TSL指令、Swap指令、信号量机制)

操作系统-进程同步与互斥经典问题(生产者消费者、多生产者多消费者、吸烟者、读写(读优先、写优先、读写公平)、哲学家进餐)


基本定义

「死锁」,就是一个死循环,一把无法解开的锁。在多道程序系统中,进程并发执行,为争夺同一个资源,会出现每一个进程都处于锁定状态的情况,且都在等待一个事件的发生(通常是请求另外一个进程释放一些资源)来触发。但是这个事件却包含在另外一个进程中,而这个进程也处于阻塞状态,导致的结果就是整个系统中的进程无法继续推进。就如下图所示,为了抢先通过十字路口,四辆车最后却同时阻塞在十字路口中央,而每一辆车都在等待另外一辆车的谦让,否则这个僵局就会持续下去,这就是死锁。


死锁产生的原因

系统资源的竞争

可剥夺性资源

进程在获得该资源后,该资源可以被其他进程或者系统剥夺。比如优先级高的进程可以剥夺优先级低的进程所支配的处理机。内存区可以通过内存管理程序将一个进程从一个存储区转移到另外一个存储区,则转移过程就是剥夺了该进程原来占有的存储区。又比如内存调度(中级调度)将进程从主存调入到外存中。因此CPU和主存均属于「可剥夺性资源」。

引自Operating systems: internals and design principles

Examples of reusable resources include processors; I/O channels; main and secondary memory; devices; and data structures such as files, databases, and semaphores.

不可剥夺性资源

系统将该类型的资源分配给某个进程后,则不能强行的回收,只能等待进程自行释放之后,例如磁带机、打印机、摄像头等,这类资源称为「不可剥夺性资源」。

系统中所拥有的不可剥夺资源数量不足以满足多个进程运行的需要,导致进程在运行过程中,会因为争夺这种资源而出现阻塞现象。只有对于不可剥夺性资源的竞争才会出现死锁,而对于可剥夺资源的竞争是不会引起死锁的。

进程推进顺序非法

进程在运行过程中,请求和释放资源的顺序不当,例如以下两个并发进程按照顺序 p 0 , p 1 , q 0 , q 1 , p 2 , q 2 p_0, p_1, q_0, q_1, p_2, q_2 p0,p1,q0,q1,p2,q2运行就会出现死锁情况,D,T为可剥夺性资源,而且通常这是一个编程导致的错误。

死锁产生的必要条件

产生死锁必须同时满足以下四个条件,只要一个条件不成立,死锁就不会发生。

  1. 「互斥条件」:进程要求所分配的资源,在一段时间内必须仅由一个进程所占有。如果其他的进程请求该资源,则请求进程必须等待该资源的释放。
  2. 「不剥夺条件」:进程获得该资源后,只有该进程主动释放资源后,其他的进程才可以竞争该资源,不可以强行被其他进程夺走,该资源属于不可剥夺资源。
  3. 「请求并保持条件」:进程已经保持了至少一个资源,但是又需要请求新的资源,该资源又被其他的进程占有,此时请求进程就处于阻塞状态,而且自己已经持有的资源也得不到释放。
  4. 「循环等待条件」:存在一个进程资源的循环等待链,链中的每个进程在持有资源的条件下又请求另外一个进程所持有的资源。


死锁的处理策略

死锁预防

死锁预防就是,破坏死锁条件中的任意一个条件即可。

破坏互斥条件

可以将互斥使用的资源改造为允许共享使用的资源,则系统不会进入死锁状态。例如SPOOLing技术,将外部设备同时联机操作,又称之为假脱机技术,是操作系统中采用的一种可以将独占设备改造成共享设备的技术。详情可以参考:SPOOLing技术 | SPOOLing技术(假脱机技术),而SPOOLing技术主要的特点就是:

  1. 提高了I/O的速度

  2. 将独占设备改造为共享设备

  3. 实现了虚拟设备的功能

以打印机为例,它属于一种「不可剥夺性资源」,如果直接将该资源分配给进程,则其他的进程是无法共享该资源的。因此采用SPOOLing技术,用户请求打印输出时,SPOOLing系统统一为它打印,但是并不是真正立即将打印机分配给该用户进程,而是在共享设备(磁盘)上的输出。SPOOLing在存储区中为其分配一块存储空间,进程的输出数据以文件的形式留在这里。各个进程的数据输出文件形成了一个「输出队列」,由于SPOOLing系统控制这个打印机的进程,一次将队列中的输出文件实际打印输出。在SPOOLing系统中,实际上并没有为任何的进程分配,而只是在输入井和输出井中,为每个进程分配了一块存储区并建立了一张I/O请求表。通过这种方式将独占的设备改造为共享设备,是一种虚拟设备技术。

破坏不可剥夺条件

方案一

当其中一个进程的请求资源得不到满足的时候,该进程需要主动放弃所持有的资源,等以后需要的时候再次申请资源。

方案二

当某个进程需要的资源被自他的进程所占有的时候,需要通过OS的协助,将资源强行剥夺。这种方式需要考虑每个进程的优先级(剥夺调度方式,处理机强行剥夺资源给优先级更高的进程使用)。

缺点:

  1. 实现复杂。
  2. 释放已经获得的资源可能会造成前一段工作的失效。这种方法一般适用于易保存或者易恢复的资源,比如CPU。
  3. 反复的申请和释放资源会增加系统开销,降低系统的吞吐量
  4. 如果采用方案一,放弃目前拥有的资源,则该进程再次请求时,可能会一直申请不到空闲资源,导致进程饥饿。

破坏请求并保持条件

采用静态分配方法,即进程在运行的前一次就申请完它所需要的所有资源,资源未满前,不将它投入运行。一旦投入运行,则这些资源就一直归该进程所有,期间不再提出其他资源的请求,这样的话可以保证系统不会发生死锁。比如在哲学家进餐问题中,第一个哲学家可以提前拿起两个筷子,等他吃完饭,再释放手中的两只筷子,供其他的进程进行竞争。

实现简单,但是会导致资源的严重浪费。有些资源仅在进程的开始或者快结束的时候才会使用,甚至不会使用。同时也会出现饥饿现象,个别的资源长期被其他的进程所占有,导致等待该资源的进程迟迟无法推进。

破坏循环等待条件

采用顺序资源分配方法,给系统中的资源进行编号,规定每个进程必须按照编号递增的顺序请求资源同类型的即编号相同的资源一次性申请完。假设系统中一共有10个资源,分别编号为1,2,3,…,10;在任何一个时刻,总有一个进程拥有的资源编号是最大的,这个进程申请之后的资源必然畅通无阻。

对于P1,其最大的资源为2号,申请编号大于2的资源,这个资源可能被其他进程占有。

对于P2,其最大的资源为4号,申请编号大于4的资源,这个资源可能被其他进程占有。

对于P3,其最大的资源为7号,则再申请编号大于7的资源,必然畅通无阻。

整个资源的请求链是一个单向的,不会出现循环等待的情况。而这种方式存在的问题就是编号必须相对稳定,这样也限制了新设备的添加。进程实际使用资源的顺序可能和编号递增的顺序不一致,这样会导致资源的浪费。必须规定申请的资源顺序,对于用户编程来说比较麻烦。

避免死锁

避免死锁同样属于事先预防策略,并不是采取某种限制措施破坏死锁的必要条件,而是在资源动态分配过程中,防止系统进入不完全状态。

系统安全状态

进程可以动态的申请资源,但是系统在进行资源分配之前,必须先计算此次分配的安全性。如果计算所得是安全的,则允许分配,但如果是不安全的,则让进程等待。而所谓的安全状态就是,系统可以按照某种进程的推进顺序( p 1 , p 2 . . . p n p_1,p_2...p_n p1,p2...pn),可以为每个进程 p i p_i pi分配其所需的资源,直到满足该进程对资源的最大需求,使得每个进程都可以顺序完成。此时 p 1 , p 2 . . . p n p_1,p_2...p_n p1,p2...pn就是一个安全序列,但如果系统找不到这样一组安全序列,则该系统就处于不安全状态。

个人理解:在这里其实就是一种单向的请求链条,破坏了循环等待条件,和顺序资源分配不一样的是,这里是要规定进程的推进顺序,而不是资源的请求顺序。

例子:假设系统中有三个进程 p 1 , p 2 , p 3 p_1,p_2,p_3 p1,p2,p3,一共有12台磁带机。进程 p 1 p_1 p1共需要10台,进程 p 2 p_2 p2共需要4台,进程 p 1 p_1 p1共需要9台。假设在某个时刻,进程 p 1 , p 2 , p 3 p_1,p_2,p_3 p1,p2,p3已分别获得5台、2台和2台,还有3台没分配。

进程最大需求已分配可用
p 1 p_1 p11053
p 2 p_2 p242
p 3 p_3 p392

分配2个资源给 p 2 p_2 p2,满足 p 2 p_2 p2的最大需求,待 p 2 p_2 p2释放所持资源,此时空闲资源共计5个单位;

分配5个资源给 p 1 p_1 p1,满足 p 1 p_1 p1的最大需求,待 p 1 p_1 p1释放所持资源,此时空闲资源共计10个单位;

分配7个资源给 p 3 p_3 p3,满足 p 3 p_3 p3的最大需求,待 p 3 p_3 p3释放所持资源,此时空闲资源共计12个单位;

最后得到安全序列 p 2 , p 1 , p 3 p_2,p_1,p_3 p2,p1,p3,这种情况下不会发生死锁。

但是如果先给 p 3 p_3 p3分配一个资源,此时系统进入不安全状态

之后再将剩下的两个资源分配给 p 2 p_2 p2,等待 p 2 p_2 p2进程运行完毕,回收其资源,此时空闲资源共有4个单位。此时4个单元无法满足 p 1 p_1 p1或者 p 3 p_3 p3,则此时无法再找到一个安全序列,陷入僵局,最终导致死锁。


并不是所有的不安全状态都是死锁状态,但是当系统进入不安全状态的时候,就有可能出现死锁情况。反之,如果系统处于安全状态,系统便可以找到安全序列,避免死锁状态。

银行家算法

理论部分不再赘述,在很多的博客都有记载,可参考计算机操作系统_银行家算法,不过光看理论有点晕,道理其实很简单,就是遍历所有的进程,比对当前的空闲资源数量和该进程仍然需要的资源数,判断是否满足最大需求,满足则将这个进程加入安全序列,更新回收进程释放的资源,不满足则跳过该进程,依次循环检测。

图解说明

此时系统中包含三种资源,分别用红、橙、蓝表示。遍历查找符合条件的进程,将该进程加入到安全序列。安全序列并不唯一,还需要考虑遍历的方式、以及进程的优先级、调度算法等等。

死锁检测及解除

死锁预防和避免算法,其实都是在进程分配资源的时候试加限制条件或者检测,但是如果系统为进程分配资源时不采取任何措施,则应该提供死锁检测和解除的手段。

死锁检测

为了能对系统是否已发生了死锁进行检测,必须:

  1. 用某种数据结构来保存资源的请求和分配信息。
  2. 提供一种算法,利用上述信息来检测系统是否已进入死锁状态。
资源分配图

死锁的检测可以利用资源分配图来分析,该数据结构包含如下的内容:

检测死锁的算法

在资源分配图中,找出既不阻塞(请求资源节点的数量足够)又不是孤点的进程 p i p_i pi,该请求边所申请的数量小于等于下同已有的空闲资源数量。所有的连接该进程的边均满足上述的条件,则这个进程就可以运行直至完成。然后释放自己拥有的资源,消除进程的请求边和分配边,使后释放自己拥有的资源,消除进程的请求边和分配边之成为孤立的节点。如果所有的节点可以被消除与其相连的边,则成为该图是可完全简化的,而且一定不会发生死锁。

对于可以消除所有的边,则称这个图是可以简化的,则一定没有发生死锁。

而如果最终不能消除所有边,那么就一定发生了死锁。

死锁消除

一旦检测出死锁的发生,就应该立刻解除死锁,死锁的检测就是通过简化资源分配图。解除死锁的主要方法:

  1. 「资源剥夺法」。通过内存调度将死锁进程转移至外存中挂起,抢占其资源,并将这些资源分配给其他的死锁进程。
  2. 「撤销进程法(或终止进程法)」。强制杀死该进程,剥夺这些进程的资源。虽然实现简单,但是付出的代价可能会很大,部分进程很可能运行了很长时间,但是被杀之后,功亏一篑。
  3. 「进程回退法」。让一个或者多个死锁进程回退到足以避免死锁的地步。这要求系统要记录进程的历史信息,设置还原点。

本文标签: 死锁 银行家 序列 算法 操作系统