操作系统-进程互斥dekker算法学习
一、概念
-
对于某一时刻仅允许一个进程访问的共享资源就叫临界资源
-
访问临界资源的程序代码段就叫做临界区
-
对进程排它地访问临界资源的这种控制手段就叫做互斥
-
软件实现进程互斥 (四个算法)
1、单标志法
设置公用整型变量turn,用于指示允许进入临界区的进程编号,turn为零,则允许P0进程进入临界区。
特点:能够确保进程互斥访问临界区,但是不能保证满足"空闲让进"原则,并且两进程必须交替进入临界区。
2、双标志先检查法
设置一个数组flag[2],表示继承是否进入临界区,flag[i]的值为FALSE,表示Pi进程未进入临界区,为TRUE则表示Pi进入临界区。
特点:不能确保进程互斥访问临界区,但是不能保证满足"忙则等待"原则,进程不必交替进入,可以连续使用。
3、双标志后检查法
设置一个数组flag[2],这里与前面不同之处就是,先设置自己的标志位,再检测对方的标志状态,若对方的标志位为true则等待呗。
特点:能够确保进程互斥访问临界区,但是存在两个进程都进入不了临界区的"饥饿"现象。违背了"空闲让进"和"有限等待";
4、Peterson 算法
设置一个数组flag[2],这里与前面不同之处就是,先设置自己的标志位,再检测对方的标志状态,若对方的标志位为true则等待呗。
特点:Peterson 算法实际上同时结合了单标志法和双标志后检查法,它的核心就是:在一开始还是和后检查法一样,抢先进行“上锁”,但是上锁之后又将 turn 置为对方线程,表示自己虽然想要进入临界区,但是不介意“将这个机会让给对方”。尽管如此,由于 while 的限制条件增加了,而 turn 又是公用的,所以保证了最后只会有一方的 while 满足条件。既做到了互斥访问资源,也避免了双方都访问不到资源。 -
Dekker算法可以用于控制两个进程(线程)之间的同步,如下实现的功能就是专门用于线程的同步: 其中,flag [2]用来表示是否想要使用关键区,turn用来表示具有访问权限的进程ID。
二、dekker算法代码
//flag为临界资源状态标识数组,0表示空闲,1表示忙碌
//flag[0]代表线程0,flag[1]代表线程1
int flag[2]={0,0};
//number=0代表权力在线程0那里,线程0不用谦让
//number=1代表权力在线程1那里,线程1不用谦让
//number相当于上面dekker概念中的turn
int number=0;
void process0(){
... //其他执行代码
flag[0]=1; //代表线程0想要进入临界区
while (flag[1]!=0){ //当线程1也想进入在临近区中
//所以线程0需要周期性的检查线程1是否想要进入临界区中
flag[0]=0; //线程0暂时将自己设置为不想进入临界区
if (number!=0){ //权力在1那边,1不必谦让线程0
while (1){
// ... //其他代码执行
}
}
}
//一直等待线程1不在临界区,则跳出while
//当线程0想进入临界区,并且线程1不在临界区时
... //线程0访问临界区
//访问完临界区的时候
number=1; //设置权力给1
flag[0]=0; //设置线程0不想进去临界区
}
void process1(){
...
flag[1]=1; //代表线程1想要进入临界区
while (flag[0]!=0){ //当线程0也想进入在临近区中
//所以线程1需要周期性的检查线程0是否想要进入临界区中
flag[1]=0; //线程1暂时将自己设置为不想进入临界区
if (number!=1){ //权力在0那边,0不必谦让线程1
while (1){
// ... //代码执行
}
}
}
//一直等待线程0不在临界区,则跳出while
//当线程1想进入临界区,并且线程0不在临界区时
... //线程1访问临界区
//访问完临界区的时候
number=0; //设置权力给0
flag[1]=0; //设置线程1不想进去临界区
}
参考学习文章:操作系统原理——Dekker互斥算法详解
参考学习文章:操作系统进程互斥的软件实现算法(单标志法、双标志检查法、双标志后检查法以及皮尔森算法)