• 进程同步


    一 临界区问题
    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.管程(太难了,理解了再补这方面笔记)
  • 相关阅读:
    USACO 2.1 Hamming Codes
    USACO 2.1 Healthy Holsteins
    USACO 2.1 Sorting a Three-Valued Sequence
    USACO 2.1 Ordered Fractions
    USACO 2.1 The Castle
    USACO 1.5 Superprime Rib
    1145: 零起点学算法52——数组中删数II
    1144: 零起点学算法51——数组中删数
    1143: 零起点学算法50——数组中查找数
    1142: 零起点学算法49——找出数组中最大元素的位置(下标值)
  • 原文地址:https://www.cnblogs.com/dzy521/p/9144842.html
Copyright © 2020-2023  润新知