01-进程同步

1、进程同步的背景

  有一个环形缓冲池,包含n个缓冲区(0~n-1)(如下图所示:)。有两类进程:一组生产者进程和一组消费者进程,生产者进程向空的缓冲区中放产品,消费者进程从满的缓冲区中取走产品。

                                             

 2、生产者进程:

while (true) {     
     /*  生产一个产品*/
     while (count == BUFFER_SIZE)
         ;        // do nothing
     buffer [in] = nextProduced;
     in = (in + 1) % BUFFER_SIZE;
     count++;
}   

3、消费者进程:

while (true)  {
     while (count == 0)
       ; // do nothing
     nextConsumed =  buffer[out];
     out = (out + 1) % BUFFER_SIZE;
     count--;
//count++ 的执行过程(机器指令):    
register1 = count
register1 = register1 + 1     
count = register1
//count-- 的执行过程(机器指令):   
register2 = count     
register2 = register2 - 1     
count = register2

设初始状态为 “count = 5” :(按照时间片轮询(按照颜色区分),最终运行结果)

S0: producer execute register1 = count {register1 = 5}

S1: producer execute register1 = register1+1 {register1 = 6} 

S2: consumer execute register2 = count {register2 = 5} 

S3: consumer execute register2=register2-1 {register2 = 4} 

S4: producer execute count = register1 {count = 6 } 

S5: consumer execute count = register2 {count = 4}

正确结果应该是: “count = 5” , 现在的结果是: “count = 4

在多道程序环境下,这里两道程序,进程并发执行,不同进程存在相互制约的关系。为了协调该关系,避免进程冲突,引入了进程同步

4、进程同步的概念:

临界资源:一次仅允许一个进程使用的资源称为临界资源。比如:打印机、共享变量等。

临界区:是指并发进程中访问临界资源的程序段。

进入区:检查是否可以进入临界区,若可以,设置正在访问临界区标志。

退出区:清除正在访问临界区标志。

进程的互斥指若干个进程要使用同一共享资源时,任何时刻最多允许一个进程去使用,其它要使用该资源的进程必须等待,直到占有资源的进程释放该资源。

进程的同步解决进程间协作关系的手段。指一个进程的执行依赖于另一个进程的消息,当一个进程没有得到来自于另一个进程的消息时则等待,直到消息到达才被唤醒。

进程互斥关系是一种特殊的进程同步关系。

5、临界区访问原则:

互斥访问– 若已有进程进入临界区,则其他的进程必须等待其离开临界区,释放临界资源。 (忙则等待)

前进– 若没有进程处于其临界区,应允许一个请求进入临界区的进程立即进入临界区,访问临界资源。(空闲让进)

有限等待– 对请求访问的进程,应保证能在有限的时间内进入临界区,避免进入“死等”。(避免死锁问题)

让权等待– 当进程不能进入临界区时,该进程应释放处理机,以免进入“忙等”状态。

6、进程同步的实现方法-----软件实现

  在进入区设置和检查一些标志来标明是否有进程在临界区,若有则在进入区循环检查进行等待,进程在退出区修改标志,以允许别的进程进入临界区。

Peterson 算法

  1981年,由Peterson提出,满足临界区访问的4原则设有两个进程Pi和Pk,且 LOADSTORE 指令是原子操作。 PiPk共享两个变量:

int turn;

Boolean flag[2] ;

变量 turn:

turn==i 表示Pi可进入其临界区。

数组 flag :

flag[i] = true 表示进程Pi请求进入临界区!

Pi进程:

while (true) {
      flag[i] = TRUE; //Pi想进入
      turn = k;   //Pk可以进入
      while ( flag[k] && turn == k)
              ;
      CRITICAL SECTION

       flag[i] = FALSE;
       REMAINDER SECTION
}

Pk进程:

while (true) {
      flag[ k] = TRUE;
      turn = i;
      while ( flag[i] && turn ==i)
              ;
      CRITICAL SECTION

       flag[k] = FALSE;
       REMAINDER SECTION
}

  若pipk同时请求进入临界区,while中的turn变量可保证只允许一个进入临界区,从而实现了互斥。考虑Pi进程的代码,flag[i] = true;意味着Pi想进入临界区,同时将turn设置为k, 若Pk在临界区,则Piwhile条件为真,Pi等待。若PK不在临界区,则flag[k]falsePi进入临界区,从而避免了死等。

7、进程同步的实现方法-----硬件方法

  很多系统都提供了解决临界区问题的硬件支持。 对于单处理器环境 – “禁止中断” 并发进程可以无预设地运行 限制了交替执行程序的能力,执行效率明显降低。 许多现代计算机系统提供了特殊的原子(执行该代码时不允许被中断)机器指令:

TestAndSet指令:读出标志并把该标志设置为TRUE

Swap指令:交换两个内存字的内容。

 

8、信号量

  Dijkstra发明了两个信号量操作原语:P操作原语和V操作原语。常用的其他符号有:waitsignalupdown等。 (原语是操作系统内核中执行时不可中断的过程,即原子操作) 除赋初值外,信号量仅能由同步原语对其进行操作,没有任何其他方法可以检查和操作信号量。 利用信号量和P、V操作既可以解决并发进程的竞争问题,又可以解决并发进程的协作问题。

整型信号量:

 

 记录型信号量:

 

 

 

 实现同步:

 

 实现互斥:

 

前驱图:

 

semaphore  a=0,b=0,c=0,d=0,e=0,f=0,g=0;
      //其他代码
         { S1; signal(a); signal(b);               }
         { wait(a); S2; signal(c); signal(d); }
         { wait(b); S3; signal(e);                  }
         { wait(c); S4; signal(f);                   }
         { wait(d); S5; signal(g);                  }
         { wait(e); wait(f); wait(g); S6;         }
        //其他代码

经典同步问题有:

生产者----消费者问题

读者--写者问题

哲学家进餐问题

posted @ 2020-05-12 21:17  一知.半解  阅读(...)  评论(...编辑  收藏