进程同步及避免死锁经典问题
2015-07-12 19:49
459 查看
定义PV操作的含义:PV操作是由P操作原语和V操作原语组成(原语是不可中断的过程),对信号量进行操作,具体定义如下:
P(S):将信号量S的值减1,即S=S-1;如果S>0,则该进程继续执行;否则该进程置为等待状态,排入等待队列。
V(S):将信号量S的值加1,即S=S+1;如果S>0,则该进程继续执行;否则释放队列中第一个等待信号量的进程。
P操作相当于申请资源,而V操作相当于释放资源。
一、读者-写者问题
计算机系统中的数据(文件、记录)常被多个进程共享,但其中某些进程可能只要求读数据(称为读者Reader),另一些进程则要求修改数据(称为写者Writer)。就共享数据而言,Reader和Writer是两组并发进程共享一组数据区,要求:
(1)允许多个读者同时执行读操作;
(2)不允许读者、写者同时操作;
(3)不允许多个写者同时操作。
Reader和Writer的同步问题分为读者优先、弱写者优先(公平竞争)和强写者优先三种情况,它们的处理方式不同:
1、读者优先
2、写者优先(弱优先性)
3、写者优先(强优先性)
保证了写者优先,只要有写者在写,后来的读者写者中优先执行写者。
二、生产者-消费者
生产者-消费者问题指的是在若干个生产者生产产品,放入共享的缓冲区,若干消费者从缓冲区中取出产品消费;生产者和消费者是互斥访问共享的缓冲区的,若缓冲区没有空位生产者要等待,若缓冲区没有产品消费者要等待,以下通过代码来演示这个过程。
注:以下代码修改自http://blog.sina.com.cn/s/blog_58069bd20100as5w.html
三、哲学家进餐
问题描述:
五个哲学家围坐在一张圆桌周围,每个哲学家面前都有一盘通心粉。由于通心粉很滑,所以需要两把叉子才能夹住。相邻两个盘子之间放有一把叉子,哲学家的生活中有两种交替活动时段:即吃饭和思考。当一个哲学家觉得饿了时,他就试图分两次去取其左边和右边的叉子,每次拿一把,但不分次序。如果成功地得到了两把叉子,就开始吃饭,吃完后放下叉子继续思考。关键问题是:能为每一个哲学家写一段描述其行为的程序,且决不会死锁吗(最好能达到最大的并行程度)?
解法思路:
1、为每个哲学家分配一个semaphore[i]初始值为0,最大值为1
2、当一个哲学家试图进餐时先通过互斥量获得对哲学家们的状态state的独占性访问
3、改变i哲学家的状态为HUNGRY,测试其左右哲学家是否在用餐,若左右哲学家都不在用餐,则i哲学家可以用餐,改变state[i]=EATING,释放其semaphore[i],释放state,拿起餐插成功进餐;若左右哲学家有人在进餐,释放state,i哲学家等待
4、哲学家用餐完毕,获取对state的独占性访问,改变state[i]=THINKING,此时i哲学家释放了两个餐插,测试其左右哲学家是否在HUNGRY状态
5、重复2-4步骤直到达到最大进餐次数
参考如下代码:(以下代码修改自http://blog.163.com/diaoshuo_1/blog/static/318902012009418115121514/)
四、银行家算法(避免死锁)
死锁产生的条件:
互斥条件:进程要求对所分配的资源进行排它性控制,即在一段时间内某资源仅为一进程所占用。
请求和保持条件:当进程因请求资源而阻塞时,对已获得的资源保持不放。(
不可剥夺条件:进程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放。
环路等待条件:在发生死锁时,必然存在一个进程--资源的环形链。
银行家问题描述:
银行家算法是避免死锁的一种重要方法,防止死锁的机构只能确保上述四个条件之一不出现,则系统就不会发生死锁。通过这个算法可以用来解决生活中的实际问题,如银行贷款等。
程序实现思路银行家算法顾名思义是来源于银行的借贷业务,一定数量的本金要应多个客户的借贷周转,为了防止银行加资金无法周转而倒闭,对每一笔贷款,必须考察其是否能限期归还。在操作系统中研究资源分配策略时也有类似问题,系统中有限的资源要供多个进程使用,必须保证得到的资源的进程能在有限的时间内归还资源,以供其他进程使用资源。如果资源分配不得到就会发生进程循环等待资源,则进程都无法继续执行下去的死锁现象。
把一个进程需要和已占有资源的情况记录在进程控制中,假定进程控制块PCB其中“状态”有就绪态、等待态和完成态。当进程在处于等待态时,表示系统不能满足该进程当前的资源申请。“资源需求总量”表示进程在整个执行过程中总共要申请的资源量。显然,,每个进程的资源需求总量不能超过系统拥有的资源总数, 银行算法进行资源分配可以避免死锁.
算法流程图:
![](http://img.blog.csdn.net/20150714110116254?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
Safe算法流程:
![](http://img.blog.csdn.net/20150714105915830?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
实例分析(参考操作系统书籍及http://www.cnblogs.com/xuxu8511/archive/2012/04/06/2435053.html):
某系统有R1,R2,R3共3中资源,在T0时刻P0,P1,P2,P3和P4这5个进程对资源的占用和需求情况如下表1,此时系统的可用资源向量为(3,3,2)。试问:
1、T0时刻系统是否存在安全序列?
2、P1请求资源:P1发出请求向量Request(1,0,2),系统是否接受该请求?请使用银行家算法检查
3、P4请求资源:P4发出请求向量Request(3,3,0),系统按银行家算法检查.
4、P0请求资源:P0发出请求向量Request(0,2,0),系统按银行家算法检查.
表1 T0时刻的资源分配表
说明:
1、T0时刻系统是否存在安全序列?
Available > Need1 ----> 可用资源分配给P1,直到P1进程执行完成,然后Available = Available + Allocation1 = (5,3,2)
Available > Need3 -----> 可用资源分配给P3,直到P3进程执行完成,然后Available = Available + Allocation3 = (7,4,3)
Available> Need4.....
得到安全序列为:P1,P3,P4,P2,P0
2、P1请求资源:P1发出请求向量Request(1,0,2),系统是否接受该请求?请使用银行家算法检查
第一步(假分配检查):把Request分配给P1,必须满足Request要小于Available,Request要小于Need。
Request(1,0,2)< Available(3,3,2)
Request(1,0,2)< Need(1,2,2)
因为满足第一步检查,进入第二层检查(安全序列检查)。
第二步(安全序列检查):建立安全性检查表
如果 Work > Need ,那么执行Work+Allocation,得到:
找到Need<Work的进程,如果没有找到这样的进程而进程集合没有执行,则算法返回,得到不存在安全序列结果,否则继续执行该算法。
这里我们找到了P3进程。修改安全序列检查表:
这样一直执行到所有的进程到完成,以完成该安全序列检查表:
这样就找到了整个安全序列为:P1,P3,P4,P0,P2
3、4小问也是同样的解题过程。这里不赘述...
代码实现(参考自http://blog.csdn.net/orange_os/article/details/7417204):
以以上实例来测试:
请输入进程的数目:5
请输入资源的种类:3
请输入每个进程最多所需的各资源数,按照5x3矩阵输入
7 5 3
3 2 2
9 0 2
2 2 2
4 3 3
请输入每个进程已分配的各资源数,也按照5x3矩阵输入
0 1 0
2 0 0
3 0 2
2 1 1
0 0 2
请输入各个资源现有的数目:
3 3 2
系统是安全的
安全序列:
1-->3-->0-->2-->4
请输入要申请资源的进程号(注:第1个进程号为0,依次类推)
1
请输入进程所请求的各资源的数量
1 0 2
系统是安全的
安全序列:
1-->3-->0-->2-->4
同意分配请求!
您还想再次请求分配吗?是请按y/Y,否请按其它键
y
请输入要申请资源的进程号(注:第1个进程号为0,依次类推)
4
请输入进程所请求的各资源的数量
3 3 0
您输入的请求数超过系统有的资源数!请重新输入!
系统是不安全的
您的请求被拒绝!
您还想再次请求分配吗?是请按y/Y,否请按其它键
y
请输入要申请资源的进程号(注:第1个进程号为0,依次类推)
0
请输入进程所请求的各资源的数量
0 2 0
系统是不安全的
您的请求被拒绝!
注:以上代码均是在Win7+VS2012环境中运行。
P(S):将信号量S的值减1,即S=S-1;如果S>0,则该进程继续执行;否则该进程置为等待状态,排入等待队列。
V(S):将信号量S的值加1,即S=S+1;如果S>0,则该进程继续执行;否则释放队列中第一个等待信号量的进程。
P操作相当于申请资源,而V操作相当于释放资源。
一、读者-写者问题
计算机系统中的数据(文件、记录)常被多个进程共享,但其中某些进程可能只要求读数据(称为读者Reader),另一些进程则要求修改数据(称为写者Writer)。就共享数据而言,Reader和Writer是两组并发进程共享一组数据区,要求:
(1)允许多个读者同时执行读操作;
(2)不允许读者、写者同时操作;
(3)不允许多个写者同时操作。
Reader和Writer的同步问题分为读者优先、弱写者优先(公平竞争)和强写者优先三种情况,它们的处理方式不同:
1、读者优先
int rc = 0; //记录有多少个读者在读 semaphore rc_semaphore = 1; //对rc互斥访问的信号量 semaphore write_semaphore = 1; //保证读写互斥的信号量 void reader() { do { P(rc_semaphore); rc++; if(1==rc) P(write_semaphore); V(rc_semaphore); read operating..... P(rc_semaphore); rc--; if(0==rc) V(write_semaphore); v(rc_semaphore); } while (true); } void writer() { do { P(write_semaphore); write operating..... V(write_semaphore); } while (true); }缺点:当不断有读者来读取时,写者就会陷入无限的等待。
2、写者优先(弱优先性)
int rc = 0; //记录有多少个读者在读 semaphore rc_semaphore = 1; //对rc互斥访问的信号量 semaphore write_semaphore = 1; //保证读写互斥的信号量 semaphore read_semaphore = 1; void reader() { do { P(read_semaphore); P(rc_semaphore); rc++; if(1==rc) P(write_semaphore); V(rc_semaphore); V(read_semaphore); Reading the file.... P(rc_semaphore); rc--; if(0==rc) V(write_semaphore); V(rc_semaphore); } while (true); } void writer() { do { P(read_semaphore); P(write_semaphore); writing the file..... V(write_semaphore); V(read_semaphore); } while (true); }缺点:这个是按照先来先服务的原则进行读写的,比如当一个进程在写时,来了N个进程读,读者们都在等写者结束,此时又来了个写者,第二个写者就要等前面N个读者读完后才能写。
3、写者优先(强优先性)
int rc = 0; int wc = 0; semaphore write_semaphore = 1; semaphore rc_semaphore =1; semaphore wc_semaphore = 1; void reader() { do { P(read_semaphore); P(rc_semaphore); rc++; if(1==rc) P(write_semaphore); V(rc_semaphore); V(read_semaphore); reading the file..... P(rc_semaphore); rc--; if(0==rc) V(write_semaphore); V(rc_semaphore); } while (true); } void writer() { do { P(wc_semaphore); wc++; if(1==wc) P(read_semaphore); V(wc_semaphore); P(write_semaphore); writing the file...... V(write_semaphore); P(wc_semaphore); wc--; if(0==wc) V(read_semaphore); V(wc_semaphore); } while (true); }
保证了写者优先,只要有写者在写,后来的读者写者中优先执行写者。
二、生产者-消费者
生产者-消费者问题指的是在若干个生产者生产产品,放入共享的缓冲区,若干消费者从缓冲区中取出产品消费;生产者和消费者是互斥访问共享的缓冲区的,若缓冲区没有空位生产者要等待,若缓冲区没有产品消费者要等待,以下通过代码来演示这个过程。
注:以下代码修改自http://blog.sina.com.cn/s/blog_58069bd20100as5w.html
#include <windows.h> #include <iostream> using namespace std; const unsigned short SIZE_OF_BUFFER = 10; //缓冲区长度 unsigned short ProductID = 0; //产品号 unsigned short ConsumeID = 0; //将被消耗的产品号 unsigned short in = 0; //产品进缓冲区时的缓冲区下标 unsigned short out = 0; //产品出缓冲区时的缓冲区下标 int g_buffer[SIZE_OF_BUFFER]; //缓冲区是个循环队列 HANDLE g_hMutex; //用于线程间的互斥 HANDLE g_hFullSemaphore; //当缓冲区满时迫使生产者等待 HANDLE g_hEmptySemaphore; //当缓冲区空时迫使消费者等待 DWORD WINAPI Producer(LPVOID); //生产者线程 DWORD WINAPI Consumer(LPVOID); //消费者线程 int main() { //创建各个互斥信号 g_hMutex = CreateMutex(NULL,FALSE,NULL); g_hFullSemaphore = CreateSemaphore(NULL,0,SIZE_OF_BUFFER,NULL); g_hEmptySemaphore = CreateSemaphore(NULL,SIZE_OF_BUFFER,SIZE_OF_BUFFER,NULL); //调整下面的数值,可以发现,当生产者个数多于消费者个数时, //生产速度快,生产者经常等待消费者;反之,消费者经常等待 const unsigned short PRODUCERS_COUNT = 3; //生产者的个数 const unsigned short CONSUMERS_COUNT = 2; //消费者的个数 //总的线程数 const unsigned short THREADS_COUNT = PRODUCERS_COUNT+CONSUMERS_COUNT; HANDLE hThreads[THREADS_COUNT]; //各线程的handle DWORD producerID[PRODUCERS_COUNT]={1,2,3}; //生产者线程的标识符 DWORD consumerID[CONSUMERS_COUNT]={1,2}; //消费者线程的标识符 //创建生产者线程 for (int i=0;i<PRODUCERS_COUNT;++i){ hThreads[i]=CreateThread(NULL,0,Producer,&producerID[i],0,NULL); if (hThreads[i]==NULL) return -1; } //创建消费者线程 for (int i=0;i<CONSUMERS_COUNT;++i){ hThreads[PRODUCERS_COUNT+i]=CreateThread(NULL,0,Consumer,&consumerID[i],0,NULL); if (hThreads[i]==NULL) return -1; } while(true){ Sleep(10000); } return 0; } //生产一个产品。简单模拟了一下,仅输出新产品的ID号 void Produce() { std::cerr << "Producing " << ++ProductID << " ... "; std::cerr << "Succeed" << std::endl; } //把新生产的产品放入缓冲区 void Append() { std::cerr << "Appending a product ... "; g_buffer[in] = ProductID; in = (in+1)%SIZE_OF_BUFFER; std::cerr << "Succeed" << std::endl; //输出缓冲区当前的状态 for (int i=0;i<SIZE_OF_BUFFER;++i){ std::cout << i <<": " << g_buffer[i]; if (i==in) std::cout << " <-- 生产"; if (i==out) std::cout << " <-- 消费"; std::cout << std::endl; } } //从缓冲区中取出一个产品 void Take() { std::cerr << "Taking a product ... "; ConsumeID = g_buffer[out]; g_buffer[out] = 0; out = (out+1)%SIZE_OF_BUFFER; std::cerr << "Succeed" << std::endl; //输出缓冲区当前的状态 for (int i=0;i<SIZE_OF_BUFFER;++i){ std::cout << i <<": " << g_buffer[i]; if (i==in) std::cout << " <-- 生产"; if (i==out) std::cout << " <-- 消费"; std::cout << std::endl; } } //消耗一个产品 void Consume() { std::cerr << "Consuming " << ConsumeID << " ... "; std::cerr << "Succeed" << std::endl; } //生产者 DWORD WINAPI Producer(LPVOID lpPara) { while(true){ WaitForSingleObject(g_hEmptySemaphore,INFINITE); WaitForSingleObject(g_hMutex,INFINITE); Produce(); Append(); Sleep(3000); ReleaseMutex(g_hMutex); ReleaseSemaphore(g_hFullSemaphore,1,NULL); } return 0; } //消费者 DWORD WINAPI Consumer(LPVOID lpPara) { while(true){ WaitForSingleObject(g_hFullSemaphore,INFINITE); WaitForSingleObject(g_hMutex,INFINITE); Take(); Consume(); Sleep(3000); ReleaseMutex(g_hMutex); ReleaseSemaphore(g_hEmptySemaphore,1,NULL); } return 0; }
三、哲学家进餐
问题描述:
五个哲学家围坐在一张圆桌周围,每个哲学家面前都有一盘通心粉。由于通心粉很滑,所以需要两把叉子才能夹住。相邻两个盘子之间放有一把叉子,哲学家的生活中有两种交替活动时段:即吃饭和思考。当一个哲学家觉得饿了时,他就试图分两次去取其左边和右边的叉子,每次拿一把,但不分次序。如果成功地得到了两把叉子,就开始吃饭,吃完后放下叉子继续思考。关键问题是:能为每一个哲学家写一段描述其行为的程序,且决不会死锁吗(最好能达到最大的并行程度)?
解法思路:
1、为每个哲学家分配一个semaphore[i]初始值为0,最大值为1
2、当一个哲学家试图进餐时先通过互斥量获得对哲学家们的状态state的独占性访问
3、改变i哲学家的状态为HUNGRY,测试其左右哲学家是否在用餐,若左右哲学家都不在用餐,则i哲学家可以用餐,改变state[i]=EATING,释放其semaphore[i],释放state,拿起餐插成功进餐;若左右哲学家有人在进餐,释放state,i哲学家等待
4、哲学家用餐完毕,获取对state的独占性访问,改变state[i]=THINKING,此时i哲学家释放了两个餐插,测试其左右哲学家是否在HUNGRY状态
5、重复2-4步骤直到达到最大进餐次数
参考如下代码:(以下代码修改自http://blog.163.com/diaoshuo_1/blog/static/318902012009418115121514/)
/* Item The Dining Philosophers Problem 具体要求 1) 5个哲学家,先吃饭,后思考。 2) 每个哲学家都需要吃9顿饭,思考9次,然后结束。 3)吃饭时间为3~7秒的随机时间 4)思考时间为3~9秒的随机时间 */ #include <windows.h> #include <time.h> #include <stdlib.h> #include <stdio.h> #define N 5 //哲学家数目 #define MAXNUM 9 //进餐思考次数 #define LEFT (i+N-1)%N //i为左邻居编号 #define RIGHT (i+N+1)%N //i为右邻居编号 #define THINKING 0 //哲学家在思考 #define HUNGRY 1 //哲学家视图拿起叉子 #define EATING 2 //哲学家进餐 ///////线程参数 typedef struct _THREAD_ARGS{ int id;//哲学家编号 int maxnum; //进餐思考最大次数 }THREAD_ARGS, *PTHREAD_ARGS; int state ;//用来记录每位哲学家的状态 int g_philobuf_fork_num;//缓冲区中叉子数目 char mutex_g_buf[]="PS_IPC_PHILO_G_BUF"; HANDLE mutex_g_buf_h; //为了互斥访问state //Semaphores' Name char* sem_perphilo_name = {"PS_IPC_PHILO_SEM_PERPHILO_0","PS_IPC_PHILO_SEM_PERPHILO_1", "PS_IPC_PHILO_SEM_PERPHILO_2","PS_IPC_PHILO_SEM_PERPHILO_3", "PS_IPC_PHILO_SEM_PERPHILO_4"}; HANDLE sem_perphilo_h ; //每个哲学家一个semaphore,初始值为0,最大值为1 DWORD WINAPI philosopher(void *pArgs); void take_forks(int i); void put_forks(int i); void test(int i); //线程函数 DWORD WINAPI philosopher(void *pArgs) { PTHREAD_ARGS pTHREAD_ARGS; pTHREAD_ARGS=(PTHREAD_ARGS)pArgs; int i=pTHREAD_ARGS->id; int totalNum = pTHREAD_ARGS->maxnum; for(int count=0;count<totalNum;count++) { take_forks(i); srand ((DWORD)time(NULL)); int eat_t=rand()%4000 + 3000; Sleep(eat_t); printf("Philosopher %d finishes eating!this is the %d(th) eating time! eat_t:%d\n",i,count+1,eat_t); put_forks(i); srand ((DWORD)time(NULL)); int think_t= rand()%6000 + 3000; Sleep(think_t); printf("Philosopher %d finishes thinking!this is the %d(th) thinking time! think_t:%d\n",i,count+1,think_t); } printf("Philosopher %d has finished all the eating and thinking activity\n!",i); return 0; } void take_forks(int i) { WaitForSingleObject(mutex_g_buf_h,INFINITE); state[i]=HUNGRY; printf("*************************take_forks start***************************\n"); printf("Philosopher %d is hungry! Try to take the forks\n",i); printf("*************************take_forks end***************************\n\n"); test(i); ReleaseMutex(mutex_g_buf_h); WaitForSingleObject(sem_perphilo_h[i],INFINITE); } void put_forks(int i) { WaitForSingleObject(mutex_g_buf_h,INFINITE); state[i]=THINKING; g_philobuf_fork_num=g_philobuf_fork_num-2;//放下了两把叉子 printf("*************************put_forks start***************************\n"); printf("Philosopher %d puts down the fork and starts thinking! current forks num:%d\n",i,g_philobuf_fork_num); printf("*************************put_forks end***************************\n\n"); test(LEFT); test(RIGHT); ReleaseMutex(mutex_g_buf_h); } void test(int i) { LONG oldSemValue; if(state[i]==HUNGRY&&state[LEFT]!=EATING&&state[RIGHT]!=EATING) { state[i]=EATING; g_philobuf_fork_num=g_philobuf_fork_num+2;//拿起了两把叉子 printf("*************************test start***************************\n"); printf("After testing,Philosopher %d can starts eating! current forks num:%d\n",i,g_philobuf_fork_num); printf("*************************test end***************************\n\n"); ReleaseSemaphore(sem_perphilo_h[i],1,&oldSemValue); } } int main(int argc,char*argv[]) { time_t launch=time(NULL); printf("****************************************************\n"); printf("开始运行时间:%s",ctime(&launch)); printf("哲学家就餐问题\n"); printf("哲学家数量:%d\n",N); printf("进餐思考次数:%d\n",MAXNUM); printf("****************************************************\n"); DWORD Status; DWORD *ThId_Philo=new DWORD ; HANDLE *philo_h=new HANDLE ; THREAD_ARGS *ThArgs_Philo=new THREAD_ARGS ; //当前拿起的叉子数目 g_philobuf_fork_num=0; mutex_g_buf_h=CreateMutex(NULL,FALSE,mutex_g_buf); int nCounter=0; for(nCounter=0;nCounter<N;nCounter++) { sem_perphilo_h[nCounter]=(HANDLE)CreateSemaphore(NULL,0,1, sem_perphilo_name[nCounter]); } //CreateThread for(nCounter=0;nCounter<N;nCounter++) { ThArgs_Philo[nCounter].id=nCounter; ThArgs_Philo[nCounter].maxnum=MAXNUM; philo_h[nCounter]=(HANDLE)CreateThread(NULL,0,philosopher, &ThArgs_Philo[nCounter],0,&ThId_Philo[nCounter]); } /* wait for all threads to terminate*/ for(nCounter = 0; nCounter < N; nCounter++) Status = WaitForSingleObject(philo_h[nCounter], INFINITE); for(nCounter=0;nCounter<N;nCounter++) { CloseHandle(sem_perphilo_h[nCounter]); } CloseHandle(mutex_g_buf_h); system("pause"); return 0; }
四、银行家算法(避免死锁)
死锁产生的条件:
互斥条件:进程要求对所分配的资源进行排它性控制,即在一段时间内某资源仅为一进程所占用。
请求和保持条件:当进程因请求资源而阻塞时,对已获得的资源保持不放。(
不可剥夺条件:进程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放。
环路等待条件:在发生死锁时,必然存在一个进程--资源的环形链。
银行家问题描述:
银行家算法是避免死锁的一种重要方法,防止死锁的机构只能确保上述四个条件之一不出现,则系统就不会发生死锁。通过这个算法可以用来解决生活中的实际问题,如银行贷款等。
程序实现思路银行家算法顾名思义是来源于银行的借贷业务,一定数量的本金要应多个客户的借贷周转,为了防止银行加资金无法周转而倒闭,对每一笔贷款,必须考察其是否能限期归还。在操作系统中研究资源分配策略时也有类似问题,系统中有限的资源要供多个进程使用,必须保证得到的资源的进程能在有限的时间内归还资源,以供其他进程使用资源。如果资源分配不得到就会发生进程循环等待资源,则进程都无法继续执行下去的死锁现象。
把一个进程需要和已占有资源的情况记录在进程控制中,假定进程控制块PCB其中“状态”有就绪态、等待态和完成态。当进程在处于等待态时,表示系统不能满足该进程当前的资源申请。“资源需求总量”表示进程在整个执行过程中总共要申请的资源量。显然,,每个进程的资源需求总量不能超过系统拥有的资源总数, 银行算法进行资源分配可以避免死锁.
算法流程图:
Safe算法流程:
实例分析(参考操作系统书籍及http://www.cnblogs.com/xuxu8511/archive/2012/04/06/2435053.html):
某系统有R1,R2,R3共3中资源,在T0时刻P0,P1,P2,P3和P4这5个进程对资源的占用和需求情况如下表1,此时系统的可用资源向量为(3,3,2)。试问:
1、T0时刻系统是否存在安全序列?
2、P1请求资源:P1发出请求向量Request(1,0,2),系统是否接受该请求?请使用银行家算法检查
3、P4请求资源:P4发出请求向量Request(3,3,0),系统按银行家算法检查.
4、P0请求资源:P0发出请求向量Request(0,2,0),系统按银行家算法检查.
表1 T0时刻的资源分配表
MAX | Allocation | Need | Available | |
P0 | 7 5 3 | 0 1 0 | 7 4 3 | 3 3 2 |
P1 | 3 2 2 | 2 0 0 | 1 2 2 | |
P2 | 9 0 2 | 3 0 2 | 6 0 0 | |
P3 | 2 2 2 | 2 1 1 | 0 1 1 | |
P4 | 4 3 3 | 0 0 2 | 4 3 1 |
1、T0时刻系统是否存在安全序列?
Available > Need1 ----> 可用资源分配给P1,直到P1进程执行完成,然后Available = Available + Allocation1 = (5,3,2)
Available > Need3 -----> 可用资源分配给P3,直到P3进程执行完成,然后Available = Available + Allocation3 = (7,4,3)
Available> Need4.....
得到安全序列为:P1,P3,P4,P2,P0
2、P1请求资源:P1发出请求向量Request(1,0,2),系统是否接受该请求?请使用银行家算法检查
第一步(假分配检查):把Request分配给P1,必须满足Request要小于Available,Request要小于Need。
Request(1,0,2)< Available(3,3,2)
Request(1,0,2)< Need(1,2,2)
因为满足第一步检查,进入第二层检查(安全序列检查)。
第二步(安全序列检查):建立安全性检查表
Work | Need | Allocation | Work+Allocation | Finish | |
P1 | 2 3 0 | 0 2 0 | 3 0 2 | ||
Work | Need | Allocation | Work+Allocation | Finish | |
P1 | 2 3 0 | 0 2 0 | 3 0 2 | 5 3 2 | true |
5 3 2 | |||||
这里我们找到了P3进程。修改安全序列检查表:
Work | Need | Allocation | Work+Allocation | Finish | |
P1 | 2 3 0 | 0 2 0 | 3 0 2 | 5 3 2 | true |
P3 | 5 3 2 | 0 1 1 | 2 1 1 | 7 4 3 | true |
7 4 3 | |||||
Work | Need | Allocation | Work+Allocation | Finish | |
P1 | 2 3 0 | 0 2 0 | 3 0 2 | 5 3 2 | true |
P3 | 5 3 2 | 0 1 1 | 2 1 1 | 7 4 3 | true |
P4 | 7 4 3 | 4 3 1 | 0 0 2 | 7 4 5 | true |
P0 | 7 4 5 | 7 4 3 | 0 1 0 | 7 5 5 | true |
P2 | 7 5 5 | 6 0 0 | 3 0 2 | 10 5 7 | true |
3、4小问也是同样的解题过程。这里不赘述...
代码实现(参考自http://blog.csdn.net/orange_os/article/details/7417204):
/*以下代码仅是求出了一种可行序列,也许存在多种可行序列*/ #include <iostream> using namespace std; #define MAXPROCESS 50 /*最大进程数*/ #define MAXRESOURCE 100 /*最大资源数*/ int ***AILABLE[MAXRESOURCE]; /*可用资源数组*/ int MAX[MAXPROCESS][MAXRESOURCE]; /*最大需求矩阵*/ int ALLOCATION[MAXPROCESS][MAXRESOURCE]; /*分配矩阵*/ int NEED[MAXPROCESS][MAXRESOURCE]; /*需求矩阵*/ int REQUEST[MAXPROCESS][MAXRESOURCE]; /*进程需要资源数*/ bool FINISH[MAXPROCESS]; /*系统是否有足够的资源分配*/ int p[MAXPROCESS]; /*记录序列*/ int m,n; /*m个进程,n个资源*/ void Init(); bool Safe(); void Bank(); int main() { Init(); Safe(); Bank(); system("pause"); return 0; } void Init() { int i,j; cout<<"请输入进程的数目:"; cin>>m; cout<<"请输入资源的种类:"; cin>>n; cout<<"请输入每个进程最多所需的各资源数,按照"<<m<<"x"<<n<<"矩阵输入"<<endl; for(i=0;i<m;i++) for(j=0;j<n;j++) cin>>MAX[i][j]; cout<<"请输入每个进程已分配的各资源数,也按照"<<m<<"x"<<n<<"矩阵输入"<<endl; for(i=0;i<m;i++) { for(j=0;j<n;j++) { cin>>ALLOCATION[i][j]; NEED[i][j]=MAX[i][j]-ALLOCATION[i][j]; if(NEED[i][j]<0) { cout<<"您输入的第"<<i+1<<"个进程所拥有的第"<<j+1<<"个资源数错误,请重新输入:"<<endl; j--; continue; } } } cout<<"请输入各个资源现有的数目:"<<endl; for(i=0;i<n;i++) { cin>>***AILABLE[i]; } } void Bank() /*银行家算法*/ { int i,cusneed; char again; while(1) { cout<<"请输入要申请资源的进程号(注:第1个进程号为0,依次类推)"<<endl; cin>>cusneed; cout<<"请输入进程所请求的各资源的数量"<<endl; for(i=0;i<n;i++) { cin>>REQUEST[cusneed][i]; } for(i=0;i<n;i++) { if(REQUEST[cusneed][i]>NEED[cusneed][i]) { cout<<"您输入的请求数超过进程的需求量!请重新输入!"<<endl; continue; } if(REQUEST[cusneed][i]>***AILABLE[i]) { cout<<"您输入的请求数超过系统有的资源数!请重新输入!"<<endl; continue; } } for(i=0;i<n;i++) { ***AILABLE[i] -= REQUEST[cusneed][i]; ALLOCATION[cusneed][i] += REQUEST[cusneed][i]; NEED[cusneed][i] -= REQUEST[cusneed][i]; } if(Safe()) { cout<<"同意分配请求!"<<endl; } else { cout<<"您的请求被拒绝!"<<endl; for(i=0;i<n;i++) { ***AILABLE[i] += REQUEST[cusneed][i]; ALLOCATION[cusneed][i] -= REQUEST[cusneed][i]; NEED[cusneed][i] += REQUEST[cusneed][i]; } } for(i=0;i<m;i++) { FINISH[i] = false; } cout<<"您还想再次请求分配吗?是请按y/Y,否请按其它键"<<endl; cin>>again; if(again=='y'||again=='Y') { continue; } break; } } bool Safe() /*安全性算法*/ { int i,j,k,l=0; int Work[MAXRESOURCE]; /*工作数组*/ for(i=0;i<n;i++) Work[i] = ***AILABLE[i]; for(i=0;i<m;i++) { FINISH[i] = false; } for(i=0;i<m;i++) { if(!FINISH[i]) { for(j=0;j<n;j++) { if(NEED[i][j]>Work[j]) { break; } } if(j==n) { FINISH[i] = true; for(k=0;k<n;k++) { Work[k]+=ALLOCATION[i][k]; } p[l++]=i; i=-1;//每次从最开始寻找一个可以分配的 } } }//for(i) if(l==m) { cout<<"系统是安全的"<<endl; cout<<"安全序列:"<<endl; for(i=0;i<l;i++) { cout<<p[i]; if(i!=l-1) { cout<<"-->"; } } cout<<endl; return true; } cout<<"系统是不安全的"<<endl; return false; }
以以上实例来测试:
请输入进程的数目:5
请输入资源的种类:3
请输入每个进程最多所需的各资源数,按照5x3矩阵输入
7 5 3
3 2 2
9 0 2
2 2 2
4 3 3
请输入每个进程已分配的各资源数,也按照5x3矩阵输入
0 1 0
2 0 0
3 0 2
2 1 1
0 0 2
请输入各个资源现有的数目:
3 3 2
系统是安全的
安全序列:
1-->3-->0-->2-->4
请输入要申请资源的进程号(注:第1个进程号为0,依次类推)
1
请输入进程所请求的各资源的数量
1 0 2
系统是安全的
安全序列:
1-->3-->0-->2-->4
同意分配请求!
您还想再次请求分配吗?是请按y/Y,否请按其它键
y
请输入要申请资源的进程号(注:第1个进程号为0,依次类推)
4
请输入进程所请求的各资源的数量
3 3 0
您输入的请求数超过系统有的资源数!请重新输入!
系统是不安全的
您的请求被拒绝!
您还想再次请求分配吗?是请按y/Y,否请按其它键
y
请输入要申请资源的进程号(注:第1个进程号为0,依次类推)
0
请输入进程所请求的各资源的数量
0 2 0
系统是不安全的
您的请求被拒绝!
注:以上代码均是在Win7+VS2012环境中运行。
相关文章推荐
- ORACLE经常使用的命令
- 大整数除法
- MySQL系列之B------MySQL安装
- hdu 4864 Task(贪心)
- Java语言基础1--专题课 拆数
- Binary Tree Postorder Traversal
- Hdu 5113 Black and White ---2014北京现场赛B题
- ASPxPopupControl PopupWindow使用方法
- unity3d热更新插件uLua学习整理
- 第三章:Java语言基础II 第9、10节 综合案例 算命程序
- teamviewer不能传输XP计算机声音
- House Robber (leetcode 198)
- linux下几种目标文件的分析
- NSHashTable的特性和使用
- 轻量级分布式 RPC 框架DIY(转)
- 快速排序
- kvm主机重命名
- 数组中只出现一次的数
- Android--Handler与Message
- 设备信息