【OS】操作系统课程笔记 第六章 并发性——死锁

news2024/11/26 8:23:19

6.1 死锁的概念

所谓死锁,是指多个进程因竞争资源而造成的一种僵局,若无外力作用,这些进程都将永远不能再向前推进。

下面举个例子,进程P1已经占用了资源R1,进程P2已经占用了资源R2,而P1和P2都要同时使用两个资源才能继续运行,所以P1要求R2,P2要求R1,这时候P1、P2便处于死锁状态。

 产生死锁的两个原因:

  • 竞争系统资源
  • 进程推进顺序不当

6.2 产生死锁的条件和处理

6.2.1 必要条件

1. 互斥条件:一个资源每次只给一个进程使用;

2. 请求保持条件:在申请新资源时,保持原资源;

3. 非剥夺条件:资源由占有者自愿释放,不得强制占用;

4. 循环等待条件:进程间形成了等待资源的环路。

6.2.2 处理死锁的基本方法

不让死锁发生:

  • 预防死锁(静态策略)
  • 避免死锁(动态策略)

死锁可能发生:

  • 检测死锁
  • 解除死锁

6.3 死锁的预防

预防死锁就要破坏死锁四个必要条件的一个或多个,但是其中第一个互斥条件是不能破坏的,因为资源可共享的情况下不可能发生死锁,也就没有预防死锁的前提了。

1. 破坏请求保持条件:采用预先分配策略

两种方案:

  1. 要求每个进程在运行之前申请它所需全部资源。
  2. 规定每个进程在请求新资源前必须释放全部已占用的资源。

两个缺点:

  1. 资源利用率较低;
  2. 可能产生饥饿现象:资源竞争激烈导致某个进程陷入无限期等待。

2. 破坏非剥夺条件:收回未使用完毕的资源

方案1:申请资源得不到满足,释放已占有的全部资源;

方案2:请求资源得不到满足,讨论两种情况:

  • 有进程占用该资源+等待更多资源,则剥夺这些资源以满足请求进程;
  • 有进程仅占用该资源,则请求进程等待(适用资源:容易保存恢复,如寄存器、存储器)

3. 破坏循环等待条件:有序分配策略

把系统中所有资源进行编号,进程在申请资源时必按资源编号的递增次序进行,否则系统不予分配,例如:

6.4 死锁的避免

系统运行过程中,允许进程动态地申请资源,在资源分配之前,先计算此次资源分配的安全性,若分配导致系统进入不安全状态,则将资源分配给进程,否则令进程等待。

最有代表性的避免死锁算法是银行家算法

6.4.1 系统安全状态

安排好进程的顺序,使得按顺序可以顺利完成所有进程,就称系统处于安全状态,如果系统中的进程不存在这么一个安全序列,则称系统处于不安全状态。

要注意,安全状态一定是没有死锁发生的

三个进程一类资源安全性检测代码:

#include<iostream>
using namespace std;
int seq[100]; // 保存安全序列的下标
struct { // 代表三个进程的结构体 
	int max_need; // 最大需求 
	int allocated; // 已分配的资源数 
	int need; //  还需要的资源数 
	int state = 0; // 进程是否已运行,0代表未运行,1代表已运行 
}P[3];  
int main() {
	for (int i = 1; i <= 3; i++) {
		cin >> P[i].max_need >> P[i].allocated >> P[i].need;
	} // 输入三个进程的数据 
	int available, work; // available表示目前可用的资源数,work用来代替available参与判断 
	int s = 3, j = 1; // s表示进程数,j是用来保存安全序列的下标 
	cin >> available;
	work = available;
	while (s--) {
		for (int i = 1; i <= 3; i++) {
			if (P[i].need <= work && P[i].state == 0) {
				work += P[i].allocated;
				seq[j++] = i;
				P[i].state = 1; 
			}
		}
	}
	if (seq[3] != 0) {
		cout << " The system is safe because there is a safe sequence: ";
		cout << "P" << seq[1] << "→P" << seq[2] << "→P" << seq[3]; 
	}
	else
		cout << "The system is not safe!"; 
	return 0; 
} 

6.4.2 银行家算法

1. 银行家算法数据结构

  • 可利用资源数量:Available
  • 最大需求矩阵:Max
  • 分配矩阵:Allocation
  • 需求矩阵:Need

2. 银行家算法过程

当进程Pi提出资源申请,系统执行以下四个步骤:

1)若Request[i]≤Need[i],转2);否则错误返回(需要资源数超过最大值)

2)若Request[i]≤Available,      转(3);否则进程等待(无足够资源)

3)系统试分配资源,则有 3 个计算:

Available:=Available - Request[i];

Allocation[i]:=Allocation[i] + Request[i];

Need[i]:=Need[i] – Request[i]

4)执行安全性算法;若新状态安全,则分配完成,若新状态不安全,则恢复原状态,进程等待

3. 安全性算法

为进行安全性检查,再定义2个数据结构:

Work: ARRAY[1..m] of integer;

Finish: ARRAY[1..n] of Boolean;

算法4个步骤如下:

1) Work := Available;   Finish := false;

2) 寻找满足条件的 i :   Finish[i] = false and  Need[i]≤Work;   如果不存在,则转4)

3) Work:=Work+Allocation[i];Finish[i]:=true;转2)

4) 若对所有i,Finish[i]=true,则系统处于安全状态,否则处于不安全状态

4. 银行家算法的代码实现

#include<stdio.h>
#include<string.h>
#define PN 5//进程数
#define RN 4//资源种类数

typedef struct//定义结构体类型pcb
{
    char name[3];//进程名,如p0
    int max[RN];//最大资源值
    int allocation[RN];//已分配资源数
    int need[RN];//任需求资源数
}pcb;

struct//定义为结构体类型,方便赋值
{
    int flag[PN];//进程检测标志,1时表示已通过
    int safes[PN];//存放进程编号,作为安全序列输出
}fs0,fs;//fs0保存初始值,fs用于工作

struct//定义为结构体类型,方便赋值
{
    int available[RN];//系统可用资源向量
}av0,av,avx;//av0保存初始值,av和avx用于工作

pcb proc0[PN],proc[PN];//proc0保存初始值,proc用于工作
int request[RN];//进程请求资源向量

void init();//初始化,输入数据
void output(pcb*);//输出数据
void cur_state();//判断系统当前状态
void req_state();//分析进程提出的请求是否可以响应
void back0();//退回至初始值
void back1(int);//退回至分配前
int check(int*);//进程need向量与available向量比较
int banker();//银行家算法

int main()
{
int num;
printf("【银行家算法】\n");
printf("进程数:%d\n",PN);
printf("资源数:%d\n",RN);
printf("=================\n");
    init();
    output(proc0);//输出初始资源分配情况
    printf("【系统当前状态分析】\n");
    cur_state();
    printf("======================================================\n\n");
    printf("选择操作序号(0:结束程序;1:资源请求分析)\n");
    printf("输入序号:");
    scanf("%d",&num);getchar();
    while(1)
    {
        switch(num)
        {
            case 1:printf("\n【进程资源请求分析】\n");req_state();break;
            default:printf("\n======分析结束!======\n");return 0;
        }
        printf("\n");
        printf("======================================================\n");
        printf("\n");
        printf("选择操作序号(0:结束程序;1:资源请求分析)\n");
        printf("输入序号:");
        scanf("%d",&num);getchar();
    }
}

void init()
{//初始化
int i,j;
    for(i=0;i<PN;i++)
    {
        printf("\n输入进程名:");
        gets(proc0[i].name);
        printf("输入max:");
        for(j=0;j<RN;j++)
            scanf("%d",&proc0[i].max[j]);
        printf("输入allocation:");
        for(j=0;j<RN;j++)
            scanf("%d",&proc0[i].allocation[j]);
        for(j=0;j<RN;j++)//计算need
            proc0[i].need[j]=proc0[i].max[j]-proc0[i].allocation[j];
        fs0.flag[i]=0;//进程标志位置0
        fs0.safes[i]=-1;//安全序列置-1
        proc[i]=proc0[i];//给工作矩阵proc赋值
        getchar();//吸收scanf()产生的回车符
    }
    printf("\n输入available:");
    for(j=0;j<RN;j++)
        scanf("%d",&av0.available[j]);
    fs=fs0;//给工作单元fs和av赋值
    av=av0;
    getchar();//吸收scanf()产生的回车符
}

void output(pcb* p)
{//输出资源分配情况,觉得麻烦可以不考虑
    int i,j;
    printf("\n");
    printf("===============================================================================\n");
    printf(" 标志位 | 进程名 |  最大值 \t|  已分配 \t|  需求值 \t|  可用数 \n");
    printf("-------------------------------------------------------------------------------\n");
    for(i=0;i<PN;i++)
    {
        printf("   %-5d",fs.flag[i]);
        printf("|");
        printf("   %-5s",p[i].name);
        printf("| ");
        for(j=0;j<RN;j++)
            printf("%-3d",p[i].max[j]);
        printf("\t| ");
        for(j=0;j<RN;j++)
            printf("%-3d",p[i].allocation[j]);
        printf("\t| ");
        for(j=0;j<RN;j++)
            printf("%-3d",p[i].need[j]);
        printf("\t| ");
        if(i==0)
            for(j=0;j<RN;j++)
                printf("%-3d",av.available[j]);
        printf("\n");
    }
    printf("===============================================================================\n\n");
}

int banker()
{//银行家算法
    int i,j,t=0,k,f;
    k=0;//计数器,找到符合条件的进程数
    f=1;//标志位,每轮查找标志,f=0时该轮未找到合适进程,查找结束
    while(f==1&&k<PN)
    {
        f=0;//本轮查找标志置0
        for(i=0;i<PN;i++)
            if(!fs.flag[i]&&check(proc[i].need))
            {//找到符合条件的进程
                f=1;//本轮查找标志置1
                fs.flag[i]=1;//进程标志置1
                for(j=0;j<RN;j++)//修改available中的值
                    av.available[j]+=proc[i].allocation[j];
                fs.safes[t]=i;//进程序号i记入safes数组中,作为安全序列输出
                t++;k++;
            }
    }
    if(k<PN)return 0;//当k<PN时说明不存在安全序列,返回0
    else return 1;
}

int check(int* p)//p为当前进程的need向量
{//当前进程need向量与available向量比较
    int i;
    for(i=0;i<RN;i++)
        if(p[i]>av.available[i])return 0;//若检测不通过则返回0
    return 1;//检测通过
}

void cur_state()
{//检测系统当前状态
    int i;
    if(banker())
    {
        printf("分析结果:当前系统处于安全状态,存在安全序列 ");
        for(i=0;i<PN;i++)//输出安全序列
            printf("p%d",fs.safes[i]);
        printf("\n");
    }
    else
        printf("分析结果:当前系统处于不安全状态!请结束程序");
    //output(proc);//输出分析后的状态(查看flag和available状况)
    fs=fs0;av=av0;//退回至初始状态
    printf("\n\n");
}

void req_state()
{//对进程提出的资源请求,判断分配的可行性
    int i,j,n;
    printf("输入提出请求的进程序号n:p");
    scanf("%d",&n);
    printf("输入请求资源向量request:");
    for(i=0;i<RN;i++)
        scanf("%d",&request[i]);
    for(i=0;i<RN;i++)//判断请求的合法性
        if(request[i]>proc[n].need[i]||request[i]>av.available[i])
        {
            printf("分析结果:请求不合法,系统不予响应!");
            for(j=0;j<RN;j++)
                request[j]=0;//清空request
            return;
        }

    for(i=0;i<RN;i++)
    {//尝试分配,修改allocation,need和available的值
        av.available[i]-=request[i];
        proc[n].need[i]-=request[i];
        proc[n].allocation[i]+=request[i];

    }
    avx=av;//avx保存分配后available的值
    //output(proc);//输出分配后的资源分布状况
    if(banker())//调用银行家算法,寻找安全序列
    {
        printf("分析结果:当前系统处于安全状态,存在安全序列 ");
        for(i=0;i<PN;i++)//输出安全序列
            printf("p%d ",fs.safes[i]);
        printf("\n");
        av=avx;//分析结束,确定资源分配
        fs=fs0;//清空标识位flag和安全序列safes
    }
    else
    {
        printf("分析结果:不存在安全序列,系统不予响应,进程阻塞!\n");
        //output(proc);//输出分析结束后的情况(查看flag和available状况)
        back1(n);//取消分配,退回至分配前
        printf("已取消分配,退回至分配前\n");
    }
    printf("\n\n");
    printf("输入“y”可退回到初始状态,请选择(y/n)\n");
    printf("请输入:");
    getchar();//吸收前面的输入所产生的回车符
    if(getchar()=='y')
    {
        back0();
        printf("已退回至初始状态\n");
    }
}

void back0()
{//恢复至初始值
    int i;
    fs=fs0;
    av=av0;
    for(i=0;i<PN;i++)
        proc[i]=proc0[i];
}

void back1(int m)//m为提出资源请求的进程序号
{//撤销分配,退回至分配前
    int i;
    fs=fs0;
    for(i=0;i<RN;i++)
    {
        av.available[i]=avx.available[i]-request[i];
        proc[m].need[i]+=request[i];
        proc[m].allocation[i]-=request[i];
    }
}

6.5 死锁的检测与解除

区别死锁的避免和非避免:

  • 避免:银行家算法时避免系统内各进程竞争系统资源而发生死锁;
  • 非避免:死锁检测与解除,操作系统不断监测系统进展情况,判断死锁是否发生,一旦死锁就采取专门的措施,解除死锁并以最小代价恢复操作系统运行。

6.5.1 死锁的检测

1. 资源分配图

2. 死锁判定法则

  • 如果图中没有环路,则系统中没有死锁
  • 如果图中有环路,且每类资源只有一个,则有环时系统存在死锁的充要条件
  • 如果图中有环路,但每类资源的个数不全为1,此时可以简化资源分配图,再检测系统是否存在死锁

3. 资源分配图化简

示例如下:

6.5.2 死锁的解除

6.6 死锁的综合处理策略

习题解析

答案:B

解析:如果只有两个进程的话,那么每个进程刚好能分配到4台打印机,不会死锁;但如果有三个进程的话,就有可能出现打印机分配为3、3、2的情况,此时每个进程都不满足,都再申请,但是都没有资源了,所以就会发生死锁,三个进程都这样,更不用说四个、五个进程了。所以K最小为3,选B。

 

答案:C

解析: 使得每个进程都只再申请一个资源时就能满足,在这里的已分配资源就是2、3、3,如果再加一个资源,系统就不会发生死锁,因为再加一个的话就可以满足其中的一个进程,等到这个进程结束后就会释放它的资源,从而满足剩下的进程。因此,可确保系统不发生死锁的设备数最小为9,选C。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1177772.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

全自动批量AI改写文章发布软件【软件脚本+技术教程】

项目原理&#xff1a; 利用AI工具将爆款文章改写发布到平台上流量变现,通过播放量赚取收益 软件功能&#xff1a; 1.可以根据你选的文章领域&#xff0c;识别你在网站上抓取的文章链接进来自动洗稿生成过原创的文章&#xff0c;自动配图 2.同时还可以将管理的账号导入进脚本软…

Java基础(第五期): 一维数组 二维数组 数组 引用数据类型在内存中的存储图解

Java基础专栏 文章目录 一、数组介绍和静态初始化1.1 数组初始化1.2 数组的定义格式1.3 数组的静态初始化格式 二、 数组元素访问三、数组遍历操作四、数组遍历求和等练习2.数组求最大值 五、数组动态初始化六、两种初始化的区别七、数组内存图和方法参数传递八、二维数组静态…

深入详解高性能消息队列中间件 RabbitMQ

目录 1、引言 2、什么是 RabbitMQ &#xff1f; 3、RabbitMQ 优势 4、RabbitMQ 整体架构剖析 4.1、发送消息流程 4.2、消费消息流程 5、RabbitMQ 应用 5.1、广播 5.2、RPC VC常用功能开发汇总&#xff08;专栏文章列表&#xff0c;欢迎订阅&#xff0c;持续更新...&am…

【工具使用-信号叠加演示】一种演示不同频率信号叠加的工具

一&#xff0c;简介 本文主要介绍一种网页演示不同频率的正弦信号叠加的工具&#xff0c;供参考。 二&#xff0c;说明 网址&#xff1a;https://teropa.info/harmonics-explorer/ 打开后可以设置不同的信号&#xff0c;然后最上面是不同信号的频率叠加之后的效果&#xff…

Blender vs 3ds Max:谁才是3D软件的未来

在不断发展的3D建模和动画领域&#xff0c;两大软件巨头Blender和3ds Max一直在争夺顶级地位。 随着技术的进步和用户需求的演变&#xff0c;一个重要问题逐渐浮出水面&#xff1a;Blender是否最终会取代3ds Max&#xff1f;本文将深入探讨二者各自的优势和劣势、当前状况&…

2024好用免费的mac苹果电脑杀毒软件CleanMyMac

杀毒软件在苹果家族中是一个小众软件&#xff0c;百度搜索苹果电脑杀毒软件&#xff0c;可能各种杀软良莠不齐&#xff0c;因为在这个市场非常小&#xff0c;绝大多数都是冲着“清理”去的&#xff0c;而不是杀毒。最近测试了一款Mac电脑杀毒软件&#xff0c;杀毒效果也是一般般…

WebDAV之π-Disk派盘 + MiXplorer

MiXplorer是一款非常强大实用的手机文档管理器,能给用户提供了一系列的文档处理功能,包括本地文件浏览、文件排序、文件筛选、切换视图、新建文件、添加收藏等等,同时还能将你手机里的所有文件都罗列出来,简洁明了,让用户一眼就能够找到相应的文件并对其进行编辑,或是删除…

YOLOv5:通过真实结果的txt文件与预测结果的txt文件进行结果评估

YOLOv5&#xff1a;通过真实结果的txt文件与预测结果的txt文件进行结果评估 前言前提条件相关介绍项目结构YOLOv5&#xff1a;通过真实结果的txt文件与预测结果的txt文件进行结果评估val_txt.py输出结果 参考 前言 由于本人水平有限&#xff0c;难免出现错漏&#xff0c;敬请批…

Istio快速入门

Istio快速入门 目录 文章目录 Istio快速入门目录本节实战前言1、安装安装方式1.使用 istioctl install2.使用 istioctl manifest generate 安装3.使用 Helm 安装4.使用 Istio Operator 安装 安装 Istio&#x1f6a9; 实战&#xff1a;istioctl 方式安装istio-2023.11.3(测试成功…

SRC实战 | CORS跨资源共享漏洞

CORS跨资源共享 跨源资源共享 (CORS) 是一种浏览器机制&#xff0c;允许网页使用来自其他页面或域的资产和数据。 大多数站点需要使用资源和图像来运行它们的脚本。这些嵌入式资产存在安全风险&#xff0c;因为这些资产可能包含病毒或允许服务器访问黑客。 CORS响应头 CORS通…

物联网AI MicroPython学习之语法 sys系统相关

学物联网&#xff0c;来万物简单IoT物联网&#xff01;&#xff01; sys 介绍 sys 模块中提供了与micropython运行环境有关的函数和变量。 常量说明 常量定义常量说明sys.argv当前程序启动的可变参数列表sys.byteorder字节顺序 (‘little’ - 小端&#xff0c; ‘big’ - 大…

深入理解强化学习——多臂赌博机:10臂测试平台

分类目录&#xff1a;《深入理解强化学习》总目录 为了大致评估贪心方法和 ϵ − \epsilon- ϵ−贪心方法相对的有效性&#xff0c;我们将它们在一系列测试问题上进行了定量比较。这组问题是2000个随机生成的 k k k臂赌博机问题&#xff0c;且 k 10 k10 k10。在每一个赌博机问…

Python的切片操作详细用法解析

在利用Python解决各种实际问题的过程中&#xff0c;经常会遇到从某个对象中抽取部分值的情况&#xff0c;切片操作正是专门用于完成这一操作的有力武器。理论上而言&#xff0c;只要条件表达式得当&#xff0c;可以通过单次或多次切片操作实现任意切取目标值。切片操作的基本语…

【计算机架构】程序指令计数 | 功耗计算 | 电力功耗 | 安德尔定律(Amdahl‘s Law)

0x00 程序的指令计数 程序的指令计数&#xff08;Instruction Count&#xff09;由程序本身、ISA&#xff08;指令集架构&#xff09;和编译器决定。这表示一个程序中包含的指令数量受到程序编写方式、计算机体系结构和编译器的影响。 每条指令的平均周期数&#xff08;Averag…

如何更改IP地址为美国IP?美国静态住宅代理如何搭建?

相信很多做跨境电商或外贸如TikTok shop、Facebook商店、Amazon、领英的玩家都需要搭建独享的美国IP环境来运营店铺&#xff0c;那么如何搭建稳定独享的IP环境呢&#xff1f;加下来为你详细介绍&#xff0c;助力您的跨境业务。 一、选择合适的代理IP 代理IP可以帮助隐藏用户真…

XSS漏洞利用工具BeEF

BeEF是Browser Exploitation Framework的缩写。随着人们越来越多地关注针对包括移动客户端在内的客户端的网络传播攻击&#xff0c;BeEF使专业的渗透测试人员可以使用客户端攻击向量来评估目标环境的实际安全状况。与其他安全框架不同&#xff0c;BeEF超越了硬化的网络边界和客…

breach1靶机攻略

breach1 准备 这个靶机ip固定为 192.168.110.140 使用vmware的话&#xff0c;将它加入一张仅主机的网卡就行&#xff0c;比如vmnet7&#xff0c;然后vmnet设置成192.168.110.0网段&#xff0c;kali也新建一张网卡加入该网卡 扫描 nmap --min-rate 10000 -p- 192.168.110.1…

登录Tomcat控制台,账号密码输入正确但点击登录没反应不跳转到控制台页面

在tomcat-users.xml里面可以查看登录tomcat控制台的账号密码&#xff0c;如果账号密码输入正确还是登录不进去&#xff0c;则很有可能是tomcat的账号被锁了&#xff08;可在catalina.xxx.log里面查看&#xff09;。tomcat账号被锁定后默认情况是不访问控制台后5分钟自动解锁&am…

第六章:Property-based Testing and Test Oracles

文章目录 Test OraclesActive and Passive Test OraclesTypes of Test OraclesFormal, executable specificationsSolved examplesMetamorphic oraclesAlternative implementations (备用实现)Heuristic oracles (启发式)The Golden Program!Oracle Deviation (Oracle偏差)T…

Rust编程基础之引用与借用

1.引用与借用 在上一章节最后的代码中, 我们必须将 String 返回给调用函数&#xff0c;以便在调用 calculate_length 后仍能使用 String&#xff0c;因为 String 被移动到了 calculate_length 内。相反我们可以提供一个 String 值的引用&#xff08;reference&#xff09;。引…