前言
多个执行流在对多个锁资源进行争抢操作时,因为推进顺序不当导致执行流相互等待,流程无法继续推进,导致死锁。
一:死锁的产生
死锁产生的四个必要条件
1. 互斥条件:
同一时间一个锁资源只能被一个执行流加锁,直到释放。
2. 不可剥夺条件:
一个执行流在释放锁资源之前,其他的执行流无法剥夺占用资源。
3. 请求保持条件:
一个执行流请求被占有的锁资源时发生阻塞,还对已经获得的锁资源不进行释放。
4. 环路等待条件:
两个执行流相互请求对方的资源并且不释放已经得到的资源,相互等待。
二:死锁的预防
破坏死锁产生的必要条件
1. 破坏不可剥夺条件:
方法一:如果执行流去抢占资源被拒绝,就释放自己已经获得的资源。
方法二:操作系统允许抢占,只要执行流优先级大,就可以抢到。
2. 破坏请求保持条件:
方法一:将锁资源一次性按顺序进行分配。
方法二:只要有一个资源得不到分配,也不给这个进程分配其他的资源。
3. 破坏环路等待条件:
系统给每类资源赋予一个编号,每一条执行流按编号顺序请求资源。
三:死锁的避免(银行家算法)
银行家算法的算法思想:
操作系统当作银行家,操作系统管理的资源相当于银行家管理的资金,进程向操作系统请求资源相当于用户向银行家贷款。
定义两种状态:系统的安全状态和非安全状态。
每一个进程进入系统时,必须申明在运行过程当中,可能需要的每种资源的最大数目。当进程请求一组资源时,系统必须首先确定是否有足够的资源分配给该进程,若资源充足,则计算将这些资源分配给进程后,是否会让系统处于不安全状态。如果不会才将资源进行分配,否则让进程等待。
银行家算法的代码实现:
#include <iostream>
#define maxP 10
#define maxS 10
using namespace std;
int Available[maxS];
int Max[maxP][maxS];
int Allocation[maxP][maxS];
int Need[maxP][maxS];
int Request[maxS];
int Finish[maxP];
int path[maxP] = { 0 };
int PNum, RNum;
void outPath() {
cout << "系统安全序列是:\n";
cout << "P" << path[0] - 1;
for (int i = 1; path[i] != 0; i++) {
cout << "->P" << path[i] - 1;
}
for (int i = 0; i < PNum; i++) path[i] = 0;
cout << endl;
}
int BankSafe() {
int i, j, l = 0;
int Work[maxS];
for (i = 0; i < RNum; i++) Work[i] = Available[i];
for (i = 0; i < PNum; i++) Finish[i] = 0;
for (i = 0; i < PNum; i++) {
if (Finish[i] == 1)
continue;
else {
for (j = 0; j < RNum; j++) {
if (Need[i][j] > Work[j])
break;
}
if (j == RNum) {
Finish[i] = 1;
for (int k = 0; k < RNum; k++)
Work[k] += Allocation[i][k];
path[l++] = i + 1;
i = -1;
}
else continue;
}
if (l == PNum) {
return 1;
}
}
return 0;
}
void input(int PNum, int RNum) {
cout << "输入每个进程最多所需的各类资源数:\n";
for (int i = 0; i < PNum; i++) {
cout << "P" << i << " : ";
for (int j = 0; j < RNum; j++)
cin >> Max[i][j];
}
cout << "输入每个进程已经分配的各类资源数:\n";
for (int i = 0; i < PNum; i++) {
cout << "P" << i << " : ";
for (int j = 0; j < RNum; j++) {
cin >> Allocation[i][j];
Need[i][j] = Max[i][j] - Allocation[i][j];
if (Need[i][j] < 0) {
cout << "你输入的进程P" << i << "所拥有的第" << j + 1 << "个资源错误,请重新输入:\n";
j--;
continue;
}
}
}
cout << "请输入各个资源现有的数目:\n";
for (int i = 0; i < RNum; i++)
cin >> Available[i];
}
int requestP() {
int Pi;
cout << "输入要申请资源的进程号(0-4):";
cin >> Pi;
Pi;
cout << "输入进程所请求的各资源的数量:";
for (int i = 0; i < RNum; i++)
cin >> Request[i];
for (int i = 0; i < RNum; i++) {
if (Request[i] > Need[Pi][i]) {
cout << "所请求资源数超过进程的需求量!\n";
return 1;
}
if (Request[i] > Available[i]) {
cout << "所请求资源数超过系统所有的资源数!\n";
return 1;
}
}
for (int i = 0; i < RNum; i++) {
Available[i] -= Request[i];
Allocation[Pi][i] += Request[i];
Need[Pi][i] -= Request[i];
}
if (BankSafe()) {
cout << "系统安全!\n";
outPath();
cout << "同意分配请求!\n";
}
else {
for (int i = 0; i < RNum; i++) {
Available[i] += Request[i];
Allocation[Pi][i] -= Request[i];
Need[Pi][i] += Request[i];
}
cout << "请求后,系统不安全,你的请求被拒!\n";
}
return 0;
}
void outDATA() {
int i, j;
cout << "\n系统可用的资源数为 :";
for (j = 0; j < RNum; j++)
cout << " " << Available[j];
cout << endl << "各进程还需要的资源量:" << endl;
for (i = 0; i < PNum; i++) {
cout << "进程 P" << i << " :";
for (j = 0; j < RNum; j++)
cout << " " << Need[i][j];
cout << endl;
}
cout << endl << "各进程已经得到的资源量:" << endl;
for (i = 0; i < PNum; i++) {
cout << "进程 P" << i << " :";
for (j = 0; j < RNum; j++)
cout << " " << Allocation[i][j];
cout << endl;
}
cout << endl;
}
int main() {
cout << "输入进程的数目:";
cin >> PNum;
cout << "输入资源的种类:";
cin >> RNum;
input(PNum, RNum);
if (BankSafe()) {
cout << "当前系统安全!\n";
outPath();
}
else {
cout << "当前系统不安全!\n";
return 0;
}
while (1) {
requestP();
outDATA();
char chose;
cout << "是否再次请求分配?是请按Y/y,否请按N/n:\n";
while (1) {
cin >> chose;
if (chose == 'Y' || chose == 'y' || chose == 'N' || chose == 'n')
break;
else {
cout << "请按要求重新输入:\n";
continue;
}
}
if (chose == 'Y' || chose == 'y')
continue;
else break;
}
return 0;
}
采用银行家算法来避免死锁是非常可靠的,同时也是非常保守的,因为它限制了进程对资源的存取,从而降低了并发运行的程度。
四:死锁的检测(死锁检测算法)
死锁检测算法的思想
不再预防或者避免死锁,而是允许进入死锁状态,定时调用死锁检测算法,发现死锁则采用死锁恢复机制。