## **一、死锁的概念**
在许多应用中进程需要以**独占**的方式访问资源,当操作系统允许多个进程并发执行时可能会出现进程永远被阻塞现象,如两个进程分别等待对方所占的资源,于是两者都不能执行而**处于永远等待状态**,此现象称为**死锁**。
> 死锁通常被定义为:如果一个进程集合中的每个进程都在等待只能由此集合中的其他进程才能引发的事件,而无限期陷入僵持的局面称为死锁。
## **二、死锁产生的条件**
* 互斥条件
临界资源是独占资源,进程应互斥且排他的使用这些资源。
* 占有和等待条件
进程在请求资源得不到满足而等待时,不释放已占有资源。
* 不剥夺条件
又称不可抢占,已获资源只能由进程自愿释放,不允许被其他进程剥夺。
* 循环等待条件
又称环路条件,存在循环等待链,其中,每个进程都在等待链中等待下一个进程所持有的资源,造成这组进程处于永远等待状态。
**死锁只有在这四个条件同时满足时出现。**
## **三、死锁产生的原因**
死锁产生的原因有很多,如:
* 进程顺序不当
* PV操作使用不妥
* 同类资源分配不均
* 对某些资源的使用未加限制
等等。
> 可见,产生死锁的原因不仅与系统拥有的资源数量有关,而且与资源分配策略、进程对资源的使用要求以及进程的推进顺序有关。
## **四、死锁解决的方法**
主要有一下三种方法:
* 死锁防止
* 死锁避免
* 死锁检测和恢复
## **4.1 死锁防止**
**在程序运行之前防止发生死锁。**
前面说了死锁产生的条件有四个,分别是:互斥条件、占有和等待条件、不剥夺条件、循环等待条件。
**而死锁防止的策略就是至少破坏这四个条件其中一项。**
### **4.1.1 破坏互斥条件**
使资源同时访问而非互斥使用,就没有进程会阻塞在资源上,从而不发生死锁。
> 只读数据文件、磁盘等软硬件资源均可采用这种办法管理;
> 但是许多资源是独占性资源,如可写文件、键盘等只能互斥的占有;
> 所以这种做法在许多场合是不适用的。
### **4.1.2 破坏占有和等待条件**
采用静态分配的方式,静态分配的方式是指进程必须在执行之前就申请需要的全部资源,且直至所要的资源全部得到满足后才开始执行。
> 实现简单,但是严重的减低了资源利用率。
> 因为在每个进程占有的资源中,有些资源在运行后期使用,有些资源在例外情况下才被使用,可能会造成进程占有一些几乎用不到的资源,而使其他想使用这些资源的进程等待。
### **4.1.3 破坏不剥夺条件**
剥夺调度能够防止死锁,但是只适用于内存和处理器资源。
方法一:占有资源的进程若要申请新资源,必须主动释放已占有资源,若需要此资源,应该向系统重新申请。
方法二:资源分配管理程序为进程分配新资源时,若有则分配;否则将剥夺此进程已占有的全部资源,并让进程进入等待资源状态,资源充足后再唤醒它重新申请所有所需资源。
### **4.1.4 破坏循环等待条件**
给系统的所有资源编号,规定进程请求所需资源的顺序必须按照资源的编号依次进行。
> 采用层次分配策略,将系统中所有的资源排列到不同层次中
* 一个进程得到某层的一个资源后,只能申请较高一层的资源
* 当进程释放某层的一个资源时,必须先释放所占有的较高层的资源
* 当进程获得某层的一个资源时,如果想申请同层的另一个资源,必须先释放此层中已占有的资源
## **4.2死锁避免**
> 各种死锁防止方法能够防止发生死锁,但必然会降低系统并发性,导致低效的资源利用率。
**在程序运行时避免发生死锁。**
### **4.2.1 安全状态**
![](https://pic2.zhimg.com/80/v2-72ae2b390bb3628b373265661676079d_720w.jpg)
图 a 的第二列 Has 表示已拥有的资源数,第三列 Max 表示总共需要的资源数,Free 表示还有可以使用的资源数。从图 a 开始出发,先让 B 拥有所需的所有资源(图 b),运行结束后释放 B,此时 Free 变为 5(图 c);接着以同样的方式运行 C 和 A,使得所有进程都能成功运行,因此可以称图 a 所示的状态时安全的。
定义:如果没有死锁发生,并且即使所有进程突然请求对资源的最大需求,也仍然存在某种调度次序能够使得每一个进程运行完毕,则称该状态是安全的。
安全状态的检测与死锁的检测类似,因为安全状态必须要求不能发生死锁。下面的银行家算法与死锁检测算法非常类似,可以结合着做参考对比。
### **4.2.2单个资源的银行家算法**
一个小城镇的银行家,他向一群客户分别承诺了一定的贷款额度,算法要做的是判断对请求的满足是否会进入不安全状态,如果是,就拒绝请求;否则予以分配。
![](https://pic4.zhimg.com/80/v2-849e1f710f9a7cf648c07c69f81a5da3_720w.jpg)
上图 c 为不安全状态,因此算法会拒绝之前的请求,从而避免进入图 c 中的状态。
### **4.2.3 多个资源的银行家算法**
![](https://pic3.zhimg.com/80/v2-7ae7ff0a84197797b63e38184a466fc6_720w.jpg)
上图中有五个进程,四个资源。左边的图表示已经分配的资源,右边的图表示还需要分配的资源。最右边的 E、P 以及 A 分别表示:总资源、已分配资源以及可用资源,注意这三个为向量,而不是具体数值,例如 A=(1020),表示 4 个资源分别还剩下 1/0/2/0。
检查一个状态是否安全的算法如下:
* 查找右边的矩阵是否存在一行小于等于向量 A。如果不存在这样的行,那么系统将会发生死锁,状态是不安全的。
* 假若找到这样一行,将该进程标记为终止,并将其已分配资源加到 A 中。
* 重复以上两步,直到所有进程都标记为终止,则状态时安全的。
如果一个状态不是安全的,需要拒绝进入这个状态。
## **4.3 死锁检测和恢复**
> 对资源的分配加以适当限制可防止或避免死锁发生,但不利于进程对系统资源的充分共享。
**不试图阻止死锁,而是当检测到死锁发生时,采取措施进行恢复。**
> 如果进程 - 资源分配图中无环路,此时系统没有发生死锁。
> 如果进程 - 资源分配图中有环路,则可分为以下两种情况:
* 每种资源类中仅有一个资源,则系统发生了死锁。此时,环路是系统发生死锁的**充分必要条件**,环路中的进程就是死锁进程。
* 每种资源类中有多个资源,则环路的存在只是产生死锁的**必要不充分条件**,系统未必会发生死锁。
**下面详细说一下这两种情况:**
### **4.3.1 每种资源类中仅有一个资源的死锁检测**
![](https://pic2.zhimg.com/80/v2-15d05471cb6d97558eac7c026f2fc7d5_720w.jpg)
上图为资源分配图,其中方框表示资源,圆圈表示进程。资源指向进程表示该资源已经分配给该进程,进程指向资源表示进程请求获取该资源。
图 a 可以抽取出环,如图 b,它满足了环路等待条件,因此会发生死锁。
> 比如:进程D需要的资源是U、T;进程G需要的资源是V、U;进程E需要的资源是T、V
> 此时进程D占有资源U,进程E占有资源T,进程G占有资源V
> 所以此时导致进程D、E、G所申请的资源不能得到全部满足,陷入死锁。
**死锁检测算法:**
每种类型一个资源的死锁检测算法是通过检测有向图是否存在环来实现,从一个节点出发进行深度优先搜索,对访问过的节点进行标记,如果访问了已经标记的节点,就表示有向图存在环,也就是检测到死锁的发生。
### **4.3.2 每种资源类中有多个资源的死锁检测**
![](https://pic1.zhimg.com/80/v2-b7e8ba213e6589be48c923a4b0bb43b4_720w.jpg)
每个资源类用一个方框表示,方框中的原点表示此资源类中的各个资源;
每个进程用一个圆圈来表示,用有向边表示进程申请资源和资源分配情况。
约定方框→圆圈表示资源分配,圆圈→方框表示申请资源。
这种情况下,图3-6 发生了死锁,而图3-7没有发生死锁。
![](https://pic2.zhimg.com/80/v2-7f7aaa0aaa4d147df95c9a931b146809_720w.jpg)
**死锁检测算法:**
![](https://pic2.zhimg.com/80/v2-fda5b7573cc9383034d75d8993277ed5_720w.jpg)
上图中,有三个进程四个资源,每个数据代表的含义如下:
* E 向量:资源总量
* A 向量:资源剩余量
* C 矩阵:每个进程所拥有的资源数量,每一行都代表一个进程拥有资源的数量
* R 矩阵:每个进程请求的资源数量
进程 P1 和 P2 所请求的资源都得不到满足,只有进程 P3 可以,让 P3 执行,之后释放 P3 拥有的资源,此时 A = (2 2 2 0)。P2 可以执行,执行后释放 P2 拥有的资源,A = (4 2 2 1) 。P1 也可以执行。所有进程都可以顺利执行,没有死锁。
算法总结如下:
每个进程最开始时都不被标记,执行过程有可能被标记。当算法结束时,任何没有被标记的进程都是死锁进程。
1. 寻找一个没有标记的进程 Pi,它所请求的资源小于等于 A。
2. 如果找到了这样一个进程,那么将 C 矩阵的第 i 行向量加到 A 中,标记该进程,并转回 1。
3. 如果没有这样一个进程,算法终止。
### **4.3.3 死锁恢复**
* 资源剥夺法
剥夺陷于死锁的进程所占用的资源,但并不撤销此进程,直至死锁解除。
* 进程回退法
根据系统保存的检查点让所有的进程回退,直到足以解除死锁,这种措施要求系统建立保存检查点、回退及重启机制。
* 进程撤销法
* 撤销陷入死锁的所有进程,解除死锁,继续运行。
* 逐个撤销陷入死锁的进程,回收其资源并重新分配,直至死锁解除。
可选择符合下面条件之一的先撤销: 1.CPU消耗时间最少者 2.产生的输出量最小者
3.预计剩余执行时间最长者 4.分得的资源数量最少者后优先级最低者
* 系统重启法
结束所有进程的执行并重新启动操作系统。这种方法很简单,但先前的工作全部作废,损失很大。
- PHP篇
- 函数传值和传引用的区别
- 简述PHP的垃圾回收机制
- 简述CGI、FAST-CGI、PHP-FPM的关系
- 常见正则表达式
- 多进程写文件,如何保证都写成功
- php支持回调函数的数组函数
- MySQL篇
- MySQL的两种存储引擎区别
- 事务的四大特性
- 数据库事务隔离级别
- 什么是索引
- 索引有哪些数据结构,优缺点
- 索引的一些潜规则
- SQL的优化方案
- 简述MySQL的锁机制
- 死锁是怎么产生的?怎么解决?
- 简述MySQL的主从复制过程,延迟问题怎么解决
- 分布式事务的解决方案
- 数据库中间件MyCat
- Linux篇
- Linux常用命令
- 对日志文件的IP出现的次数进行统计,并显示次数最多的前5名
- WEB篇
- 跨域是怎么产生的,如何解决跨域
- Redis篇
- redis介绍
- redis和memcached区别
- redis的持久化方案
- 缓存穿透、击穿、雪崩、预热、更新、降级
- 网络篇
- 计算机网络体系结构
- 简述TCP的三次握手、四次挥手过程
- UDP、TCP 区别,适用场景
- HTTP常见状态码含义
- 设计模式篇
- 单例模式
- 简单工厂模式
- 抽象工厂模式
- 观察者模式
- 策略模式
- 注册模式
- 适配器模式
- 安全篇
- 跨站脚本攻击(XSS)
- 跨站点请求伪造(CSRF)
- SQL 注入
- 应用层拒绝服务攻击
- PHP安全
- 运维篇
- docker面试题
- 消息队列篇
- 架构篇
- 数据结构与算法篇