[Linux]死锁

死锁是指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。之前信号量的时候我们知道,如果多个进程等待,主要体现在占有锁的问题上。死锁也可以被定义为:一组竞争系统资源或互相通信的进程间相互的永久阻塞。当一组进程中的所有进程都在等待一个事件,而只有在进程集合中的其他阻塞的进程才可以触发该事件,这时就称一组进程死锁。因为没有事件触发,因此死锁是永久性的。

产生死锁的原因有

(1)竞争资源。当系统中供多个进程共享的资源如打印机、公用队列等,其数目不足以满足诸进程的需要时,会引起诸进程对资源的竞争而产生死锁。

(2)进程间推进顺序非法。进程在运行过程中,请求和释放资源的顺序不当,也同样会导致产生进程死锁。

详细分析一下产生死锁的原因哦。

(1)可剥夺和非剥夺性资源

可剥夺性资源指某进程在获得这类资源后,该资源可以再被其他进程或系统剥夺。如优先级高的可以剥夺优先级低的进程的处理机。非剥夺性资源是当这类资源分配给某进程后,再不能强行收回,只能在进程用完后自行释放。

(2)竞争非剥夺性资源

由于它们的数量不能满足诸进程运行的需要,会使进程在运行过程中,因剥夺这些资源而陷入僵局。

比如,系统中有一台打印机R1和一台磁带机R2,供进程P1,P2共享。如果P1占用了打印机,P2占用了磁带机。此时如果P1想使用磁带机,而P2想使用打印机,这就使P1和P2都在等待对方的资源被释放,而陷入僵局。从而也使P1和P2得不到自己的资源而不能释放现有的资源,最后进入死锁状态。

(3)竞争临时性资源

上述的打印机资源属于顺序性重复使用的资源,属于永久性资源。还有一种是临时性资源,是一个进程产生,由另一进程使用短暂时间后无用的资源。也可能产生死锁。

(4)推进顺序不当。P1—>Request(R2),p2—>Request(R1)时产生死锁。

产生死锁的必要条件

如果发生死锁,则有四个产生死锁的必要条件:

(1)互斥条件。一次只有一个进程使用资源,其他进程不能访问已分配的资源。

(2)请求和保持条件(也称占有且等待)。指进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源又已被其他进程占有,此时请求进程阻塞,但又对自己已获得的其他资源保持不放。

(3)不剥夺条件(非抢占)。指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完自己释放。

(4)环路等待(循环等待)。指存在一个封闭的进程链,使得每个资源至少占有此链中下一个进程所需要的一个资源。在发生死锁时,必然存在一个进程,资源的环形链,即P0,P1,P2….Pn,则P0在等待P1占有的资源,P1占有P2的,….Pn占有P0的。

说完死锁之后,我们应该想想如何处理这种死锁呢。处理死锁的基本方法有:预防死锁,避免死锁,检测死锁,解除死锁。

预防死锁是使四个必要条件中的第2,3,4个中的一个不成立来避免不发生死锁。为预防死锁,可以使请求和保持条件中,可以要求进程一次性地请求所有需要的资源,并且阻塞这个进程直到所有请求都同时满足。对于非抢占式的预防死锁的方法,如果占有某些资源的一个进程进行进一步资源请求被拒绝,则该进程必须释放它最初占用的资源,如果有必要,可以再次请求此类资源。还有一种情况是,如果一个进程请求当前被另一个进程占有的一个资源,则操作系统可以抢占另一个进程,要求它释放资源。(这类情况适合具有不同优先级的进程)

死锁避免

解决死锁问题的另一种方法是死锁避免。与死锁预防的差距也不大。这里有俩种死锁避免的方法,

1.如果一个进程的请求会导致死锁,则不启动此进程
2.如果一个进程增加资源的请求导致死锁,则不允许此分配

这里解决死锁问题的方法有:银行家算法

银行家算法通过已分配的资源和可用资源数等来获取安全序列,如果存在安全序列,就可以分配请求资源,不存在则说明不可以请求资源,这样就可以有效的避免死锁。

(1)请求资源,发出请求向量
(2)假定可为P1分配资源,修改Available,Allocation和Need向量
(3)利用安全性算法检查此时系统是否安全

银行家算法需求分析:

允许进程动态地申请资源,系统在每次实施资源分配之前,先计算资源分配的安全性,若此次资源分配安全(即资源分配后,系统能按某种顺序来为每个进程分配其所需的资源,直至最大需求,使每个进程都可以顺利地完成),便将资源分配给进程,否则不分配资源,让进程等待。
功能实现:
理解了死锁的原因,尤其是产生死锁的四个必要条件,就可以最大可能地避免、预防和解除死锁。所以,在系统设计、进程调度等方面注意如何能够不让这四个必要条件成立,如何确定资源的合理分配算法,避免进程永久占据系统资源。此外,也要防止进程在处于等待状态的情况下占用资源,在系统运行过程中,对进程发出的每一个系统能够满足的资源申请进行动态检查,并根据检查结果决定是否分配资源,若分配后系统可能发生死锁,则不予分配,否则予以分配 。因此,对资源的分配要给予合理的规划。
1) 可利用资源向量Available。这是一个含有m个元素的数组,其中的而每一个元素代表一类可利用资源数目,其初始值是系统中所配置的该类全部可用资源的数目,其数值随该类资源的分配和回收而动态的改变。如果Available[j]=K,则表示系统中现有Rj类资源K个。
2) 最大需求矩阵Max。这是一个n*m的矩阵,它定义了系统中n个进程中的每一个进程对m类资源的最大需求。如果Max[i,j]=K;则表示进程i需要Rj类资源的最大数目为K。
3) 分配矩阵Allocation。这也是一个n*m的矩阵,它定义了系统中每一类资源当前已分配给每一进程的资源数。如果Allocation[i,j]=K,则表示进程i当前已分得Rj类资源的数目为K。
4) 需求矩阵Need。这也是一个n*m的矩阵,用以表示每一个进程尚需的各类资源数。如果Need[i,j]=K,则表示进程i还需要Rj类资源K个,方能完成任务。
上述三个矩阵间存在下述关系:Need[i,j]=Max[i,j]-Allocation[i,j]
这里写图片描述

#define _CRT_SECURE_NO_WARNINGS 1
extern "C"
#include<stdio.h>
#include<stdlib.h>
#define W 10
int Available[W];           //可使用资源
int Max[W][W];             //最大需求资源数
int Allocation[W][W];      //已分配资源
int Need[W][W];            //需求资源
int Work[W];              //工作向量
int Finish[W];           //是否有足够的资源分配,状态标志
int Request[W][W];        //进程申请资源向量
int Temp[W];             //暂存可用资源数
int AllReSourceNum[W];        //各类资源总数
int i, j;
int ResourceNum;              //系统资源总数
int ProcessNum;               //总的进程数
int a;                       //当前申请的进程号
int l, e;                     
int b = 0, c = 0, f = 0, g;  //c: 统计对于每一个进程,成功分配的资源类别数
int  SecurityCheck()     //安全性检测
{printf("\n\n");printf("\t\t\t 安全性检测 \n\n");printf("          工作向量       尚需求量       已分配      工作向量+已分配\n进程 ");for (c = 1; c <= 4; c++){for (j = 1; j <=ResourceNum; j++){printf("  %d类", j);}}for (i = 1; i <=ProcessNum; i++){Temp[i] = Available[i];    //Temp[i]只是一个暂时寄存的中间变量,为防止在下面安全性检查时修改到Available[i]而代替的一维数组Finish[i] = false;}for (g = 1; g <=ProcessNum; g++){for (i = 1; i <=ProcessNum; i++){b = 0;                 //计数器初始化Finish[i] == false;for (j = 1; j <=ResourceNum; j++){if (Need[i][j] <= Temp[j]){b = b + 1;}if (Finish[i] == false && b ==ResourceNum){Finish[i] = true;printf("\nP[%d] ", i);        //依次输出进程安全序列  for (l = 1; l <=ResourceNum; l++){printf("  %2d ", Temp[l]);}for (j = 1; j <=ResourceNum; j++){printf("  %2d ",Need[i][j]);}for (j = 1; j <=ResourceNum; j++){//Allocation[i][j]=Temp[j]-Need[i][j];printf("  %2d ", Allocation[i][j]);}for (j = 1; j <=ResourceNum; j++){printf("  %2d ", Temp[j] + Allocation[i][j]);}for (l = 1; l <=ResourceNum; l++){Temp[l] = Temp[l] + Allocation[i][l];        }}}}}printf("\n\n");for (i = 1; i <=ProcessNum; i++){if (Finish[i] == true) f = f + 1;  //统计Finish[i]==true的个数}if (f ==ProcessNum){printf("安全序列");printf("\n\n系统剩余资源量:");for (i = 1; i <=ResourceNum; i++){printf("   %d ", Available[i]);}f = 0;       //将计数器f重新初始化,为下一次提出新的进程申请做准备return 1;}else{printf("不安全序列");return 0;}
}
void Initialize()    //初始化
{printf("请输入系统的资源种类数:");scanf("%d", &ResourceNum);for (i = 1; i <=ResourceNum; i++){printf("第%d类资源总数:", i);scanf("%d", &AllReSourceNum[i]);}printf("请输入进程总数:");scanf("%d", &ProcessNum);for (i = 1; i <=ProcessNum; i++){for (j = 1; j <=ResourceNum; j++){printf("进程P[%d]对第%d类资源的最大需求量:", i, j);scanf("%d", &Max[i][j]);}}for (i = 1; i <=ProcessNum; i++){for (j = 1; j <=ResourceNum; j++){printf("进程P[%d]对第%d类资源已分配数:", i, j);scanf("%d", &Allocation[i][j]);Need[i][j] = Max[i][j] - Allocation[i][j];}}for (i = 1; i <=ResourceNum; i++){for (j = 1; j <=ProcessNum; j++){AllReSourceNum[i] -= Allocation[j][i];}}for (i = 1; i <=ResourceNum; i++)Available[i] = AllReSourceNum[i];SecurityCheck();
}
void RequestResource()  //进程申请资源
{printf("请输入申请资源的进程:");scanf("%d", &a);for (i = 1; i <=ResourceNum; i++){printf("请输入进程P[%d]对%d类资源的申请量:", a, i);scanf("%d", &Request[a][i]);if (Request[a][i] >Need[a][i]){printf("\n出错!进程申请的资源数多于它自己申报的最大需求量\n");return;}if (Request[a][i] > Available[i]){printf("\nP[%d]请求的资源数大于可用资源数,必须等待\n", a);return;}}for (i = 1; i <=ResourceNum; i++){//以下是试探性分配Available[i] = Available[i] - Request[a][i];Allocation[a][i] = Allocation[a][i] + Request[a][i];Need[a][i] =Need[a][i] - Request[a][i];}int ret=SecurityCheck();if (ret == 1){int key = 0;for (j = 1; j <=ResourceNum; j++){if (Need[a][j] == 0){key++;}}if (key ==ResourceNum){for (j = 1; j <=ResourceNum; j++){Available[j] += Allocation[a][j];Allocation[a][j] = 0;}}}
}void ResourceState()
{printf("\n\n");printf("          已分配       最大需求量       尚需要量 \n进程");for (i = 1; i <= 3; i++){for (j = 1; j <=ResourceNum; j++){printf("  %d类", j);}}for (i = 1; i <=ProcessNum; i++){printf("\nP[%d]", i);for (j = 1; j <=ResourceNum; j++){printf("  %2d ", Allocation[i][j]);}for (j = 1; j <=ResourceNum; j++){printf("  %2d ", Max[i][j]);}for (j = 1; j <=ResourceNum; j++){printf("  %2d ",Need[i][j]);}}printf("\n\n系统剩余资源量:   ");for (i = 1; i <=ResourceNum; i++){printf("   %d ", Available[i]);}printf("\n");
}void menu()
{printf("\n\t-------------银行家算法---------------\n");printf("             1、初始化                \n");printf("             2、进程请求资源          \n");printf("             3、资源分配状态          \n");printf("             4、退出                  \n");printf("\t--------------------------------------\n");printf("\n请输入你的选择: ");
}
//BankerAlth.cpp
#include"BankerAlth.h"
int main()
{int select = 0;printf("\n\n");while (1){menu();scanf("%d", &select);printf("\n\n");switch (select){case 1:Initialize();break;case 2:RequestResource();break;case 3:ResourceState();break;case 4:printf("\nSee you Next time !\n\n");system("pause");return 0;}}system("pause");return 0;
}

运行结果:

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

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

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

相关文章

[Linux]线程安全和可重入函数

线程安全&#xff1a;一个函数被称为线程安全的&#xff0c;当且仅当被多个并发进程反复调用时&#xff0c;它会一直产生正确的结果。如果一个函数不是线程安全的&#xff0c;我们就说它是线程不安全的。 重入&#xff1a;函数被不同的控制流程调用,有可能在第一次调用还没返回…

[Linux]信号量

信号量是一个计数器&#xff0c;用于为多个进程提供对共享数据对象的访问。 在信号量上只有三种操作可以进行&#xff0c;初始化、递增和增加&#xff0c;这三种操作都是原子操作。递减操作可以用于阻塞一个进程&#xff0c;增加操作用于解除阻塞一个进程。 为了获得共享资源…

[Linux]关于SIGCHLD

之前我们就学过&#xff0c;关于wait和waitpid来处理僵尸进程&#xff0c;父进程等待子进程结束后自己才退出&#xff0c;这样的方法有俩种方式&#xff0c;一种是父进程死死的等子进程退出&#xff0c;也就是使用阻塞的方式等待子进程退出&#xff0c;另一种方式是通过非阻塞的…

C语言思维导图

本人能力有限&#xff0c;知识点难免概括不全&#xff0c;如有错误欢迎指正

pthread和互斥量条件变量函数意义速查表

数据类型 pthread_t 线程 互斥量和条件变量

[Linux]共享内存

共享内存是UNIX提供的进程间通信手段中速度最快的一种&#xff0c;也是最快的IPC形式。为什么是最快的呢&#xff0c;因为数据不需要在客户进程和服务器进程之间复制&#xff0c;所以是最快的一种IPC。这是虚存中由多个进程共享的一个公共内存块。 两个不同进程A、B共享内存的…

[Linux]gdb调试多进程多线程例程

gdb相信学linux的同学已经比较熟悉了吧&#xff0c;它是linux下代码调试工具。我们在写c语言&#xff0c;c的代码时经常会用到&#xff0c;它有一些常用的调试命令: run&#xff08;r&#xff09;&#xff1a;运行程序&#xff0c;如果有断点在下一个断点处停止 start&#xf…

[Linux]守护进程(精灵进程)

一、守护进程是什么 守护进程是生存期很长的一种进程&#xff0c;可以说它是7*24小时工作的。&#xff08;什么是7*24&#xff0c;一周7天&#xff0c;每天24小时&#xff0c;这不就是一年365天一直在工作嘛&#xff0c;还搞的这么诙谐&#xff0c;哈哈&#xff09;。它们常常…

浅谈shell中的clear命令实现

NAME(名称) clear - 清除终端屏幕 SYNOPSIS(总览) clear DESCRIPTION(描述) clear可以在允许的情况下清屏. 它会在环境变量中查找终端的类型, 然后到terminfo数据库中找出清屏的方法. 《man手册》 #include <stdio.h>int clear_main(int argc, char **argv) {/* Th…

[Linux]ARP协议

概念&#xff1a; 1. ARP协议(地址解析协议):由IP地址转换为MAC地址的协议。IP地址&#xff1a;网络号主机号。MAC地址&#xff1a;数据链路层的物理地址&#xff08;硬件地址&#xff09;。IP协议使用了ARP协议&#xff0c;因此被划归为网络层&#xff0c;但其用途是从网络层…

Makefile使用及多文件gdb 调试

文件内容 [koulocalhost makefile]$ cat 1.c #include "3.h" int main() {key_t key ftok(".",1);printf("%d\n",add(1,2));return 0; }[koulocalhost makefile]$ cat 2.c #include "3.h" int add(int a, int b) {return a b; } [k…

[Linux]CRC校验

CRC(Cyclic Redundancy Check),循环冗余校验码&#xff0c;是数据通信领域中最常用的一种差错校验码&#xff0c;其特征是信息字段和校验字段的长度可以任意选定。 CRC校验步骤&#xff1a; CRC分为两部分&#xff0c;前部分为信息码&#xff0c;后部分为校验码&#xff1b;设…

python字符串系列

1.find方法用于在长串中查找子串&#xff0c;返回子串中最左位置的下标&#xff0c;如果没找到&#xff0c;则返回-1 2.join方法用于在队列中添加元素 3.lower返回字符串的小写字母版 4.replace返回字符串中所有匹配项均被替换之后得到字符串 5.split将字符串分割成序列 6.stri…

linux网络编程Internet Socket地址,套接字,和函数

文章内容节选《linux/UNIX 系统网络编程》 Internet domain socket地址有两种&#xff1a;IPv4 IPv6 IPv4被存储在结构体中&#xff0c; 该结构体在 netinet/in.h 中进行定义 cd usr/include/netinet/in.h struct in_addr {in_addr_t s_addr; //32位IPv4地址 }struct so…

浅谈socket网络编程函数参数(一)

socket函数解析 概念: 每个进程的进程空间里都有一个socket描述符表。套接字描述符表属于一个进程&#xff0c;而socket地址结构位于操作系统的内核缓冲。 函数原型 #include <sys/socket.h>int socket(int domain, int type, int protocol);函数参数 family参数 默…

为什么计算机起始时间是1970年1月1日

1969年8月&#xff0c;贝尔实验室的程序员肯汤普逊利用妻儿离开一个月的机会&#xff0c;开始着手创造一个全新的革命性的操作系统&#xff0c;他使用B编译语言在老旧的PDP-7机器上开发出了Unix的一个版本。随后&#xff0c;汤普逊和同事丹尼斯里奇改进了B语言&#xff0c;开发…

TCP三次挥手四次握手(面试总结)

1、 为什么建立连接协议是三次握手&#xff0c;而关闭连接却是四次握手呢&#xff1f; 全双工通信。 这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后&#xff0c;它可以把ACK和SYN&#xff08;ACK起应答作用&#xff0c;而SYN起同步作用&#xff09;放在一个…

csdn怎么快速转载别人的文章

如何转载 用谷歌浏览器加载文章地址&#xff0c;打开文章F12打开Developer Tools&#xff0c;并打开Elements页面 将文章开头部分的文字作为关键字在Elements界面搜索 以此文为例&#xff1a;http://blog.csdn.net/aggressive_snail/article/details/54375876 搜索找了好久关…

解释性语言和汇编性语言对比

解释性语言和编译型语言的区别和不同解释性语言编译型语言概念计算机不能直接的理解高级语言&#xff0c;只能直接理解机器语言&#xff0c;所以必须要把高级语言翻译成机器语言&#xff0c;计算机才能执行高级语言的编写的程序。翻译的方式有两种&#xff0c;一个是编译&#…