实验四 使用信号量进行互斥和同步

实验目的

本实验介绍在Linux中使用信号量进行进程同步、互斥的方法。读者可以通过实验进一步理解进程间同步与互斥、临界区与临界资源的概念与含义,并学会Linux信号量的基本使用方法。

实验环境

硬件环境:计算机一台,局域网环境;

软件环境:Linux Ubuntu操作系统,gcc编译器。

实验内容和步骤

  1. (一)参考:POSIX以及System V
  • System V:Unix众多版本中的一支,最初由AT&T定义,目前为第四个版本,其中定义了较为复杂的API。

  • POSIX:Portable Operating System Interface,IEEE为了统一Unix接口而定义的标准,定义了统一的API接口。Linux即支持System API,又支持POSIX API

  1. (二)Linux提供两种信号量:
  • 内核信号量:用于内核中资源共享控制

  • 用户态信号量:主要包括POSIX信号量和SYSTEM V信号量。其中

  • POSIX信号量分为两类。

  • 无名信号量:主要用于线程间同步,也可用于进程间(由fork产生)同步。

  • 有名信号量:既可用于进程间同步,也可用于线程间同步。

  • POSIX有名信号量主要包括:

  • 在这里插入图片描述

  • 如果有任何进程/线程引用这个信号量,sem_unlink函数不会起到任何作用,即只有最后一个使用该信号量的进程来执行sem_unlink才有效。

  1. (三)实验步骤

    • step1:通过实例查看不使用互斥时的情况

      /*
       * @Description: 观察没有互斥的情况下
       * @version: 
       * @Author: 
       * @Date: 2021-04-30 10:48:37
       * @LastEditors: Please set LastEditors
       * @LastEditTime: 2021-04-30 10:52:47
       */
      #include <stdio.h>
      #include <stdlib.h>
      int main(int argc, char *argv[])
      {
          char message = 'X';
          int i = 0;
          if (argc > 1)
              message = argv[1][0];
          for (i = 0; i < 10; i++)
          {
              printf("%c", message);
              fflush(stdout);
              sleep(rand() % 3);
              printf("%c", message);
              fflush(stdout);
              sleep(rand() % 2);
          }
          sleep(10);
          exit(0);
      }
      
  • 观察X和O的出现规律,并分析原因。

  • 在这里插入图片描述

  • 出现无规律,因为两个进程并发执行,并没有进行互斥同步

  • step2:使用信号量来对临界资源进行互斥

    /*
     * @Description: 利用信号量进行互斥访问
     * @version: 
     * @Author: 
     * @Date: 2021-04-30 12:29:46
     * @LastEditors: Please set LastEditors
     * @LastEditTime: 2021-04-30 12:39:48
     */
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/stat.h>
    #include <semaphore.h>
    #include <fcntl.h>
    int main(int argc, char *argv[])
    {
        char message = 'X';
        int i = 0;
        if (argc > 1)
            message = argv[1][0];
        sem_t *mutex = sem_open("mysem", O_CREAT, 0666, 1);
        for (i = 0; i < 10; ++i)
        {
            // P操作
            sem_wait(mutex);
            printf("%c", message);
            fflush(stdout);
            sleep(rand() % 3);
            printf("%c", message);
            fflush(stdout);
            // V操作
            sem_post(mutex);
            sleep(rand() % 2);
        }
        sleep(10);
        sem_close(mutex);
        sem_unlink("mysem");
        exit(0);
    }
    
    • 编译链接,同时运行两个进程。

    • 观察X和O的出现规律,并分析原因。

    • 在这里插入图片描述

    • 一定是偶数个X后跟着相同偶数个的O,因为在循环开始应用了信号量,进行了互斥,即使在输出第二个字符前sleep但是信号量没有打开,则第二个进程也不能输出,第二个进程阻塞,只有当输出偶数个X后,执行了V操作才能允许第二个进程输出。这样就造成了只有偶数个的情况。

  • step3:使用信号量来模拟下象棋红黑轮流走子的情况

    • 编写两个C语言程序black_chess.c以及red_chess.c,分别模拟下象棋过程中红方走子和黑方走子过程。

    • 走子规则:红先黑后,红、黑双方轮流走子,到第10步,红方胜,黑方输。

    • 编程思路:设置一下两个同步信号量。

      (1)hei:初值为1,代表黑方已经走子,轮到红方走子(满足棋规“红先黑后”)。

      (2)hong: 初值为0,代表红方尚未走子。

    • 红棋走子之前,先测试信号量hei,判断黑方是否已经走子,如是,则轮到红方走子,否则阻塞等待黑子走子,由于hei的初值为1,因此一定是红方先走。红方走子完毕后,置信号量hong,通知黑方走子。

    • 黑方走子之前,先测试信号量hong,判断红方是否已经走子,如是,则轮到黑方走子,否则阻塞等待红方走子,由于hong的初值为0,因此在红方没有走子之前,黑方不会走子。黑方走子完毕后,置信号量hei,通知红方走子。

      /*
       * @Description: 走红棋程序
       * @version: 
       * @Author: 
       * @Date: 2021-04-30 12:49:34
       * @LastEditors: Please set LastEditors
       * @LastEditTime: 2021-04-30 12:56:54
       */
      // 红棋先走,10步,红色赢
      #include <stdio.h>
      #include <stdlib.h>
      #include <sys/types.h>
      #include <sys/ipc.h>
      #include <sys/stat.h>
      #include <fcntl.h>
      #include <semaphore.h>
      int main(int argc, char *argv[])
      {
          int i = 0;
          // 黑棋是否已经走过,是则是1,初始值是1,因为让红棋先走
          sem_t *hei = sem_open("chess_black_sem", O_CREAT, 0666, 1);
          sem_t *hong = sem_open("chess_red_sem", O_CREAT, 0666, 0);
          for (i = 0; i < 10; i++)
          {
              sem_wait(hei);
              if (i != 9)
                  printf("Red chess had moved, black chess go!\n");
              else
                  printf("Red chess win!!!\n");
              fflush(stdout);
              sem_post(hong);
          }
          sleep(10);
          sem_close(hei);
          sem_close(hong);
          sem_unlink("chess_red_sem");
          sem_unlink("chess_black_sem");
          exit(0);
      }
      
      /*
       * @Description: 走黑棋程序
       * @version: 
       * @Author: 
       * @Date: 2021-04-30 12:58:04
       * @LastEditors: Please set LastEditors
       * @LastEditTime: 2021-04-30 13:02:42
       */
      #include <stdio.h>
      #include <stdlib.h>
      #include <sys/types.h>
      #include <sys/stat.h>
      #include <sys/ipc.h>
      #include <fcntl.h>
      #include <semaphore.h>
      int main(int argc, char *argv[])
      {
          int i = 0;
          sem_t *hei = sem_open("chess_black_sem", O_CREAT, 0666, 1);
          sem_t *hong = sem_open("chess_red_sem", O_CREAT, 0666, 0);
          for (i = 0; i < 10; i++)
          {
              sem_wait(hong);
              if (i != 9)
                  printf("Black chess had moved, red chess go!\n");
              else
                  printf("Black chess lost!!!\n");
              fflush(stdout);
              sem_post(hei);
          }
          sleep(10);
          sem_close(hei);
          sem_close(hong);
          sem_unlink("chess_red_sem");
          sem_unlink("chess_black_sem");
          exit(0);
      }
      

      在这里插入图片描述

四、实验总结

  1. 对程序结果进行分析,并结合操作系统课程中讲授的原理,总结信号量在进程同步和互斥中所起的作用。
  • 信号量的应用可以解决多个进程同时访问某一临界资源的互斥问题。当进程检测到信号量为<=0时,说明已经没有临界资源,将阻塞等待其他进程释放资源(信号量加一),这样就实现了互斥关系。

  • 同样也可以解决多个进程的前驱关系中,多个进程的同步问题。如上面的下棋问题,同步关系是,只有当对方已经下了自己才能下,这样一个前驱(合作)关系。用信号量实现就是在自己下棋前检查对放信号量,是否为1,为1表示已经下了,自己可以下。如果这时切换到另一个进程,检查对方的信号量,这时对方信号量仍为0 ,表示还没有下,所以应该阻塞等待。在下棋后,将自己的信号量置1,表示自己已经下了,唤醒对方进程。如此就实现了进程的同步。

  1. 对信号量的操作如果没有成对出现,会导致什么现象发生?

    • 实现进程互斥时,如果缺少P操作则不能保证对临界资源的互斥访问,如果缺少V操作则会使临界资源得不到释放,从而使因等待该资源而阻塞的进程不能被唤醒。可能会造成死锁。

    • 实现进程同步时,如S1->s2,如果缺少P操作不能s2一定在s1,之前进行,如果缺少V操作,则导致S2阻塞后而无法唤醒,无法执行。

  2. 用于实现同步、互斥的信号量再出现的位置上各有什么特点?

    • 对于实现互斥的信号量:是在访问临界资源前进行P操作,访问完毕后执行V操作

    • 对于实现同步的信号量:是在程序开始前进行对方信号量的P操作,结束后执行对自己信号量的V操作。(前P后V)

  3. 如果改成“黑先红后” ,红、黑双方轮流走子,到第20步,黑方胜,红方输,如何编程实现?

    • 在这里插入图片描述

      /*
       * @Description: 黑先红后,20步,黑赢
       * @version: 
       * @Author: 
       * @Date: 2021-04-30 13:25:00
       * @LastEditors: Please set LastEditors
       * @LastEditTime: 2021-04-30 13:35:46
       */
      #include <stdio.h>
      #include <stdlib.h>
      #include <sys/types.h>
      #include <sys/ipc.h>
      #include <sys/stat.h>
      #include <fcntl.h>
      #include <semaphore.h>
      int main(int argc, char *argv[])
      {
          int i = 0;
          sem_t *hei = sem_open("chess_black_sem", O_CREAT, 0666, 0);
          sem_t *hong = sem_open("chess_red_sem", O_CREAT, 0666, 1);
          for (i = 0; i < 20; i++)
          {
              sem_wait(hei);
              if (i != 19)
                  printf("Red chess had moved, black chess go!\n");
              else
                  printf("Red chess lost!!!\n");
              fflush(stdout);
              sem_post(hong);
          }
          sleep(10);
          sem_close(hei);
          sem_close(hong);
          sem_unlink("chess_red_sem");
          sem_unlink("chess_black_sem");
          exit(0);
      }
      
      /*
       * @Description: 
       * @version: 
       * @Author: 
       * @Date: 2021-04-30 13:26:01
       * @LastEditors: Please set LastEditors
       * @LastEditTime: 2021-04-30 13:35:40
       */
      #include <stdio.h>
      #include <stdlib.h>
      #include <sys/types.h>
      #include <sys/stat.h>
      #include <sys/ipc.h>
      #include <fcntl.h>
      #include <semaphore.h>
      int main(int argc, char *argv[])
      {
          int i = 0;
          sem_t *hei = sem_open("chess_black_sem", O_CREAT, 0666, 0);
          sem_t *hong = sem_open("chess_red_sem", O_CREAT, 0666, 1);
          for (i = 0; i < 20; i++)
          {
              sem_wait(hong);
              if (i != 19)
                  printf("Black chess had moved, red chess go!\n");
              else
                  printf("Black chess win!!!\n");
              fflush(stdout);
              sem_post(hei);
          }
          sleep(10);
          sem_close(hei);
          sem_close(hong);
          sem_unlink("chess_red_sem");
          sem_unlink("chess_black_sem");
          exit(0);
      }
      
Logo

更多推荐