同步与互斥
一、进程同步
进程具有异步性的特征,即各并发执行的进程以各自不可预知的速度向前推进。
但是在某些情况下需要解决进程的异步性,实现同步;
同步:亦称直接制约关系,它是指完成某种任务而建立的两个或多个进程,这些进程因为需要在某些位置上协调他们的工作次序而产生的制约关系。进程间的直接制约关系就是源自他们之间的相互合作。
二、进程互斥
进程的并发需要共享的支持,各个并发执行的进程难免需要共享一些资源。
两种资源共享方式:
1.互斥共享:
系统中的某些资源可以提供给多个进程使用,但一个时间内只允许一个进程访问该资源------临界资源
2.同时共享:
系统中,某些资源一个时间段内允许多个进程同时对它们进行访问
临界资源的划分:
逻辑上分为四个部分:
c
do{
entry section;//进入区:检查是否可以进入临界区,若可以则上锁(设置正在访问该临界资源的标志,阻止其他进程访问)
critical section;//临界区:访问临界资源
exit section;//退出区:解除标志,退出临界资源
remainder section;//剩余区:做一些其他操作
}
为了实现对临界资源的互斥访问,保持系统的整体性能,应遵守以下四个规则:
1.空闲让进:在临界区空闲时,应让一个请求进入临界区的进程进入
2.忙则等待:在临界区中有进程时,其他请求进入的进程应等待
3.有限等待:对于等待进入临界区的进程,应保证在有限的时间内进行临界区
4.让权等待:在进程无法进入临界区时,应解放处理机,防止出现饿死
三、进程互斥的软件实现方法
1.单标志法
算法思想:进程在访问完临界区后,把访问临界区的权限交给另一个进程
大致实现逻辑:
c
int turn=0;//turn表示当前允许进入临界区的进程号码
//进程0进入临界区
while(turn!=0);//1
critical section;//2
turn=1;//3
remainder section;//4
//进程1进入临界区
while(trun!=1);//5
critical section;//6
turn=0;//7
remainder section;//8
1.当进程1先上处理机运行时,由于此时turn为0,导致进程1卡在第5步,直到时间片用完发生轮转,进程0上处理机正常运行
2.当进程0先上处理机,运行到第二步时,时间片用完发生轮转,姿此时由于turn仍然是0,进程1依然无法进入临界区,直到时间片用完,进程0上处理机正常运行
由此可见,这保证了同一个时间段内最多只能有一个进程访问临界资源
但是进程只能按照P0->P1->P0的顺序进行轮流的访问,如果此时轮到进程0上处理机进行访问,但进程0不进行访问,此时虽然临界区处于空闲状态,进程一仍然无法进入,这一点又违背了"空闲让进"原则
2.双标志先检测法
算法思想:设置一个布尔类型的数组变量flag\[\],数组中的各项元素用来标记各进程是否想要进入临界区,例如:flag0=true表示P0进程想要访问临界资源,每个进程在进入临界区之前应先检查是否有别的进程想要进行访问,若没有,则将自己的flagx改为true,进行访问
实现逻辑:
c
bool flag[2];
flag[0]=false,flag[1]=false;
//P0进程进行访问
while(flag[1]);//1
flag[0]=true;//2
critical section;//3
flag[0]=false;//4
remainder section;//5
//P1进程进行访问
while(flag[0]);//6
flag[1]=true;//7
critical section;//8
flag[1]=false;//9
remainder section;//10
如果两个进程是并发执行的,会出现这样一种情况,当P0进程上处理机运行,在1->2这个过程中发生了进程切换,进程P1上处理机,此时由于flag0=false,此时P1也可以对临界区进行访问,若此时在调度回P0,此时会出现多个进程同一个时间段对临界区进行访问,违背了忙则等待的原则。
究其原因,是由于检查和上锁两个操作不是一气呵成的,在检查后,上锁前,会发生进程的调度。
3.双标志后检查法
与双标志前检查法相似,但与之不同的是先上锁后检查
实现逻辑
c
bool flag[2];
flag[0]=false,flag[1]=false;
P0进程:
flag[0]=true;
while(flag[1]);
critical section;
flag[0]=false;
remainder section;
P1进程:
flag[1]=true;
while(flag[0]);
critical section;
flag[1]=false;
remainder section;
后检查法虽然解决了"忙着等待"原则,但如果P0与P1两个进程是并发执行的,那么两个进程都会卡在第一步,导致二者都无法进行访问,违背了"空闲让进"原则和"有限等待"原则,可能会出现饥饿现象
4.Petersom算法
算法思想:结合了单标志和双标志的算法思想
实现逻辑:
c
bool flag[2];
flag[0]=false,flag[1]=false;
turn=0;
P0进程:
flag[0]=true;
turn=1;
while(flag[1]&&turn==1);
critical section;
flag[0]=false;
remainder section;
P1进程:
flag[1]=true;
turn=0;
while(flag[0]&&turn==0);
critical section;
flag[1]=false;
remainder section;
该算法增加了一个公共变量turn,这保证了即使两个进程在并发执行的过程中,也不会出现双标志后检查中两个进程互相卡在第一步的问题;
该算法满足"空闲让进"、"忙则等待"、"有限等待"三个原则,但无法满足"让权等待"这一原则
四、进程互斥的硬件实现方法
1.中断屏蔽法
利用"开/关中断指令"的原语性来实现中断的屏蔽,无法实现进程的切换,自然就无法两个进程同时进行访问
优点:简单、高效
缺点:不适应多处理机,由于开/关中断指令都是内核指令,所以其只适用于内核进程,不适合用户进程。
2.TestAndSet指令
利用硬件来实现执行过程不被中断,利用c语言描述其逻辑:
c
bool TestAndSet(bool* lock){
bool old;
old=*lock;
*lock=true;//true为上锁
return old;
}
bool lock=false;
进程P:
while(TestAndSet(&lock));
临界区;
lock=false;
剩余区
优点:实现简单,通过一个函数保证了检查与上锁同时进行,适用于多处理机环境。
缺点:无法实现"让权等待",暂时无法访问的进程会一直运行TestAndSet函数
3.swap指令/Exchange指令/XCHG指令
与TestAndSet指令一样,也是利用硬件来实现中断屏蔽,下面利用c语言来大致描述其逻辑原理:
c
Swap(bool* a,bool* b){
bool temp;
temp=*a;
*a=*b;
*b=temp;
}
bool old=true;
while(old){
swap(old,lock)
}
临界区;
lock=false;
剩余区;
Swap指令的大概逻辑与TestAndSet指令类似,因此二者的优缺点也一致
互斥锁
锁,一种布尔变量,acquire()与release()两个函数可以对其进行操作,实现上锁与开锁。
缺点:会出现忙等待的现象,需要逻辑循环的锁可以称为自旋锁,例如Swap指令、单标志法等等
优点:等待期间不用切换进程上下文,使用于多处理机,若上锁时间短,等待代价更小。