【操作系统】进程互斥的实现
三个管理原则:
- 互斥性
- 进展性
- 有限等待性
两个进程互斥相关的算法
尝试1:给进程一个turn值.按号进入
代码:(需要注意,这个代码是类c的代码,并不是严格按照c的语法来写的…)
int turn;
P0:
do{
while(turn==1);//turn只要为1就一直循环,叫做"Busy Waiting"状态,不进入等待状态,只是忙式等待
/*此处写临界区代码*/
turn=1;//turn一开始为0.所以P0进入临界区,执行完将turn置1,P1就可以退出循环进入临界区了
/*此处写其余代码*/
}while(1);
P1:
do{
while(turn==0);
/*此处写临界区代码*/
turn=0;
/*此处写其余代码*/
}while(1);
缺点:不满足进展性;P0和P1必须交替进入临界区
就是说.如果有一个进程进展比较慢,还没到临界区的这部分代码.那么另一个进程就一直一直忙式等待.就不能保证只要临界区空.就放人进去
尝试2:给个flag
Boolean flag[2];//代表进程[i]要不要进入等待区
P0:
Do{
while(flag[1]);//先看看对方要不要进入等待区,若对方要进,则等待(Busy Waiting),反之则自己进入,并将flag设为true
flag[0]=true;
/*此处放临界区代码*/
flag[0]=false;
/*此处放其余代码*/
}while(1);
P1:
Do{
while(flag[0]);
flag[1]=true;
/*此处放临界区代码*/
flag[1]=false;
/*此处放其余代码*/
}while(1);
但是,这样不满足互斥性原则:如果两人同时判断对方不处于需要状态,同时进入临界区的话,就不满足互斥性原则
尝试3:
将"自己的flag置1"操作提前到"判断另一个人的flag"之前
boolean flag[2];
P0:
do{
flag[0]=true;//将自己的flag先置一
while(flag[1]);//在判断另一个人
/*此处临界区代码*/
flag[0]=false;
/*此处其余代码*/
}while(1);
P1:
do{
flag[1]=true;//将自己的flag先置一
while(flag[0]);//在判断另一个人
/*此处临界区代码*/
flag[1]=false;
/*此处其余代码*/
}while(1);
但是这种方式容易造成谦让,即:两方都判定对方的flag是1,两方都不进入临界区造成临界区空着不放人进,也不满足进展性
Dekker算法
综合了前三种,既有turn,也有flag
int flag[2];//init 0
int turn;//who's turn
P0:
do{
flag[0]=1;//自己要进入临界区
while(flag[1])//判断对方要不要进入临界区
+//(注意,只有俩人都要进入临界区时才判断turn值!!)
if(turn==1){
flag[0]=0;//放弃追求
while(turn==1){skip;}//忙式等待直到对方离开让出使用权
flag[0]=1;//重拾追求,这个时候再去while的判断也进不来while了.因为P1已经把flag[1]置零了
}
/*此处写临界区代码*/
turn=1;//释放临界区使用权,it's your turn,"1"!
flag[0]=0;//释放临界区使用权
/*此处写其余代码*/
}while(1);
P1:
do{//嗯哼,同理
flag[1]=1;
while(flag[0])
if(turn==1){
flag[1]=0;
while(turn==0){skip;}
flag[1]=1;
}
turn=0;
flag[1]=0;
}while(1);
更简版:(思想一样,代码量少)
Peterson算法
谁先修改turn值,谁就先进入临界区(虽然是反直觉的,但是想想确实,虽然我先把turn修改成了别人,但是后修改的那个人又把turn改成了我…)
boolean flag[2];
int turn;
P0:
do{
flag[0]=1;turn=1;
while(flag[1]&&turn==1);
/*此处临界区代码*/
flag[0]=false;//让出临界区
/*此处放其余代码*/
}while(1);
P1:
do{
flag[1]=1;turn=0;
while(flag[0]&&turn==0);
/*此处临界区代码*/
flag[1]=false;
/*此处放其余代码*/
}while(1);
以上都是针对2个进程的,当进程数>=3时呢?
Lamport面包店算法
就是抓号,按号进入;但是要规定有进程正在抓号时,不能比号.(要不然会出现同时进入的情况?)
boolean chossing[0,...,n-1];//init false
int number[0,...,n-1];//init 0
Pi:
choosing[i]=true;
number[i]=max{number[0],number[1],...,number[n-1]}+1;
chossing[i]=false;//抓号结束
for(int j=0;i<n;j++){
while(choosing[j]);//等待直至没有进程正在抽号
while(number[j]!=0&&(number[j],j)<(number[i],i);//定义(a,b)<(c,d) iff (a<c)or(a==c&&b<d)
//如果进程抽到的号一样,那么就比较进程原本的编号
}
Eisenberg/Mcguirre算法(还是不太明白)
有flag(一个枚举型数组)
enum flag[0,....,n-1](idle,want_in,in_cs);//idle是不想进入,want_in是想进,in_cs是已经进入或者想进入临界区
int turn;
Pi:
do{
flag[i]=want_in;
j=turn;
whie(j!=i){
if(flag[j]!=idle) j=turn;//有想进或已经进的,就从turn开始找
//这里,是不断地把j设为新的turn,判断即时的turn是否是i(我)
//为何是碰到想进或已经进的才判断这个呢?
else j=(j+1)%n;//往后找
}//什么时候退出循环往下走呢,turn值等于我的时候
flag[i]=in_cs;
j=0;
while(j<n&&(j==i||flag[j]!=in_cs)) j++;
}while(j!=n);
/*在临界区的代码*/
Pi离开:
j=(turn+1)%n;//"下一个~"
while(flag[j]==idle) j=(j+1)%n;//叫号直到有想进入的进程
turn = j;
flag[i]=idle;//i让出

浙公网安备 33010602011771号