第二章 进程与线程 二十、死锁的处理策略(预防死锁、避免死锁、死锁的检测和解除)

目录

一、分类

二、预防死锁

1、破坏互斥条件

2、破坏不剥夺条件

3、破坏请求和保持条件

4、破坏循环等待条件

5、总结

三、避免死锁

1、什么是安全序列

2、安全状态和不安全状态

3、银行家算法

(1)核心思想

(2)例子

(3)代码实现

(4)考点

四、死锁的检测和解除

1、死锁的检测

(1)资源分配图

(2)例子

2、死锁的解除

五、总结


一、分类

第二章 进程与线程 二十、死锁的处理策略(预防死锁、避免死锁、死锁的检测和解除)_第1张图片

二、预防死锁

1、破坏互斥条件

第二章 进程与线程 二十、死锁的处理策略(预防死锁、避免死锁、死锁的检测和解除)_第2张图片

2、破坏不剥夺条件

第二章 进程与线程 二十、死锁的处理策略(预防死锁、避免死锁、死锁的检测和解除)_第3张图片

3、破坏请求和保持条件

第二章 进程与线程 二十、死锁的处理策略(预防死锁、避免死锁、死锁的检测和解除)_第4张图片

4、破坏循环等待条件

第二章 进程与线程 二十、死锁的处理策略(预防死锁、避免死锁、死锁的检测和解除)_第5张图片

5、总结

第二章 进程与线程 二十、死锁的处理策略(预防死锁、避免死锁、死锁的检测和解除)_第6张图片

三、避免死锁

1、什么是安全序列

  • 所谓安全序列,就是指如果系统按照这种序列分配资源,则每个进程都能顺利完成。
  • 只要能找出一个安全序列,系统就是安全状态。
  • 当然,安全序列可能有多个。
     

2、安全状态和不安全状态

  • 如果分配了资源之后,系统中找不出任何一个安全序列,系统就进入了不安全状态。
  • 这就意味着之后可能所有进程都无法顺利的执行下去。
  • 当然,如果有进程提前归还了一些资源,那系统也有可能重新回到安全状态,不过我们在分配资源之前总是要考虑到最坏的情况。
  • 如果系统处于安全状态,就一定不会发生死锁。
  • 如果系统进入不安全状态,就可能发生死锁(处于不安全状态未必就是发生了死锁,但发生死锁时一定是在不安全状态)

3、银行家算法

(1)核心思想

在进程提出资源申请时,先预判此次分配是否会导致系统进入不安全状态。如果会进入不安全状态,就暂时不答应这次请求,让该进程先阻塞等待。

(2)例子

1、若我们有如下例子,我们计算出各个进程最大还需要多少,和剩余多少

第二章 进程与线程 二十、死锁的处理策略(预防死锁、避免死锁、死锁的检测和解除)_第7张图片

2、我们依次对比各个进程需要的资源和剩余资源

此时P1一定可以顺利执行完毕,所以将P1存入安全序列,并增加可用资源的值

第二章 进程与线程 二十、死锁的处理策略(预防死锁、避免死锁、死锁的检测和解除)_第8张图片

此时的安全序列为:P1

3、然后重新从P0开始比对,发现还是不够;接下来是P2,同样不够;

直到P3,剩余资源满足P3的要求资源

第二章 进程与线程 二十、死锁的处理策略(预防死锁、避免死锁、死锁的检测和解除)_第9张图片

此时的安全序列为:P1、P3

4、依此类推最终得到的安全序列为:P1、P3、P0、P2、P4

(3)代码实现
#include 
#include 

using namespace std;

// 定义函数:银行家算法
bool banker_algorithm(vector &available, vector> &max_request, vector> &allocation)
{
    // 计算出需要的资源和已分配的资源
    vector> need(max_request.size(), vector(max_request[0].size()));
    for (int i = 0; i < allocation.size(); i++) {
        for (int j = 0; j < allocation[0].size(); j++) {
            need[i][j] = max_request[i][j] - allocation[i][j];
        }
    }
    // 定义一些变量
    vector finish(allocation.size(), false);  // 用于标记每个进程是否完成
    vector work(available);  // 定义工作向量
    // 实现银行家算法
    while (true) {
        // 在未完成的进程中查找一个满足条件的进程
        int i = -1;
        for (int k = 0; k < allocation.size(); k++) {
            if (!finish[k]) {
                bool can_allocated = true;
                for (int j = 0; j < allocation[0].size(); j++) {
                    if (need[k][j] > work[j]) {
                        can_allocated = false;
                        break;
                    }
                }
                if (can_allocated) {
                    i = k;
                    break;
                }
            }
        }
        // 如果找不到满足条件的进程,则返回false表示不能进行资源分配
        if (i == -1) {
            return false;
        }
        // 更新工作向量和已完成的进程
        for (int j = 0; j < allocation[0].size(); j++) {
            work[j] += allocation[i][j];
        }
        finish[i] = true;
        // 如果所有进程都已完成,则返回true表示可以进行资源分配
        if (count(finish.begin(), finish.end(), true) == finish.size()) {
            return true;
        }
    }
}

int main()
{
    // 测试
    vector available = {3, 3, 2};
    vector> max_request = {
        {7, 5, 3},
        {3, 2, 2},
        {9, 0, 2},
        {2, 2, 2},
        {4, 3, 3},
    };
    vector> allocation = {
        {0, 1, 0},
        {2, 0, 0},
        {3, 0, 2},
        {2, 1, 1},
        {0, 0, 2},
    };
    if (banker_algorithm(available, max_request, allocation)) {
        cout << "可以进行资源分配" << endl;
    } else {
        cout << "不能进行资源分配" << endl;
    }
    return 0;
}
 
(4)考点

第二章 进程与线程 二十、死锁的处理策略(预防死锁、避免死锁、死锁的检测和解除)_第10张图片

四、死锁的检测和解除

1、死锁的检测

(1)资源分配图

第二章 进程与线程 二十、死锁的处理策略(预防死锁、避免死锁、死锁的检测和解除)_第11张图片

(2)例子

1、我们有如下例子

第二章 进程与线程 二十、死锁的处理策略(预防死锁、避免死锁、死锁的检测和解除)_第12张图片

2、我们观察P1连接的线,发现它在请求R2的一个资源;而R2此时恰好剩余一个资源,所以P1可以不会发生阻塞;而P2申请的R1资源已经被分配完了,所以P2会被阻塞。

第二章 进程与线程 二十、死锁的处理策略(预防死锁、避免死锁、死锁的检测和解除)_第13张图片

3、当P1释放资源后,P2被唤醒,此时它也可以正常运行

第二章 进程与线程 二十、死锁的处理策略(预防死锁、避免死锁、死锁的检测和解除)_第14张图片

4、当消除所有边后

第二章 进程与线程 二十、死锁的处理策略(预防死锁、避免死锁、死锁的检测和解除)_第15张图片

2、死锁的解除

第二章 进程与线程 二十、死锁的处理策略(预防死锁、避免死锁、死锁的检测和解除)_第16张图片

五、总结

第二章 进程与线程 二十、死锁的处理策略(预防死锁、避免死锁、死锁的检测和解除)_第17张图片

你可能感兴趣的:(操作系统学习,java,开发语言,操作系统,算法)