[docs update]操作系统-死锁

This commit is contained in:
guide 2022-03-03 15:35:16 +08:00
parent 8437c0f7b0
commit b44ec42cb5
1 changed files with 40 additions and 60 deletions

View File

@ -5,17 +5,15 @@ tag:
- 操作系统
---
大家好,我是 Guide 哥!
很多读者抱怨计算操作系统的知识点比较繁杂,自己也没有多少耐心去看,但是面试的时候又经常会遇到。所以,我带着我整理好的操作系统的常见问题来啦!这篇文章总结了一些我觉得比较重要的操作系统相关的问题比如**进程管理**、**内存管理**、**虚拟内存**等等。
文章形式通过大部分比较喜欢的面试官和求职者之间的对话形式展开。另外Guide哥 也只是在大学的时候学习过操作系统,不过基本都忘了,为了写这篇文章这段时间看了很多相关的书籍和博客。如果文中有任何需要补充和完善的地方,你都可以在 issue 中指出!
文章形式通过大部分比较喜欢的面试官和求职者之间的对话形式展开。另外Guide 哥 也只是在大学的时候学习过操作系统,不过基本都忘了,为了写这篇文章这段时间看了很多相关的书籍和博客。如果文中有任何需要补充和完善的地方,你都可以在 issue 中指出!
这篇文章只是对一些操作系统比较重要概念的一个概览,深入学习的话,建议大家还是老老实实地去看书。另外, 这篇文章的很多内容参考了《现代操作系统》第三版这本书,非常感谢。
开始本文的内容之前,我们先聊聊为什么要学习操作系统。
- **从对个人能力方面提升来说** :操作系统中的很多思想、很多经典的算法,你都可以在我们日常开发使用的各种工具或者框架中找到它们的影子。比如说我们开发的系统使用的缓存(比如 Redis和操作系统的高速缓存就很像。CPU 中的高速缓存有很多种,不过大部分都是为了解决 CPU 处理速度和内存处理速度不对等的问题。我们还可以把内存看作外存的高速缓存,程序运行的时候我们把外存的数据复制到内存,由于内存的处理速度远远高于外存,这样提高了处理速度。同样地,我们使用的 Redis 缓存就是为了解决程序处理速度和访问常规关系型数据库速度不对等的问题。高速缓存一般会按照局部性原理2-8 原则)根据相应的淘汰算法保证缓存中的数据是经常会被访问的。我们平常使用的 Redis 缓存很多时候也会按照 2-8 原则去做,很多淘汰算法都和操作系统中的类似。既说了 2-8 原则,那就不得不提命中率了,这是所有缓存概念都通用的。简单来说也就是你要访问的数据有多少能直接在缓存中直接找到。命中率高的话,一般表明你的缓存设计比较合理,系统处理速度也相对较快。
- **从对个人能力方面提升来说** :操作系统中的很多思想、很多经典的算法,你都可以在我们日常开发使用的各种工具或者框架中找到它们的影子。比如说我们开发的系统使用的缓存(比如 Redis和操作系统的高速缓存就很像。CPU 中的高速缓存有很多种,不过大部分都是为了解决 CPU 处理速度和内存处理速度不对等的问题。我们还可以把内存看作外存的高速缓存,程序运行的时候我们把外存的数据复制到内存,由于内存的处理速度远远高于外存,这样提高了处理速度。同样地,我们使用的 Redis 缓存就是为了解决程序处理速度和访问常规关系型数据库速度不对等的问题。高速缓存一般会按照局部性原理2-8 原则)根据相应的淘汰算法保证缓存中的数据是经常会被访问的。我们平常使用的 Redis 缓存很多时候也会按照 2-8 原则去做,很多淘汰算法都和操作系统中的类似。既说了 2-8 原则,那就不得不提命中率了,这是所有缓存概念都通用的。简单来说也就是你要访问的数据有多少能直接在缓存中直接找到。命中率高的话,一般表明你的缓存设计比较合理,系统处理速度也相对较快。
- **从面试角度来说** :尤其是校招,对于操作系统方面知识的考察是非常非常多的。
**简单来说,学习操作系统能够提高自己思考的深度以及对技术的理解力,并且,操作系统方面的知识也是面试必备。**
@ -33,7 +31,7 @@ tag:
🙋 **我** :我通过以下四点向您介绍一下什么是操作系统吧!
1. **操作系统Operating System简称 OS是管理计算机硬件与软件资源的程序是计算机的基石。**
2. **操作系统本质上是一个运行在计算机上的软件程序 ,用于管理计算机硬件和软件资源。** 举例:运行在你电脑上的所有应用程序都通过操作系统来调用系统内存以及磁盘等等硬件。
2. **操作系统本质上是一个运行在计算机上的软件程序 ,用于管理计算机硬件和软件资源。** 举例:运行在你电脑上的所有应用程序都通过操作系统来调用系统内存以及磁盘等等硬件。
3. **操作系统存在屏蔽了硬件层的复杂性。** 操作系统就像是硬件使用的负责人,统筹着各种相关事项。
4. **操作系统的内核Kernel是操作系统的核心部分它负责系统的内存管理硬件设备的管理文件系统的管理以及应用程序的管理**。 内核是连接应用程序和硬件的桥梁,决定着系统的性能和稳定性。
@ -149,14 +147,12 @@ tag:
- **互斥**:资源必须处于非共享模式,即一次只有一个进程可以使用。如果另一进程申请该资源,那么必须等待直到该资源被释放为止。
- **占有并等待**:一个进程至少应该占有一个资源,并等待另一资源,而该资源被其他进程所占有。
- **非抢占**:资源不能被抢占。只能在持有资源的进程完成任务后,该资源才会被释放。
- **循环等待**:有一组等待进程 `{P0, P1,..., Pn}` `P0` 等待的资源被 `P1` 占有,`P1` 等待的资源被 `P2` 占有,......`Pn-1` 等待的资源被 `Pn` 占有,`Pn` 等待的资源被 `P0` 占有。
- **循环等待**:有一组等待进程 `{P0, P1,..., Pn}` `P0` 等待的资源被 `P1` 占有,`P1` 等待的资源被 `P2` 占有,......`Pn-1` 等待的资源被 `Pn` 占有,`Pn` 等待的资源被 `P0` 占有。
注意,只有四个条件同时成立时,死锁才会出现。
### 2.8 解决死锁的方法
#### 那么解决死锁的方法是什么呢?
解决死锁的方法可以从多个角度去分析,一般的情况下,有**预防,避免,检测和解除四种**。
- **预防** 是采用某种策略,**限制并发进程对资源的请求**,从而使得死锁的必要条件在系统执行的任何时间上都不满足。
@ -168,77 +164,74 @@ tag:
#### 死锁的预防
死锁四大必要条件上面都已经列出来了,很显然,`只要破坏四个必要条件中的任何一个就能够预防死锁的发生`
死锁四大必要条件上面都已经列出来了,很显然,只要破坏四个必要条件中的任何一个就能够预防死锁的发生。
破坏第一个条件 **互斥条件**:使得资源是可以同时访问的,这是种简单的方法,磁盘就可以用这种方法管理,但是我们要知道,有很多资源**往往是不能同时访问的**,所以这种做法在大多数的场合是行不通的。
破坏第一个条件 **互斥条件**:使得资源是可以同时访问的,这是种简单的方法,磁盘就可以用这种方法管理,但是我们要知道,有很多资源 **往往是不能同时访问的** ,所以这种做法在大多数的场合是行不通的。
破坏第三个条件**非抢占**:也就是说可以采用**剥夺式**调度算法,但剥夺调度方法目前一般仅适用于**主存资源**和**处理器资源**的分配,并不适用于所以的资源,会导致**资源利用率下降**。
破坏第三个条件 **非抢占** :也就是说可以采用 **剥夺式调度算法**,但剥夺调度方法目前一般仅适用于 **主存资源** **处理器资源** 的分配,并不适用于所以的资源,会导致 **资源利用率下降**
所以一般比较实用的**预防死锁的方法**,是通过考虑破坏第二个条件和第四个条件。
所以一般比较实用的 **预防死锁的方法**,是通过考虑破坏第二个条件和第四个条件。
- **静态分配策略**:所谓静态分配策略,就是指一个进程必须在执行前就申请到**它所需要的全部资源**,并且知道它所要的资源都得到满足之后才开始执行。这种做法破坏了死锁产生的第二个条件**占有并等待**,进程要么占有所有的资源然后开始执行,要么不占有资源,不会出现占有一些资源等待一些资源的情况。
静态分配策略逻辑简单,实现也很容易,但这种策略**严重地降低了资源利用率**,因为在每个进程所占有的资源中,有些资源是在比较靠后的执行时间里采用的,甚至有些资源是在**额外的情况**下才是用的,这样就可能造成了一个进程占有了一些**几乎不用的资源而使其他需要该资源的进程产生等待**的情况。
**1、静态分配策略**
- **层次分配策略**:层次分配策略破坏了产生死锁的第四个条件(**循环等待**)。在层次分配策略下,所有的资源被分成了多个层次,一个进程得到某一次的一个资源后,**它只能再申请较高一层的资源**;当一个进程要释放某层的一个资源时,必须先释放所占用的**较高层的资源**,按这种策略,是不可能出现**循环等待链的**,因为那样的话,就出现了已经申请了较高层的资源,反而去申请了较低层的资源,不符合**层次分配策略**,证明略。
静态分配策略可以破坏死锁产生的第二个条件(占有并等待)。所谓静态分配策略,就是指一个进程必须在执行前就申请到它所需要的全部资源,并且知道它所要的资源都得到满足之后才开始执行。进程要么占有所有的资源然后开始执行,要么不占有资源,不会出现占有一些资源等待一些资源的情况。
静态分配策略逻辑简单,实现也很容易,但这种策略 **严重地降低了资源利用率**,因为在每个进程所占有的资源中,有些资源是在比较靠后的执行时间里采用的,甚至有些资源是在额外的情况下才是用的,这样就可能造成了一个进程占有了一些 **几乎不用的资源而使其他需要该资源的进程产生等待** 的情况。
**2、层次分配策略**
层次分配策略破坏了产生死锁的第四个条件(循环等待)。在层次分配策略下,所有的资源被分成了多个层次,一个进程得到某一次的一个资源后,它只能再申请较高一层的资源;当一个进程要释放某层的一个资源时,必须先释放所占用的较高层的资源,按这种策略,是不可能出现循环等待链的,因为那样的话,就出现了已经申请了较高层的资源,反而去申请了较低层的资源,不符合层次分配策略,证明略。
#### 死锁的避免
上面提到的**破坏**死锁产生的四个必要条件之一就可以成功**预防系统发生死锁**,但是会导致**低效的进程运行**和**资源使用率**。而死锁的避免相反,它的角度是允许系统中**同时存在四个必要条件**,只要掌握并发进程中与每个进程有关的资源动态申请情况,做出**明智和合理的选择**,仍然可以避免死锁,因为四大条件仅仅是产生死锁的必要条件。
上面提到的 **破坏** 死锁产生的四个必要条件之一就可以成功 **预防系统发生死锁** ,但是会导致 **低效的进程运行** **资源使用率** 。而死锁的避免相反,它的角度是允许系统中**同时存在四个必要条件** ,只要掌握并发进程中与每个进程有关的资源动态申请情况,做出 **明智和合理的选择** ,仍然可以避免死锁,因为四大条件仅仅是产生死锁的必要条件。
我们将系统的状态分为**安全状态**和**不安全状态**,每当在未申请者分配资源前先测试系统状态,若把系统资源分配给申请者会产生死锁,则拒绝分配,否则接受申请,并为它分配资源。
我们将系统的状态分为 **安全状态** **不安全状态** ,每当在未申请者分配资源前先测试系统状态,若把系统资源分配给申请者会产生死锁,则拒绝分配,否则接受申请,并为它分配资源。
`安全状态`:如果操作系统能够保证**所有的进程在有限的时间内得到需要的全部资源**,则称系统处于安全状态,否则说系统是不安全的。很显然,`系统处于安全状态则不会发生死锁,系统若处于不安全状态则可能发生死锁`
> 如果操作系统能够保证所有的进程在有限的时间内得到需要的全部资源,则称系统处于安全状态,否则说系统是不安全的。很显然,系统处于安全状态则不会发生死锁,系统若处于不安全状态则可能发生死锁。
那么如何保证系统保持在安全状态呢?通过算法,其中最具有代表性的**避免死锁算法**就是Dijkstra的银行家算法银行家算法用一句话表达就是,“当一个进程申请使用资源的时候,**银行家算法**通过先**试探**分配给该进程资源,然后通过**安全性算法**判断分配后系统是否处于安全状态,若不安全则试探分配作废,让该进程继续等待,若能够进入到安全的状态,则就**真的分配资源给该进程**”
那么如何保证系统保持在安全状态呢?通过算法,其中最具有代表性的 **避免死锁算法** 就是 Dijkstra 的银行家算法,银行家算法用一句话表达就是当一个进程申请使用资源的时候,**银行家算法** 通过先 **试探** 分配给该进程资源,然后通过 **安全性算法** 判断分配后系统是否处于安全状态,若不安全则试探分配作废,让该进程继续等待,若能够进入到安全的状态,则就 **真的分配资源给该进程**
银行家算法详情可见[(41条消息) 一句话+一张图说清楚——银行家算法_土豆洋芋山药蛋的博客-CSDN博客_银行家算法](https://blog.csdn.net/qq_33414271/article/details/80245715)
银行家算法详情可见[《一句话+一张图说清楚——银行家算法》](https://blog.csdn.net/qq_33414271/article/details/80245715)
操作系统教程树中讲述的银行家算法也比较清晰,可以一看.
死锁的避免(银行家算法)改善解决了**资源使用率低的问题**,但是它要不断地检测每个进程对各类资源的占用和申请情况,以及做**安全性检查**,需要花费较多的时间。
死锁的避免(银行家算法)改善解决了 **资源使用率低的问题** ,但是它要不断地检测每个进程对各类资源的占用和申请情况,以及做 **安全性检查** ,需要花费较多的时间。
#### 死锁的检测
对资源的分配加以限制可以**预防和避免**死锁的发生,但是都不利于各进程对系统资源的**充分共享**。解决死锁问题的另一条途径是**死锁检测和解除**(这里突然联想到了乐观锁和悲观锁,感觉死锁的检测和解除就像是**乐观锁**,分配资源时不去提前管会不会发生死锁了,等到真的死锁出现了再来解决嘛,而**死锁的预防和避免**更像是悲观锁,总是觉得死锁会出现,所以在分配资源的时候就很谨慎)。
对资源的分配加以限制可以 **预防和避免** 死锁的发生,但是都不利于各进程对系统资源的**充分共享**。解决死锁问题的另一条途径是 **死锁检测和解除** (这里突然联想到了乐观锁和悲观锁,感觉死锁的检测和解除就像是 **乐观锁** ,分配资源时不去提前管会不会发生死锁了,等到真的死锁出现了再来解决嘛,而 **死锁的预防和避免** 更像是悲观锁,总是觉得死锁会出现,所以在分配资源的时候就很谨慎)。
这种方法对资源的分配不加以任何限制,也不采取死锁避免措施,但系统**定时地运行一个 “死锁检测”**的程序,判断系统内是否出现死锁,如果检测到系统发生了死锁,再采取措施去解除它。
这种方法对资源的分配不加以任何限制,也不采取死锁避免措施,但系统 **定时地运行一个 “死锁检测”** 的程序,判断系统内是否出现死锁,如果检测到系统发生了死锁,再采取措施去解除它。
##### 进程-资源分配图
操作系统中的每一刻时刻的**系统状态**都可以用**进程-资源分配图**来表示,进程-资源分配图是描述进程和资源申请及分配关系的一种有向图,可用于**检测系统是否处于死锁状态**。
用一个方框表示每一个资源类,方框中的黑点表示该资源类中的各个资源,每个键进程用一个圆圈表示,用**有向边**来表示**进程申请资源和资源被分配的情况**。约定$P_{i} \rightarrow R_{j}$为请求边,表示线程$P_{i}$申请资源类$R_{j}$中的一个资源**得不到满足**而处于等待$R_{j}$类资源的状态,该有向边从进程开始指到方框的边缘,表示进程$P_{i}$申请$R_{j}$类中的一个资源。反之,$R_{j}\rightarrow P_{i}$为**分配边**表示R的一个资源分配给了P。
用一个方框表示每一个资源类,方框中的黑点表示该资源类中的各个资源,每个键进程用一个圆圈表示,用 **有向边** 来表示**进程申请资源和资源被分配的情况**。
图中2-21是**进程-资源分配图**的一个例子,其中有三个资源类,每个进程的资源占有和申请情况已清楚地表示在图中。在这个例子中,由于存在**占有和等待资源的环路**,导致一组进程永远处于等待资源的状态,**发生了死锁**。
图中 2-21 是**进程-资源分配图**的一个例子,其中有三个资源类,每个进程的资源占有和申请情况已清楚地表示在图中。在这个例子中,由于存在 **占有和等待资源的环路** ,导致一组进程永远处于等待资源的状态,发生了 **死锁**
<img src="https://github.com/zhengjianda/JavaGuide/blob/main/docs/cs-basics/operating-system/images/%E8%BF%9B%E7%A8%8B-%E8%B5%84%E6%BA%90%E5%88%86%E9%85%8D%E5%9B%BE.jpg" height="300"></img>
![进程-资源分配图](./images/进程-资源分配图.jpg)
进程-资源分配图中存在环路并不一定是发生了死锁。因为循环等待资源仅仅是死锁发生的必要条件,而不是充分条件。图 2-22 便是一个有环路而无死锁的例子。虽然进程 P1 和进程 P3 分别占用了一个资源 R1 和一个资源 R2并且因为等待另一个资源 R2 和另一个资源 R1 形成了环路,但进程 P2 和进程 P4 分别占有了一个资源 R1 和一个资源 R2它们申请的资源得到了满足在有限的时间里会归还资源于是进程 P1 或 P3 都能获得另一个所需的资源,环路自动解除,系统也就不存在死锁状态了。
进程-资源分配图中存在环路**并不一定是**发生了死锁。因为循环等待资源仅仅是死锁发生的必要条件而不是充分条件。图2-22便是一个有环路而无死锁的例子。虽然进程P1和进程P3分别占用了一个资源R1和一个资源R2并且因为等待另一个资源R2和另一个资源R1形成了环路但进程P2和进程P4分别占有了一个资源R1和一个资源R2它们申请的资源得到了满足在有限的时间里会归还资源于是进程P1或P3都能获得另一个所需的资源环路自动解除系统也就不存在死锁状态了。
##### 死锁检测步骤
##### 死锁检测步骤:
知道了死锁检测的原理,我们可以利用下列步骤编写一个 **死锁检测** 程序,检测系统是否产生了死锁。
知道了死锁检测的原理,我们可以利用下列步骤编写一个**死锁检测**程序,检测系统是否产生了死锁。
1) 如果进程-资源分配图中无环路,则此时系统没有发生死锁
2) 如果进程-资源分配图中有环路,且每个资源类仅有一个资源,则系统中已经发生了死锁。
3) 如果进程-资源分配图中有环路,且涉及到的资源类有多个资源,此时系统未必会发生死锁。如果能在进程-资源分配图中找出一个**既不阻塞又非独立的进程**,该进程能够在有限的时间内归还占有的资源,也就是把边给消除掉了,重复此过程,直到能在有限的时间内**消除所有的边**,则不会发生死锁,否则会发生死锁。(消除边的过程类似于**拓扑排序**)
1. 如果进程-资源分配图中无环路,则此时系统没有发生死锁
2. 如果进程-资源分配图中有环路,且每个资源类仅有一个资源,则系统中已经发生了死锁。
3. 如果进程-资源分配图中有环路,且涉及到的资源类有多个资源,此时系统未必会发生死锁。如果能在进程-资源分配图中找出一个 **既不阻塞又非独立的进程** ,该进程能够在有限的时间内归还占有的资源,也就是把边给消除掉了,重复此过程,直到能在有限的时间内 **消除所有的边** ,则不会发生死锁,否则会发生死锁。(消除边的过程类似于 **拓扑排序**)
#### 死锁的解除
当死锁检测程序检测到**存在死锁发生时**,应设法让其解除,让系统从死锁状态中恢复过来,常用的解除死锁的方法有以下四种:
当死锁检测程序检测到存在死锁发生时,应设法让其解除,让系统从死锁状态中恢复过来,常用的解除死锁的方法有以下四种:
1. **立即结束所有进程的执行,重新启动操作系统**
这种方法简单,但以前所在的工作全部作废,损失很大。
2. **撤销涉及死锁的所有进程**,解除死锁后继续运行
这种方法能彻底打破**死锁的循环等待**条件,但将付出很大代价,例如有些进程可能已经计算了很长时间,由于被撤销而使产生的部分结果也被消除了,再重新执行时还要再次进行计算。
3. 逐个撤销涉及死锁的进程,回收其资源直至死锁解除。
4. 抢占资源:从涉及死锁的**一个或几个进程**中抢占资源,把夺得的资源再分配给涉及死锁的进程直至死锁解除。
1. **立即结束所有进程的执行,重新启动操作系统** :这种方法简单,但以前所在的工作全部作废,损失很大。
2. **撤销涉及死锁的所有进程,解除死锁后继续运行** :这种方法能彻底打破**死锁的循环等待**条件,但将付出很大代价,例如有些进程可能已经计算了很长时间,由于被撤销而使产生的部分结果也被消除了,再重新执行时还要再次进行计算。
3. **逐个撤销涉及死锁的进程,回收其资源直至死锁解除。**
4. **抢占资源** :从涉及死锁的一个或几个进程中抢占资源,把夺得的资源再分配给涉及死锁的进程直至死锁解除。
## 三 操作系统内存管理基础
@ -429,8 +422,8 @@ tag:
- **OPT 页面置换算法(最佳页面置换算法)** :最佳(Optimal, OPT)置换算法所选择的被淘汰页面将是以后永不使用的,或者是在最长时间内不再被访问的页面,这样可以保证获得最低的缺页率。但由于人们目前无法预知进程在内存下的若千页面中哪个是未来最长时间内不再被访问的,因而该算法无法实现。一般作为衡量其他置换算法的方法。
- **FIFOFirst In First Out 页面置换算法(先进先出页面置换算法)** : 总是淘汰最先进入内存的页面,即选择在内存中驻留时间最久的页面进行淘汰。
- **LRU Least Recently Used页面置换算法最近最久未使用页面置换算法** LRU算法赋予每个页面一个访问字段用来记录一个页面自上次被访问以来所经历的时间 T当须淘汰一个页面时选择现有页面中其 T 值最大的,即最近最久未使用的页面予以淘汰。
- **LFU Least Frequently Used页面置换算法最少使用页面置换算法** : 该置换算法选择在之前时期使用最少的页面作为淘汰页。
- **LRU Least Recently Used页面置换算法最近最久未使用页面置换算法** LRU 算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间 T当须淘汰一个页面时选择现有页面中其 T 值最大的,即最近最久未使用的页面予以淘汰。
- **LFU Least Frequently Used页面置换算法最少使用页面置换算法** : 该置换算法选择在之前时期使用最少的页面作为淘汰页。
## Reference
@ -443,16 +436,3 @@ tag:
- <https://www.geeksforgeeks.org/interprocess-communication-methods/>
- <https://juejin.im/post/59f8691b51882534af254317>
- 王道考研操作系统知识点整理: https://wizardforcel.gitbooks.io/wangdaokaoyan-os/content/13.html