4.1进程同步互斥概念
思考:
为什么引入进程同步的概念?
不同进程之间存在着制约关系,为了协调这样的制约关系,引入该概念。
1.同步:直接制约关系
2.互斥:间接制约关系
3.临界资源:在一段时间内只允许一个进程访问的资源。临界资源要求互斥地共享,或互斥地访问。
访问临界区的过程:
do {
entry section //进入区
critical section//临界区
exit section//退出区
reminder section//剩余区
} while (1);
4,为禁止两个进程同时进入临界区,同步机制应遵循以下准则:
空闲让进:
忙则等待:
有限等待:当一个进程申请进入临界区,应限制其它进程进入临界区的次数,以便申请的进程有机会进入临界区。
让权等待:等待的时候释放CPU的执行权。
4.2实现临界区互斥的基本方法
1.软件实现方法:Peterson’s Algorithm,利用turn解决了饥饿。
//Pi
do{
flag[i] = TRUE;//进入区 表示进程i 想进入临界区
turn = j;//进入区 表示现在轮到进程j 进入临界区了
while(flag[j] && turn == j);//进入区 如果进程j 在临界区中那么i 不能进去
//临界区
flag[i] = FALSE;//退出区
//剩余区
} while(TRUE);
//Pj
do{
flag[j] = TRUE;//进入区 表示进程j 想进入临界区
turn = i;//进入区 表示现在轮到进程i 进入临界区了
while(flag[i] && turn == i);//进入区 如果进程j 在临界区中那么i 不能进去
//临界区
flag[j] = FALSE;//退出区
//剩余区
} while(TRUE);
2.硬件实现方法:
1)中断屏蔽方法:修改共享变量时禁止一切中断产生。适用于非抢占内核。不适用于多处理器环境,因为将消息传递给所有处理器时,消息传递导致进入每个临界区都会延迟。
2)特殊硬件指令方法:TestAndSet是原子操作,可读出指定标志后把标志设置为真。
boolean TestAndSet(boolean *target){
boolean rv=*target;//保存锁原来的状态
*target=true;//加锁
return rv;//返回原来的锁的状态
}
//实现互斥的算法如下:
do{
while(TestAndSet(&lock));//只要锁原状态是true,那么表示该临界资源有其他进程在访问,所以这里就要一直监测,直到临界资源不再被其他进程占用
//临界区
lock=false;//访问完毕,下锁
//剩余区
}while(1)
思考:
Swap指令:你能写出指令和算法的伪代码吗?(p80)
优点:适用于任意数目的进程,单处理机和多处理机都可;支持进程内有多个临界区,只需为每个临界区设立一个bool量。
缺点:不能实现让权等待,从等待进程中随机选择一个进入临界区,有的进程可能一直选不上,导致饥饿。
4.3信号量
1.整型信号量
2.记录型信号量
3.实现同步
4.实现互斥
5.实现前驱关系
大量例题和练习题参考老师的PPT
4.4管程
4.5死锁
1.定义:多个进程因竞争资源而造成的一个僵局(互相等待),若无外力作用,这些进程都将无法向前推进。
1)死锁是指一组处于等待(阻塞)状态的进程,每一个进程持有其它进程所需要的资源,而又等待使用其它进程所拥有的资源,致使这组进程互相等待,均无法向前推进。
2)死锁是指一组处于等待(阻塞)状态的进程,互相等待只有这组进程才能产生的事件,致使这组进程都无法往前推进,这些永远在互相等待的进程称为死锁进程。
饥饿:长时间处于阻塞或就绪,但不是互相等待。死锁是互相等待,处于阻塞状态。
2.资源分配图表达形式:进程Pi 到资源类型Rj 的有向边Pi–>Rj,表示进程Pi 已经申请了资源类型Rj 的一个示例,并且正在等待该资源。即Pi -->Rj称为申请边,有向边Rj -->Pi称为分配边。
*如果资源分配图无环,那么不存在死锁。如果有环且每个资源类型只有一个示例会出现死锁。(充分必要条件)
*
3.产生原因:
1)系统资源的竞争:对不可抢占资源的竞争。
2)进程推进顺序非法:请求和释放资源不当、信号量使用不当。
3)死锁产生的必要条件,只要以下条件任意一个不成立就不会产生死锁。(必要条件就是即使这个条件存在也不一定是死锁,但是死不成立一定没有死锁)
互斥条件:
不抢占条件:
占有并等待条件:进程已经占有了至少一个资源,此时又去申请另一个资源,如果这个资源被其他进程占用,那么请求进程被阻塞,它占用的资源也不会释放。
循环等待条件:处于等待状态的进程集合{p1,p2,…,pn},且Pi的资源被Pi+1占用,Pn的资源又被P0占用。
4.死锁的处理策略:破坏四个必要条件之一或者能检测死锁并实现恢复。
死锁预防、避免死锁、死锁的检测与解除
4.1 死锁预防:
破坏互斥条件(不可行)
破坏非抢占条件:即允许进程已经占用的资源释放并给别的进程使用,这可能会导致释放资源的进程前一阶段工作失效并且反复申请和释放资源增加了系统开销。该方法一般用于状态易于保存和恢复的资源比如CPU寄存器和内存资源,一般不用于打印机之类的资源。
破坏占有并等待条件:进程在运行前申请完所有需要的资源,并且这些资源一直属于它,不再提出资源分配。缺点:系统资源被严重浪费,还可能个别资源被长期占用,导致需要它的进程饥饿。
破坏循环等待条件:使用顺序资源分配法,规定每个进程按照编号递增的顺序请求资源,同类资源一次申请完。缺点:编号稳定会限制新设备的增加;还是会发生作业使用资源的顺序与系统规定顺序不同的情况造成资源浪费;给用户编程带来麻烦。
4.2 死锁避免:
允许动态申请资源,在分配资源之前应先计算资源分配的安全性,若此次分配不会导致系统进入不安全状态,则将资源分配给进程。
思考:
什么是安全状态?
什么是安全序列?你知道如何找到一组安全序列吗?
不安全状态一定是死锁状态吗?
1.资源分配图算法
需求变:虚线
假分配–>判断是否有环–>有环即不分配;无环即真分配
2.银行家算法
操作系统–>银行家,资源–>资金,进程请求资源–>用户向银行贷款。
算法思想:当进程首次申请资源,要测试该进程的资源最大需求量,如果系统资源可以满足则分配否则延迟分配。当进程在执行过程中继续申请资源,要测试申请的资源和已占有的资源综合是否超过其最大需求量,若超过则拒绝,若未超过则再来判断系统资源是否满足其申请量,满足即分配,否则继续推迟。系统最后要执行安全性算法,如果系统安全则分配,否则
算法需要的数据结构:
Available:可利用资源向量,含m个元素的数组,Available[i]=k表示Rj 类资源有K个。
Max:最大需求矩阵,n行m列,Max[i,j]=K表示Pi 需要Rj 类资源的最大数目。
Allocation:分配矩阵,n行m列,Allocation[i,j]=K表示Pi 已经分得 Rj 类资源的数目为K。
Need:需求矩阵,n行m列,Need[i,j]=K表示Pi 还需 Rj 类资源的数目为K。
关系:Need=Max-Allocation
思考:
你能结合数据结构并结合算法思想描述出如下算法部分或者伪代码吗?
资源请求算法部分:
安全性算法部分:
4.3 死锁的检测和解除
根据矩阵Max Available Allocation Need矩阵等,如何判断是否有死锁呢?
根据资源分配图怎么检测出是否有死锁呢?这可能会考到。
题解:判断资源分配图是否可完全简化,不可的话就死锁。具体参考https://blog.csdn.net/baidu_41774120/article/details/101998375
step1:先看R1资源,它有三个箭头是向外的,因此它一共给进程分配了3个资源,此 时,R1没有空闲的资源剩余。
step2:再看R2资源,它有一个箭头是向外的,因此它一共给进程分配了1个资源,此时,R2还剩余一个空闲的资源没分配。
step3:看完资源,再来看进程,先看进程P2,它只申请一个R1资源,但此时R1资源已经用光了,所以,进程P2进入阻塞状态,因此,进程P2暂时不能化成孤立的点。
step4:再看进程P1,它只申请一个R2资源,此时,系统还剩余一个R2资源没分配,因此,可以满足P1的申请。这样,进程P1便得到了它的全部所需资源,所以它不会进入阻塞状态,可以一直运行,等它运行完后,我们再把它的所有的资源释放。相当于:可以把P1的所有的边去掉,变成一个孤立的点,如下图所示:
step5:进程P1运行完后,释放其所占有的资源(2个R1资源和1个R2资源),系统回收这些资源后,空闲的资源便变成2个R1资源和1个R2资源,由于进程P2一直在申请一个R1资源,所以此时,系统能满足它的申请。这样,进程P2便得到了它的全部所需资源,所以它不会进入阻塞状态,可以一直运行,等它运行完后,我们再把它的所有的资源释放。相当于:可以把P2的所有的边都去掉,化成一个孤立的点,变成下图:
判断出死锁后,如何解除呢?
资源抢占:挂起某些死锁进程并将它的资源分配给其他死锁进程,但要保证原进程不会长时间一直被挂起。
撤销进程:撤销部分或者全部死锁进程并抢占资源。
进程回退:回退到足以避免死锁的地步,这里不是剥夺它的资源,而是回退后它自己愿意释放。