进程同步

一 临界区问题
1 do{
2     进入区
3        临界区
4    退出区
5        剩余区
6 }while(TRUE);

 

临界区:每个进程有一个代码段称为临界区,该区中进程可能改变共同变量、更新一个表、写一个文件等。
进入区:请求允许进入临界区。
退出区:
剩余区:剩余代码
 
必须满足:互斥:若进程Pi在其临界区内执行,则其他进程不可在其临界区内执行
                 前进:如果没有进程在其临界区内执行且有进程需进入临界区,那么只有那些不在剩余区内执行的进程可参加选择来确定谁能下一                               个进入临界区,且这种选择不能无限推迟
                有限等待:从一个进程做出进入临界区的请求,直到该请求允许为止,其他进程允许进入其临界区的次数有上限
抢占内核允许处于内核模式的进程被抢占,非抢占内核不允许。
 
二 Peterson算法
 1 do{
 2    flag[i]=TRUE;  //数组flag表示哪个进程想要进入临界区
 3    turn=j;  //turn表示哪个进程可以进入临界区
 4    while(flag[j]&&turn==j); //j==1-i
 5   -------------------------------
 6       临界区
 7   -------------------------------
 8   flag[i]=FALSE;
 9   -------------------------------
10      剩余区
11 }while(TRUE);
Peterson算法适用于两个进程在临界区与剩余区交替执行。
 
 
三 硬件同步----锁
 1 do{
 2     进入锁
 3        临界区
 4    释放锁
 5        剩余区
 6 }while(TRUE);
 7  
 8     
 9 boolean TestAndSet(boolean *target){   
10   boolean *rv = *target;
11   *target = TRUE;
12   return rv;
13 }//TestAndSet指令定义  该指令可以原子地(不可中断地)执行
14  
15 do{
16  while(TestAndSet(&lock))    //lock初始化为false
17      ;//do nothing
18      //critical section
19  lock=FALSE;
20      //remainder section
21 }while(TRUE);  //使用TestAndSet的互斥实现
22  
23 void Swap(boolean *a, boolean *b){
24   boolean temp=*a;
25   *a=*b;
26   *b=temp;
27 }    //Swap指令的定义,原子地执行
28  
29 do{
30   key=TRUE;
31   while(key==TRUE) 
32     Swap(&lock,&key);  //lock初始化为false
33     //critical section 
34   lock=FALSE;
35     //remainder section
36 }while(TRUE);//使用Swap的互斥实现

 

 
   四 信号量
1 wait(S){
2   while(S<=0)
3     ;//no-op
4   S--;
5 }  //加锁
6  
7 signal(S){
8   S++;
9 } //解锁

 

4.1 用法
通常操作系统区分计数信号量与二进制信号量。计数信号量的值域不受限制,而二进制信号量的值只能为0或1。有的系统,将二进制信号量称为互斥锁,因为他们可以提供互斥。
1 do{
2   waiting(mutex);   //使用二进制信号量处理多进程临界问题,n个进程共享一个信号量mutex,并初始化为1
3      //critical section
4   signal(mutex);
5      //remainder section
6 }while(TRUE);

 

4.2 实现
 问题:上述信号量主要缺点是忙等待。当一个进程位于其临界区内时,任何其他试图进入其临界区的进程都必须在其进入代码中连续循环,浪费了CPU时钟,这本来可有效的为其他进程所使用。这种类型信号量也称为自旋锁,因为进程在等待琐时还在运行。
 
解决办法:带一个进程执行wait()操作时,发现信号量不为正,则它必须等待(阻塞自己)。阻塞操作将一个进程放入到与信号量相关的等待队列中,并将该进程的状态切换为等待状态。接着,控制转到CPU调度程序,以选择另外一个进程来执行。
 
 1 typedef struct{
 2   int value;
 3   struct process *list;   //当一个进程必须等待信号量时,就将其加入到进程链表上
 4 }semaphore;
 5  
 6 wait(semaphore *S){
 7   S->value--;
 8   if(S->value < 0){
 9     add this process to S->list;
10     block();//该函数作用为挂起调用它的进程
11   }
12 }
13  
14 signal(semaphore *S){
15     S->value++;
16     if(S->value <= 0){
17        remove a process P from S->list;
18        wakeup(P);      //该函数作用为重新启动阻塞进程P的执行
19     }
20 }
注:在信号量的经典定义下,其值不可能为负。但是本实现中可能产生负的信号量值,且如果其值为负的,则其绝对值为等待该信号量的进程的个数。
      信号量关键之处在于它们原子地执行,必须确保没有两个进程能够同时对同一信号量执行wait()和signal()。
      无限期阻塞/饥饿:进程在信号量内无限等待。
 
 
五 经典同步问题
 5.1 有限缓冲问题

 

 

5.2 读者-写者问题
     读者:只读数据库     写者:读和写数据库
     第一读者--写者问题:当写者写时,所有读者等待;  当一个读者读时,其他读者也可以读,但写者必须等待。(可能导致写者饥饿)
     第二读者--写者问题:当写者等时,不允许有新的读者开始读操作。(可能导致读者饥饿)

部分代码解释:
1 if(readcount==1) wait(wrt);   //如果该读者为第一个读者,那么他将对wrt上锁,防止写者写
2 if(readcount==1)   signal(wrt);        //如果该读者为最后一个读者,那么他将对wrt解锁,表面现在没人在读,写者可以写了
3  
4 wait(mutex).....signal(mutex)        //由于读者间共享readcount,故每次对其操作时,也应对readcount上锁

 

6.管程(太难了,理解了再补这方面笔记)

 

posted @ 2018-06-06 14:11  花花与小叮当  阅读(260)  评论(0)    收藏  举报