本文是操作系统资源并发申请管理的模拟,主要加深对死锁预防算法的认识。代码在centos上测试通过。
银行家算法(Banker's Algorithm)是一个避免死锁(Deadlock)的著名算法,是由艾兹格·迪杰斯特拉在1965年为T.H.E系统设计的一种避免死锁产生的算法。它以银行借贷系统的分配策略为基础,判断并保证系统的安全运行。
如果某一客户的交易认为对银行的后续业务产生不安全风险,那么就延迟或不进行这笔交易。算法的重点对于安全和不安全状态的定义。
如果存在一个由系统中所有进程构成的安全序列P1,…,Pn,则系统处于安全状态。安全状态一定是没有死锁发生。
不存在安全序列,但是不一定导致死锁发生。
我们可以把操作系统看作是银行家,操作系统管理的资源相当于银行家管理的资金,进程向操作系统请求分配资源相当于用户向银行家贷款。
为保证资金的安全,银行家规定:
(1) 当一个顾客对资金的最大需求量不超过银行家现有的资金时就可接纳该顾客;
(2) 顾客可以分期贷款,但贷款的总数不能超过最大需求量;
(3) 当银行家现有的资金不能满足顾客尚需的贷款数额时,对顾客的贷款可推迟支付,但总能使顾客在有限的时间里得到贷款;
(4) 当顾客得到所需的全部资金后,一定能在有限的时间里归还所有的资金.
按上面的规则,操作系统为进程分配资源,当进程首次申请资源时,要测试该进程对资源的最大需求量,如果系统现存的资源可以满足它的最大需求量则按当前的申请量分配资源,否则就推迟分配。
当进程在执行中继续申请资源时,先测试该进程本次申请的资源数是否超过了该资源所剩余的总量。若超过则拒绝分配资源,若能满足则按当前的申请量分配资源,如果分配导致不安全的话也要推迟分配。
那就说明当前申请分配后,系统是安全的。否则是不安全的,需要继续等待。
除了核心算法外,如何自动模拟多进程申请/释放资源的无序竞争过程呢,就需要一个模拟器来辅助助。模拟器主要增加了以下几个模块,这部分这里不细讲,避免偏离重点,有兴趣的朋友了解评论区可以联系我。
下面是C代码,在linux下运行(centos 7),调节下面宏的大小,可以控制程序运行的时长,当申请记录满时程序结束,并打印申请记录。
#define MAX_HISTORY_RECORE_NUM 50 /* 资源申请成功的记录条数,日志满时,程序结束。调整此参数,可以控制程序运行时长*/
/*
* 银行家算法的模拟。
* 2022/12/5 created by senllang
* 介绍:
* 银行每次交易检查的目的,主要保证每次交易后都处在安全状态,也就是有一条安全路径。
* 如果当前银行资源满足不了申请者,判定为死锁可能发生。
* 如何避免呢,也就是,可以按照上一次的安全路径中的顺序进行分配资源,
* 或者找到另一条安全路径,就会解除死锁。
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//#include
/* banker relation */
#define PNUM 5 /* 申请者数量 */
#define RNUM 3 /* 资源类型数量 */
#define MAX_HISTORY_RECORE_NUM 100 /* 资源申请成功的记录条数,日志满时,程序结束。调整此参数,可以控制程序运行时长*/
/* 当前资源的可用数量,初始值为最大资源数量 */
int available_t[RNUM] = {10, 5, 7};
/* 已经分配的资源列表,初始值合为0 */
int allocation_t[PNUM][RNUM] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}};
/* 每种资源,对于每个申请者最大可申请的数量,为预设值 */
int max_t[PNUM][RNUM] = {{7, 5, 3}, {3, 2, 2}, {9, 0, 2}, {2, 2, 2}, {4, 3, 3}};
/* 每个申请者需要的资源数量 */
int need_t[PNUM][RNUM] = {{7, 5, 3}, {3, 2, 2}, {9, 0, 2}, {2, 2, 2}, {4, 3, 3}};
/*
* 记录安全路径。
*/
static int safePath[PNUM] = {0};
/* 记录申请记录 */
struct recordData
{
int pnum;
int allocated[RNUM];
};
struct recordList
{
int offset;
struct recordData record[MAX_HISTORY_RECORE_NUM];
};
static struct recordList historyAllocated_t = {0};
/* 多进程同步 IPC相关信息 */
#define IPC_SHM_KEY 0x1205
static int shmid = -1;
static char *g_shm_base = NULL;
int *available = NULL;
int *allocation = NULL;
int *max = NULL;
int *need = NULL;
struct recordList *historyAllocated = NULL;
/* 进程管理相关变量定义 */
static int subprocess_pid[PNUM] = {0};
int *lock_flag = NULL;
#define TRY_COUNT_MAX 10000
int MyProc = -1;
int MyPos = -1;
int *MyNeed = NULL;
int *MyAllocation = NULL;
/* 算法相关接口 */
bool banker();
bool SafeCheck();
void rollback(int req[]);
void releaseResource();
bool ResourceMgr();
int requestResource(int req[]);
void RandomRegister();
int insertHistory(int pnum, int req[]);
void display();
void displaySafePath();
void displayAllocatedHistory();
void displayRequest(int req[]);
/* IPC通信管理相关接口 */
int GetShmSize();
int InitializeEnvironment();
int DestroyEnvironment();
void lock();
void unlock();
/* 进程管理操作接口 */
int CreateSubProcess();
int ProcessMain();
int BaseInit();
int DestroySubProcess();
int GetStatus();
void SetStopStatus();
int main()
{
// In the Head
InitializeEnvironment();
display();
CreateSubProcess();
// La la la
// The End
DestroySubProcess();
displayAllocatedHistory();
DestroyEnvironment();
return EXIT_SUCCESS;
}
void displaySafePath()
{
printf("safe check path:\n");
for(int i = 0; i < PNUM; i++)
{
printf("P%-2d ", safePath[i]);
if(i < PNUM-1)
printf(" -> ");
}
printf("\n");
}
void displayAllocatedHistory()
{
printf("The history record of allocated :\n");
printf("%-3s\t%-10s\t%-10s\n","NO ","process", "allocation");
for(int i = 0; i < historyAllocated->offset; i++)
{
printf("%-3d\tP%-9d\t",i,historyAllocated->record[i].pnum);
for (int j = 0; j < RNUM; j++)
printf("%2d ", historyAllocated->record[i].allocated[j]);
printf("\n");
}
}
int insertHistory(int pnum, int req[])
{
if(historyAllocated->offset >= MAX_HISTORY_RECORE_NUM)
{
printf("The list of history is full.\n");
return -1;
}
historyAllocated->record[historyAllocated->offset].pnum = pnum;
for(int i = 0; i < RNUM; i++)
historyAllocated->record[historyAllocated->offset].allocated[i] = req[i];
historyAllocated->offset++;
return 1;
}
void display()
{
printf("The resource state now:\n");
printf("%-10s\t%-10s\t%-10s\t%-10s\t%-10s\n", "process", " max", "allocation", " need", "available");
for (int i = 0; i < PNUM; i++)
{
int j = 0;
printf("P%-9d\t", i);
for (j = 0; j < RNUM; j++)
printf("%2d ", *(max+(i*RNUM)+j));
printf("\t");
for (j = 0; j < RNUM; j++)
printf("%2d ", *(allocation+(i*RNUM)+j));
printf("\t");
for (j = 0; j < RNUM; j++)
printf("%2d ", *(need+(i*RNUM)+j));
printf("\t");
if (i == 0)
for (j = 0; j < RNUM; j++)
printf("%2d ", available[j]);
printf("\n");
}
}
void displayRequest(int req[])
{
printf("P%d request:", MyProc);
for(int j = 0; j < RNUM; j++)
{
printf("%-3d", req[j]);
}
printf("\n");
}
void rollback(int req[])
{
int j = 0;
for (j = 0; j < RNUM; j++)
{ // rollback if a process's request is not allowed
MyAllocation[j] -= req[j];
MyNeed[j] += req[j];
available[j] += req[j];
}
}
bool SafeCheck()
{
int work[RNUM];
bool finish[PNUM];
int i = 0, j = 0;
int safePathOffset = 0;
int *needBase = need;
int *allocationBase = allocation;
/* 每次使用前,先赋初值;finish[]全部为false; work[]为available[] */
for (i = 0; i < PNUM; i++)
finish[i] = false;
for (j = 0; j < RNUM; j++)
work[j] = available[j];
for (i = 0; i < PNUM; i++)
safePath[i] = 0;
///is safe state?
int num = PNUM, flag = 0;
while (num-- && flag != PNUM)
{
/* 尝试一遍检查,最多尝试num遍的检查。每遍检查把当前符合的申请者标记出来。 */
for (i = 0; i < PNUM; i++)
{
/* 跳过已经安全检查过的申请者 */
if (finish[i] == true)
continue;
needBase = need + i * RNUM;
/* 查找下一个符合的申请者 */
for (j = 0; j < RNUM; j++)
{
/*
* 如果有一种资源需求大于当前可用资源数量,
* 那么不再继续比较其它资源。
*/
if (needBase[j] > work[j])
break;
}
/*
* RNUM个资源都符合的情况,那么work中增加该申请者已经占用的资源。
* 也就是假设该申请者释放全部持有资源后,下一个申请者的可用资源数量。
*/
if (j == RNUM)
{
flag++;
allocationBase = allocation + i * RNUM;
for (j = 0; j < RNUM; j++)
{
work[j] += allocationBase[j];
}
finish[i] = true;
safePath[safePathOffset++] = i;
}
}
}
/* 如果所有申请者都符合,那么返回true,否则返回false */
if (flag == PNUM)
return true;
else
return false;
}
bool banker()
{
int request[RNUM] = {0};
int i = 0, j = 0, id;
bool wait = false;
bool issafe = false;
while (GetStatus())
{
/* 如果已经提过申请,不再产生新的请求,继续提交银行处理。 */
if(!wait)
{
if(requestResource(request) == 0)
{
// ignore 0 request
continue;
}
}
/* 开始交易前重置处理结果。 */
wait = false;
lock();
displayRequest(request);
//banker algorithm
for (j = 0; j < RNUM; j++)
{
if (request[j] > MyNeed[j])
{
/* 请求不合法,超过了自己的合同需求。 */
break;;
}
if (request[j] > available[j])
{
/* 需要等待可用资源充足时再进行业务处理。 */
printf("P%d request over current available resource.\n", MyProc);
display();
wait = true;
break;
}
}
if (j == RNUM)
{
/* 预分配 */
for (i = 0; i < RNUM; i++)
{
available[i] -= request[i];
MyAllocation[i] += request[i];
MyNeed[i] -= request[i];
}
/*
* 进行安全检查
*/
issafe = SafeCheck();
if (issafe)
{
/* 存在安全路径,分配成功,将记录加入历史记录列表中。 */
if(insertHistory(MyProc, request) < 0)
{
printf("history space is not enough. We will rollback and exit.\n");
unlock();
SetStopStatus();
return true;
}
printf("P%d 可以申请,结果如下\n", MyProc);
display();
displaySafePath();
/* 进行持有资源的管理;我们假设使用时间为0,即当我们拿到所需资源后,立即释放持有资源。 */
if(ResourceMgr())
{
/* 如果已经分配了所需足够资源,那么该申请者处理完后就结束。 */
unlock();
return true;
}
}
else
{
/* 不存在安全路径,回退分配。 */
rollback(request);
printf("P%d 该请求会造成不安全\n", MyProc);
display();
/* 等待可用资源充足时再来交易。 */
wait = true;
}
}
unlock();
}
return true;
}
void releaseResource()
{
for(int i = 0; i < RNUM; i++)
{
available[i] += MyAllocation[i];
MyNeed[i] = MyAllocation[i];
MyAllocation[i] = 0;
}
}
bool ResourceMgr()
{
int check = 0;
for(int i = 0; i < RNUM; i++)
check += MyNeed[i];
if(0 == check)
{
releaseResource();
return true;
}
return false;
}
int requestResource(int req[])
{
int check = 0;
for(int i = 0; i < RNUM; i++)
{
req[i] = MyNeed[i];
req[i] = random() % (req[i]+1);
}
for(int i = 0; i < RNUM; i++)
{
check += req[i];
}
return check;
}
void RandomRegister()
{
srand((unsigned int)time(NULL));
}
int GetShmSize()
{
int shmSize = 0;
shmSize += sizeof(available_t);
shmSize += sizeof(allocation_t);
shmSize += sizeof(max_t);
shmSize += sizeof(need_t);
shmSize += sizeof(struct recordList);
shmSize += sizeof(int)*2; // IPC control infomation
return shmSize;
}
int InitializeEnvironment()
{
int ret = 0;
char *pos = NULL;
shmid = shmget(ftok(".", IPC_SHM_KEY), GetShmSize(), IPC_CREAT|0666);
if(shmid == -1)
{
printf("create shared memory failure. errno[%d]\n", errno);
return -1;
}
g_shm_base = (char*)shmat(shmid, NULL, 0);
pos = g_shm_base;
// 共享内存赋值
lock_flag = (int *)pos;
pos += sizeof(int) * 2;
*lock_flag = PNUM+1; /* all of process can't start before ready . */
*(lock_flag+1) = 0x01; /* 进程状态同步标志,初始化为运行。 */
available = (int *)pos;
memcpy(available, available_t, sizeof(available_t));
allocation = (int *)(pos + sizeof(available_t));
memcpy(allocation, allocation_t, sizeof(allocation_t));
max = (int *)(pos + sizeof(available_t)+sizeof(allocation_t));
memcpy(max, max_t, sizeof(max_t));
need = (int *)(pos + sizeof(available_t)+sizeof(allocation_t)+sizeof(max_t));
memcpy(need, need_t, sizeof(need_t));
historyAllocated = (struct recordList *)(pos + sizeof(available_t)+sizeof(allocation_t)+sizeof(max_t)+sizeof(need_t));
return ret;
}
int DestroyEnvironment()
{
int ret = 0;
if(NULL != g_shm_base)
ret = shmdt(g_shm_base);
ret = shmctl(shmid, IPC_RMID, NULL);
return ret;
}
static void inline fence()
{
__asm__ __volatile__("" ::: "memory");
}
// let's use a light method to control synchronization.
void lock()
{
int count = 0;
while(1)
{
do
{
int old = __sync_val_compare_and_swap(lock_flag, PNUM, MyProc);
if(old == PNUM)
{
return;
}
count ++;
}while(count < TRY_COUNT_MAX);
}
fence();
if(count == TRY_COUNT_MAX)
{
printf("system status may not normal. \n");
exit(0);
}
}
void unlock()
{
int old = __sync_val_compare_and_swap(lock_flag, MyProc, PNUM);
if(old != MyProc)
{
printf("P%d unlock failure. \n",MyProc);
}
fence();
}
/* 多进程运行状态同步,当进程需要退出时,状态为0,正常运行状态为大于1. */
int GetStatus()
{
return *(lock_flag+1);
}
/* 进程需要退出时,设置状态为0。 */
void SetStopStatus()
{
__sync_fetch_and_and(lock_flag+1, 0x00);
}
int CreateSubProcess()
{
int ret = 0;
int pid = 0;
for (int i = 0; i < PNUM; i++)
{
pid = fork();
if(pid < 0)
{
printf("forking process %d failure, errno[%d] \n", i, errno);
return ret;
}
else if(pid == 0)
{
// subprocess starting
MyProc = i;
ret = ProcessMain();
exit(ret);
}
else
{
subprocess_pid[i] = pid;
ret ++;
}
}
// here , maybe notice subprocess start
*lock_flag = PNUM;
return ret;
}
int ProcessMain()
{
int ret = 0;
// process level initialiaze
BaseInit();
/*
display();
if (issafe() == true)
{
printf("The system is safe now!\n");
}
else
printf("The system is not safe\n");
*/
//test banker()
banker();
return 0;
}
int BaseInit()
{
MyPos = MyProc*RNUM;
MyNeed = need+MyPos;
MyAllocation = allocation+MyPos;
RandomRegister();
return 0;
}
int DestroySubProcess()
{
int i = 0;
int pnum = 0;
int ret = 0;
while(1)
{
int pid = wait(&ret);
pnum ++;
if(pnum == PNUM)
break;
/* 实际启动的进程数少时,根据PID记录判断 */
for(i = 0; i < PNUM; i++)
{
if(subprocess_pid[i] != 0)
{
break;
}
}
if(i == PNUM)
break;
}
return 0;
}
作者邮箱:[email protected]
如有错误或者疏漏欢迎指出,互相学习。
注:未经同意,不得转载!