• center进程间通信center


    进程间通信

    1.1进程间通信的概念

    进程间通信就是进程之间进行信息的交换和传播

    很重要的一句话:进程间通信的本质是让不同的进程看到同一根资源
    在这里插入图片描述

    1.2进程间通信的目的

    • 数据传输:一个进程需要将自己的数据发生给其他的进程
    • 资源共享:多个进程之间共用相同的资源
    • 通知事件:一个进程需要向拎一个进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)
    • 进程控制:有些进程希望完全控制另一个进程的执行(如debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变

    1.3进程间通信的方式

    • 管道(本文主讲)
      • 匿名管道pipe
      • 命名管道
    • System V进程间通信(本文不涉及)
      • System V消息队列
      • System V共享内存
      • System V信号量
    • POSIX进程间通信(本文不涉及)
      • 消息队列
      • 共享内存
      • 信号量
      • 互斥量
      • 条件变量
      • 读写锁

    2.1管道的概念

    管道是一种古老的进程间通信的形式

    • 通常把一个进程连接到另一个进程的一个数据流称为一个“管道”

    2.2匿名管道的介绍

    匿名管道就是没有名字的管道,由函数pipe(int fd[2]) 创建,常用于具有亲缘关系的进程间进行通信,作为两个进程共享的资源,从而实现两个进程间的信息交换达到通信的目的。

    2.2.1管道的5种特征4种情况

    5种特征:

    • 1.管道内部已经自动提供了同步与互斥机制(读和写只能一个进行另一个等待,不能同时进行)

    • 2.如果打开文件的进程退出了,文件也会被释放掉(所有打开了某一文件的进程全部退出了,该文件的资源才会彻底释放掉)

    • 3.管道是提供流式服务的(本文只是提及,知道有这么个东西即可,后续的博客应该会讲)

    • 4.管道是半双工通信的(管道只能是单向通信的,例如父进程在进行写入数据到匿名管道,那么子进程就不能进行读取管道信息,必须等父进程写完了才可以进行读操作,也就是父子进程只能是其中一个对管道进行读或写操作,不能父子进程同时对管道进行读或者写操作!!!)

    • 5.匿名管道适合具有血缘关系的进程进行进程间通信,常用于父子

    4种情况:

    • 1.(子或父)不write,(父或子)一直read,read阻塞
    • 2.(子或父) 不read ,(父或子)一直write,write阻塞
    • 3.write写完后关闭,read返回值为0
    • read关闭,一直写,写方会被操作系统杀掉,写入无意义

    2.3管道读写的规则说明

    (fd_arrray[3] fd_array[4] 不理解的第三张图有解释)

    在这里插入图片描述

    通过pipe(int fd[2])创建了管道后,再通过fork()创建子进程

    错误读写:
    在这里插入图片描述

    正确读写:

    在这里插入图片描述

    父进程读,子进程写的相关代码(本文主要代码,举例也是这在这段代码的基础上变换来的):

    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<sys/wait.h>
    #include<sys/types.h>
    #include<string.h>
    
    int main()
    {
      int fd[2];
      if(pipe(fd)<0)
      {
        //创建管道
        perror("pipe!");//这个函数自带换行
        return 1;
      }
      //printf("fd[0]\n",fd[0]);
      //printf("fd[1]\n",fd1]);
      
      //创建子进程
       
      pid_t id = fork();
      if(id==0)
      {
        //child
        //子进程要做的是向管道里写数据 要把读端关闭 最后写完了再把写端关闭
        close(fd[0]);
        int count=10;
       const char* str="hello father i am your child!\n";
    
        while(count)
        {
          write(fd[1],str,strlen(str));
        //  printf("%s",str);
          count--;
          sleep(1);
        }
        close(fd[1]);
        exit(1);
        
      }
      else if(id>0)
      {
        //father
        close(fd[1]);//父进程关闭写端,只进行读
        char buff[64];
        while(1)
        {
          int ret=read(fd[0],buff,sizeof(buff));
          if(ret>0)
          {
            buff[ret]='\0';
            printf("child send to father's message is:%s\n",buff);
         //   close(fd[0]);
          //  break;
          //这里不能直接就break 不然就是只读了一次 这里写了10次要一直读 直到读到了文件结尾才停止 才能读完
          }
          else if(ret==0)
          {
            printf("read file of end!\n");
            break;
          }
          else 
          {
            perror("read");
            break;
          }
    
        }
      }
      else
      {
        //error
        perror("fork!");
        return 1;
      }
    
        int status=0;
        int ret=waitpid(id,&status,0);//阻塞式等待
        if(ret>0)
        {
          printf("child is quit! single is:%d",status&0x7F);
    
        }
    
      return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88

    4种情况的分析:

    第一种:(子或父)不write,(父或子)一直read,read阻塞

    在这里插入图片描述

    代码:

    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<sys/wait.h>
    #include<sys/types.h>
    #include<string.h>
    int main()
    {
      int fd[2];
     int ret = pipe(fd);
     if(ret<0)
     {
       perror("pipe");
       return -1;
     }
    
     pid_t id=fork();//创建子进程
     if(id==0)
     {
       //child
      close(fd[0]);//子进程关闭读端 使得其只能往管道中进行写入
      const char* str="i\n";
    
      int count=5;
      while(count)
      {
      if(count>=2)//count 大于等于2就写 否则就休眠
        {
         write(fd[1],str,strlen(str));
         count--;
         sleep(1);
        }
       else{
          sleep(1000);//子进程写两次就不写了 进行休眠 但是父进程一直在读 这样就会形成读堵塞
        }
      }
      //一直写 当不再打印count时说明管道的缓冲区写满了,此时的count就是管道的容量
     }
    
     else if(id>0)
     {
       //father
       close(fd[1]);//关闭写端
      // sleep(1000);
       char arr[1000];
       while(1)
       {
         int ret=read(fd[0],arr,sizeof(arr));
          if(ret>0)//读到了内容
          {
            arr[ret]='\0';
            printf("%s",arr);
          }
          else if(ret==0)
          {
            //读到了文件结尾
            printf("read end of file!\n");
            break;
          }
          else
          {
            //read error
           // perror("read");
           printf("read error!\n");
            break;
          }
     }
     }
     else
     {
       //error
      perror("fork!");
     }
      int status=0;
      int s=waitpid(id,&status,0);//阻塞式等待
      if(s>0)
      {
      printf("child quit!\n");
      }
      else
      {
        printf("wait error!");
      }
      printf("quit singal is:%d\n",ret&0x7F);
      return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87

    第二种:(子或父) 不read ,(父或子)一直write,write阻塞

    在这里插入图片描述

    代码:

    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<sys/wait.h>
    #include<sys/types.h>
    #include<string.h>
    
    
    int main()
    {
    
      int fd[2];
     int ret = pipe(fd);
     if(ret<0)
     {
       perror("pipe");
       return -1;
     }
    
     pid_t id=fork();//创建子进程
     if(id==0)
     {
       //child
      close(fd[0]);//子进程关闭读端 使得其只能往管道中进行写入
      const char* str="i";//每次写一个字节
      int count=0;
      while(1)
      {    
      write(fd[1],str,strlen(str));
      count++;
      printf("%d\n",count);//写一次打印一次count 
      }
      //一直写 当不再打印count时说明管道的缓冲区写满了,此时的count就是管道的容量
     }
    
     else if(id>0)
     {
       //father
       close(fd[1]);//关闭写端
       sleep(1000);//直接让父进程休眠,就不会读取管道里的数据,让子进程一直往里写就行了
       char arr[1000];
       while(1)
       {
        
         int ret=read(fd[1],arr,sizeof(arr));
         while(1)
         {
          if(ret>0)//读到了内容
          {
            arr[ret]='\0';
            printf("%s",arr);
          }
          else if(ret==0)
          {
            //读到了文件结尾
            printf("read end of file!\n");
            break;
          }
          else
          {
            //read error
            perror("read");
            break;
          }
         }
       }
     }
     else
     {
       //error
      perror("fork!");
     }
        //等待子进程退出
      int status=0;
      int s=waitpid(id,&status,0);//阻塞式等待
      if(s>0)
      {
    
      printf("child quit!\n");
      }
      else
      {
        printf("wait error!");
        
      }
      
      printf("quit singal is:%d\n",ret&0x7F);
      return 0;
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91

    在这里插入图片描述

    运行结果-> 不再打印count 说明write阻塞了 测得管道缓冲区大小为65536个字节

    结合文档 本机是3.几的版本 所以是65536个字节 得到证实

    在这里插入图片描述

    第三种:.write写完后关闭,read返回值为0

    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<sys/wait.h>
    #include<sys/types.h>
    #include<string.h>
    
    int main()
    {
      int fd[2];
     int ret = pipe(fd);
     if(ret<0)
     {
       perror("pipe");
       return -1;
     }
    
     pid_t id=fork();//创建子进程
     if(id==0)
     {
       //child
      close(fd[0]);//子进程关闭读端 使得其只能往管道中进行写入
      const char* str="i\n";//每次写一个字节
    
      int count=5;
      while(count)
      {
         write(fd[1],str,strlen(str));
         count--;
         sleep(1);
      }
    	//重点
      close(fd[1]);//写完关闭 写端 父进程读的时候就会读到文件结尾 返回值为0
      exit(0);//子进程写完退出
     }
    
     else if(id>0)
     {
       //father
       close(fd[1]);//关闭写端
      // sleep(1000);
       char arr[1000];
       while(1)
       {
         int ret=read(fd[0],arr,sizeof(arr));
          if(ret>0)//读到了内容
          {
            arr[ret]='\0';
            printf("%s",arr);
          }
          else if(ret==0)
          {
            //读到了文件结尾
            printf("read end of file!\n");
            break;
          }
          else
          {
            //read error
           // perror("read");
           printf("read error!\n");
            break;
          }
       }
       
      int status=0;
      int s=waitpid(id,&status,0);//阻塞式等待
      if(s>=0)
      {
       printf("child quit!\n");
      }
      else
      {
        printf("wait error! s:%d\n",s);
        
      }
      
      printf("quit singal is:%d\n",status&0x7F);
    
      return 0;
     }
     else
     {
       //error
      perror("fork!");
     }
    
      return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90

    运行结果:写端写完关闭 读端再读 读到文件结尾 read返回值为0

    在这里插入图片描述

    第四种:read关闭,一直写,写方会被操作系统杀掉,写入无意义

    代码:

    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<sys/wait.h>
    #include<sys/types.h>
    #include<string.h>
    
    
    int main()
    {
    
      int fd[2];
     int ret = pipe(fd);
     if(ret<0)
     {
       perror("pipe");
       return -1;
     }
    
     pid_t id=fork();//创建子进程
     if(id==0)
     {
       //child
      close(fd[0]);//子进程关闭读端 使得其只能往管道中进行写入
      const char* str="i\n";//每次写一个字节
    
      while(1)
      {
         write(fd[1],str,strlen(str));
         sleep(1);
      
      }
    
      close(fd[1]);//写完关闭 写端 父进程读的时候就会读到文件结尾 返回值为0
      exit(0);
     }
    
     else if(id>0)
     {
       //father
       close(fd[1]);//关闭写端
       close(fd[0]);//父进程直接关闭读端 不读取管道信息 子进程还在一直写 没有意义 操作系统会将其杀掉
      // sleep(1000);
       char arr[1000];
       while(1)
       {
         int ret=read(fd[0],arr,sizeof(arr));
          if(ret>0)//读到了内容
          {
            arr[ret]='\0';
            printf("%s",arr);
          }
          else if(ret==0)
          {
            //读到了文件结尾
            printf("read end of file!\n");
            break;
          }
          else
          {
            //read error
           // perror("read");
           printf("read error!\n");
            break;
          }
       }
       
      int status=0;
      int s=waitpid(id,&status,0);//阻塞式等待
      if(s>=0)
      {
       printf("child quit!\n");
      }
      else
      {
        printf("wait error! s:%d\n",s);
        
      }
      
      printf("quit singal is:%d\n",status&0x7F);
    
      return 0;
     }
     else
     {
       //error
      perror("fork!");
     }
    
      return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92

    运行结果:子进程被操作系统杀掉 因为父进程不会读 子进程写的没有意义 且子进程退出信号为13 SINPIPE

    在这里插入图片描述

    注意的点(重点):

    1.pipe()创建出来的管道是要传一个元素为二的数组的 fd[0] f[1]分别代表读和写端

    2.既然创建子进程后要把父子进程的读或写端关掉一个,那么为什么不开始创建的时候父进程的读写端都是打开的呢?

    实质上是为了让子进程继承下来,后面关掉一端,是因为管道只能是单向通信的

    3.管道是自带同步与互斥机制的,这样就使得父子进程不能同时使用一块相同资源,子进程写的时候父进程不能读,只能等待子进程写完了才可以去读,即读写不能同时进行,否则会使得数据错乱。在多执行流下(父子)看到的同一份资源就是叫临界资源

    4如果写端关闭,读端就会读到文件的结尾,返回0,代表文件结束

    5.文件的生命周期随着进程的结束而结束,也就是说打开文件的进程退出了,文件也就会被释放掉,这里可能是多个进程都打开了某个文件,则所有打开此文件的进程都退出了此文件资源才会完全释放掉

    6.管道提供流式服务且管道是半双工通信

    7.匿名管道适合具有血缘关系的进程进行进程间通信,常用于父子

  • 相关阅读:
    入门:树莓派装系统、亮机,无需外接显示器键盘鼠标(保姆级教程)
    Zbrush 导出置换 然后导入vray 在 3ds max 和 maya 设置
    TCP/IP 错误号
    如何使用 Excel拆分文本单元格,基于LEFT、RIGHT、MID、SUBSTITUTE、FIND、SEARCH
    vacuum full table释放表占用的空间
    【数据结构初阶】双向带头循环链表原来是纸老虎,结构复杂,操作简单
    C#面:如何避免类型转换时的异常?
    学习pinia 介绍-State-Getters-Actions-Plugins
    java--关键字、标识符
    一起Talk Android吧(第三百六十四回:多线程之同步块)
  • 原文地址:https://blog.csdn.net/xbhinsterest11/article/details/125457333