十一、进程互斥
1、什么是进程互斥?什么是进程同步?
进程具有异步性和独立性等并发特点。由于资源的有限性以及进程的异步性(进程的不可预知性),并发的进程间对于有限的计算机资源存在竞争与共享,这种竞争关系就是进程互斥。
在异步环境下,一组进程因直接制约而互相发送消息而互相合作、互相等待,并使各进程按一定的速度共同进行下去的行为就是进程的同步。
2、临界区问题
临界区和临界资源的定义
我们将不允许多个并发进程交叉执行的程序段称为临界部分(或临界区),将使用中不可被共享的资源称之为临界资源。
临界区问题:
所谓的临界区问题就是由属于不同并发进程的程序段共享共用数据或公用数据变量引起的。临界区不可能通过增加硬件的方式解决。
临界区算法描述:
确定临界资源;
确定临界区;
判断两个相邻的进程是否有相同的临界区,若有则必须互斥进入;若无则不互斥。
3、对临界区的互斥访问必须遵循:
①、空闲让进:空闲时才允许进程切入
②、忙则等待:当前临界区忙碌时,其他进程想要进入时必须等待
③、有限等待:等待的时间必须是有限的
④、让权等待:当进程不能访问临界区时必须及时释放。
4、临界区的组成:
①、进入区:判断是否允许进入临界区(不允许进入时将会陷入等待,若允许进入则会进行上锁处理)
②、临界区:临界区资源
③、退出区:退出临界区时执行的指令(包括为临界区开锁)
④、剩余区:退出临界区后的剩余操作内容
5 、一组进程互斥执行时必须满足的条件有(四条)
(1)不能假设各并发进程的相对速度。即不能假设某一进程将在什么时候需要进入临界区,并在那个时候将临界区预留给它。
(2)互斥进程间不能阻止其他进程进入临界区(只能有系统统一决定)
(3)某一时段内,即使有多个临界区申请,临界区中有且仅有一个进程。
(4)进程在申请进入临界区后,不能无限制等待进入临界区。
5、互斥的软件(算法)实现
①、单标志法(一般适用于双进程临界区):在每个进程访问完临界区后,会将访问权限交给另一进程
int turn =0; //设标志量,当进程PID=标志量时,进程允许进入临界区
P0进程:
while(turn!=0); //当turn不等于0时,P0进程等待进入临界区
临界区;
turn=1; //将进入权限转交给P1进程
remainder section; //剩余区
P1进程:
while(turn!=1); //当turn不等于0时,P0进程等待进入临界区
临界区;
turn=0; //将进入权限转交给P0进程
remainder section; //剩余区
算法思想:设置一个“门卡”,持有门卡的进程可以访问临界区
算法规则:由上一进入临界区的进程决定下一进入临界区的进程
算法适用范围:适用于进程顺序较为清晰的系统
优点:上一进入临界区的进程决定下一进入临界区的进程
缺点:进入临界区的进程的顺序是固定的,若出现一直不进入临界区的情况,违背了空闲让进的原则
②、双标志先检查法: 该算法的基本思想是在每个进程访问临界区资源之前,先查看另一进程是否想要访问临界区,若是,该进程需等待;否则,进程才进入自己的临界区。
bool flag[2]={false,false}; //表示进入临界区意愿的进程
P0进程:
while(flag[1]); //查看另一进程是否想要进入临界区
flag[0]=ture; //标记进程0想要进入临界区
临界区访问
flag[0]=false;
remainder section;
P1进程:
while(flag[1]); //查看另一进程是否想要进入临界区
flag[0]=ture; //标记进程P1想要进入临界区
临界区访问
flag[0]=false;
remainder section;
算法思想:互相“询问”对方是否进入临界区
算法规则:每个进程访问临界区资源之前,先查看另一进程是否想要访问临界区,若是,该进程需等待;否则,进程才进入自己的临界区。
缺点:违背了“忙则等待”的原则,可能会出现两个进程同时进入临界区的情况(检查与上锁不是同一原语,中间可能会发生进程切换)
③、双标志后检查法
bool flag[2]={false,false}; //表示进入临界区意愿的进程
P0进程:
flag[0]=ture; //标记进程0想要进入临界区
while(flag[1]); //查看另一进程是否想要进入临界区
临界区访问
flag[0]=false;
remainder section;
P1进程:
flag[0]=ture; //标记进程P1想要进入临界区
while(flag[1]); //查看另一进程是否想要进入临界区
临界区访问
flag[0]=false;
remainder section;
④、Peterson算法:
算法思想:在双标志后检查法的基础上,如果出现两者都需要进入临界区的情况,则采取“孔融让梨”得方式将机会让给对方。
算法规则:当进程想要访问临界区时,他会先申请访问,然后做出“谦让”;然后进行判断
bool flag[2]={false,false}; //表示进入临界区意愿的进程
int turn =0; //谦让变量
P0进程:
flag[0]=ture; //标记进程P0想要进入临界区
turn=1; //谦让
while(flag[1]&&turn==0); //查看另一进程是否想要进入临界区且上一次谦让是否为自己
临界区访问
flag[0]=false;
remainder section;
P1进程:
flag[0]=ture; //标记进程P1想要进入临界区
turn=0; //谦让
while(flag[1]&&turn==1); //查看另一进程是否想要进入临界区且上一次谦让是否是自己
临界区访问
flag[0]=false;
remainder section;
6、互斥的硬件实现
①、中断屏蔽方法:在进入临界区时,直接进行中断处理
优点:简单高效
缺点:不适用于多处理机的情况,因为关中断指令只对当前处理机有效,其他处理机依然可以切换进程;只适用于内核进程(中断指令的权限较高)
②、TsetAndSet(TS/TSL)指令
//TestAndSret指令是一个硬件指令,以下为其软件逻辑内容,并非实际算法
bool lock; 设置一个新的bool变量判断临界区是否加锁
bool TsetAndSet(bool *lock){
bool old;
old = *lock;
lock = ture; //将lock赋ture值
return old; //将原值传回
}
while (TestAndSet(&lock));检查之前是否上锁
临界区访问
lock=false; //解锁
剩余代码
TSL指令是作为硬件指令存在的,所以不会出现上锁和检查分开执行的情况;
③、Swap指令(Exchange/XCHG指令)
Swap指令和TSL指令的不同是;Swap指令是将old和lock的值交换
7、信号量与PV原语
信号量是当前临界区的状态标志。信号量能且仅能被PV原语改变。
进程同步中,制约进程执行速度的信号量只与制约进程与被制约进程有关,因此被称为私用信号量。(与公用信号量向对)
注:wait原语和signal原语又称PV原语
信号量操作:初始化、PV操作
①、P操作过程:(设信号量为sem)
(1)sem-1
(2)若sem减一后大于或等于0则P原语返回;
(3)若小于0 ,则进程被阻塞,并进入等待队列。
②、V操作过程:
(1)sem+1
(2)若加一后大于0则V原语返回;
(3)若小于等于0,则等待队列中尚有被阻塞的进程,前往唤醒。
8、信号量实现进程互斥
semaphore mutex=1; //设置临界区信号量
P0(){
。。。
P(mutex);
临界区代码
V(mutex);
。。。
}
P1(){
。。。
}
9、信号量实现进程同步
semaphore mutex=0;
P1(){
代码1;
P(mutex);
代码2;
}
P1(){
代码3;
V(mutex);
代码4;
}
10、信号量解决前驱关系
信号量也可以用来描述程序之间或者语句之间的前驱关系。
注:实现前驱操作的过程类似于实现同步操作的拓展。
11、管道
管道有无名管道和有名管道两种,无名管道为建立一条进程与其子孙的通过比特流传递信息的单向通信管道,管道在逻辑上可以看成管道文件,在物理上由文件系统的高速缓冲区构成很少涉及外设。
发送进程利用文件系统的系统调用:write(fd[1]),buf,size),把buf中的s长度为size的字符串取出放入管道入口fd[1],接收进程使用系统调用:read(fd[0],buf,size)从管道fd[0]中读出size个字符的消息置于buf中。
创建管道:pipe(fd) 创建完后fd[1]为输入 ,fd[0]为输出
子进程输入:write(fd[1],buf,size)
父进程输出:read(fd[0],buf,size)
十、死锁问题
1、死锁问题:各并发进程相互等待对方所拥有的资源,且这些并发进程在得到别人的资源前不会释放自己的资源,导致大家都因资源不足而无法推进。
2、死锁的原因:根本原因是资源的有限性。
3、产生死锁的必要条件:
①互斥条件②不可剥夺条件③部分分配条件(进程每次申请资源时只能得到所需的一部分资源。)④环路条件(存在一种进程循环链,链中每一个进程获得的资源同时被下一个进程所请求。)
4、死锁的消除方法