Linux系统编程--进程间通信 ---管道篇

Posted 蚍蜉撼树谈何易

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux系统编程--进程间通信 ---管道篇相关的知识,希望对你有一定的参考价值。

进程间通信

进程间通信的定义

进程间通信就是在不同进程之间传播或交换信息

进程间通信的目的

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

进程间通信的发展。

管道
System V进程间通信
POSIX进程间通信

进程间通信的分类

管道
匿名管道pipe
命名管道
System V IPC
System V 消息队列
System V 共享内存
System V 信号量
POSIX IPC
消息队列
共享内存
信号量
互斥量
条件变量
读写锁

进程通信的方式及原理介绍

管道

管道是Unix中最古老的进程间通信的形式。
我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”
管道分类:
匿名管道和命名管道

匿名管道

匿名管道的创建

int pipe(fd[2]);
在这里插入图片描述
头文件#include<unistd.h>
返回值: 0代表成功创建,-1代表管道创建失败
参数为输出型参数,其中数组第一个元素为读端,第二个为写入端。

匿名管道的特性:

1.管道是半双工的,只能一端写,一端读。
2.匿名管道创建出来的话是没有标识符的,导致了其他进程无法查询到此缓冲区。但是创建的进程可以通过读写端进行操作。
3.匿名管道只支持具有亲缘关系的进程间进行通信。一般是父子进程。本质原因:具有亲缘关系的进程复制了父进程的PCB
4,当管道为空时,在默认情况下,read会阻塞。
验证:

 1 #include<iostream>
  2 #include<unistd.h>
  3 #include<fcntl.h>
  4 #include<string.h>
  5 #include<stdlib.h>
  6 using namespace std;
  7 int main()
  8 {
  9     int fk[2]={0};
 10     pipe(fk);
 11     pid_t pd=fork();
 12     if(pd<0)
 13     {
 14         cerr<<("fork error");
 15         exit(1);
 16     }
 17 
 18     if(pd>0)
 19     {
 20         close(fk[0]);
 21         sleep(20);                                                                                                                                                                    
 22         const char*str="hello my son ,i am your father";
 23         write(fk[1],str,strlen(str));
 24         close(fk[1]);
 25         cout<<"i send you "<<endl;
 26     }
 27     else if(pd==0)
 28     {
 29         close(fk[1]);
 30         char buf[128];
 31      read(fk[0],buf,128-1);
 32         cout<<"i receive your message"<<endl;
 33 
 34         close(fk[0]);
 35 
 36         cout<<buf<<endl;
 37     }
 38     return 0;
 39 }

在这里插入图片描述

5,管道的大小为64k。
6,write函数在往文件中写入内容时,若写满,则会阻塞管道。

1 #include<iostream>                                                                                                                                                                    
  2 #include<unistd.h>
  3 #include<fcntl.h>
  4 #include<string.h>
  5 #include<stdlib.h>
  6 #include<stdio.h>
  7 using namespace std;
  8 int main()
  9 {
 10     int fk[2]={0};
 11     pipe(fk);
 12     pid_t pd=fork();
 13     if(pd<0)
 14     {
 15         cerr<<("fork error");
 16         exit(1);
 17     }
 18 
 19     if(pd>0)
 20     {
 21        // close(fk[0]); 
 22         sleep(20);
 23        // const char*str="hello my son ,i am your father";
 24        int count=0;
 25        while(1)
 26        {
 27 
 28        // write(fk[1],'i',1);
 29        write(fk[1],"c",1);
 30        count++;
 31         cout<<"after write:"<<count<<endl;
 32        }
 33         close(fk[1]);
 34         cout<<"i send you "<<endl;
 35 
 36     return 0;
 37 }

在这里插入图片描述

7,匿名管道的生命周期是追随进程的。
8,管道提供字节流服务,字节流:描述符的前后两个数据间没有明显的边界。
9,从管道中读取内容时,不是管道内容的一份拷贝,而是直接将内容拿走。

1 #include<iostream>
  2 #include<unistd.h>
  3 #include<fcntl.h>
  4 #include<string.h>
  5 #include<stdlib.h>
  6 #include<stdio.h>
  7 using namespace std;
  8 int main()
  9 {
 10     int fk[2]={0};
 11     pipe(fk);
 12     pid_t pd=fork();
 13     if(pd<0)
 14     {
 15         cerr<<("fork error");
 16         exit(1);
 17     }                                                                                                                                                                                 
 18 
 19     if(pd>0)
 20     {
 21         close(fk[0]);
 22         const char*str="hello my son ,i am your father";
 23         write(fk[1],str,strlen(str));
 24         close(fk[1]);
 25         cout<<"i send you "<<endl;
 26       }
 27     else if( pd==0 )
 28     {
 29         close(fk[1]);
 30         char buf[64]={0};
 31         while(1)
 32         {
 33           size_t ret=read(fk[0],buf,64);
 34           sleep(5);
 35           cout<<ret<<endl;
 36 
 37         }
 38     }
 39     return 0;
 40     }

在这里插入图片描述

10,当对管道进行读写操作时,此时若读写的字节数小于pipe_size 512字节,则保证操作的原子性。原子性:执行了,必须要执行完。
11.如果写端不关闭文件描述符,且不写入,则读端可能长时间阻塞。
可能:比如管道内本身就写入了一些内容的话,此时就会读完阻塞。。

12.如果写端在写入完成时,关闭文件描述符,读端在读取完成后,会读到文件尾。

  1 #include<iostream>                                                                                                                                                                  
    2 #include<unistd.h>
    3 #include<fcntl.h>
    4 #include<string.h>
    5 #include<stdlib.h>
    6 #include<stdio.h>
    7 using namespace std;
    8 int main()
    9 {
   10     int fk[2]={0};
   11     pipe(fk);
   12     pid_t pd=fork();
   13     if(pd<0)
   14     {
   15         cerr<<("fork error");
   16         exit(1);
   17     }
   18 
   19     if(pd==0)
   20     {
   21         close(fk[0]); 
   22         const char*str="hello my father ,i am your son\\n";
   23         int count=0;
   24         while(1)
   25         {
   26           
   27         write(fk[1],str,strlen(str));  
   28         cout<<"CHILD :"<<count++<<endl;
   29         if(count==3)
   30         {
   31             close(fk[1]);
   32             break;
   33         }
   34         }        
   35       
   36     exit(2);
   37     }
   38     else  if( pd>0 )
   39     {
    40         int count=0;
   41         close(fk[1]);
   42         char buf[64]={0};
   43         while(1)
   44         {
   45 
   46           size_t ret=read(fk[0],buf,64);
   47           if(ret>0)
   48           {
   49               cout<<buf<<endl;
   50               sleep(1);
   51           }
   52           cout<<ret<<endl;
   53           sleep(5);
   54        }        
   55 
   56     //    char buf[64]={0};
   57     //    while(1)
   58     //    {
   59 
   60     //    }                                                                                                                                                                         
   61     }
   62     return 0;
   63 }


在这里插入图片描述

13,如果读端关闭,写端进程可能会被进程直接干掉。

1 #include<iostream>
  2 #include<unistd.h>
  3 #include<fcntl.h>
  4 #include<string.h>
  5 #include<stdlib.h>
  6 #include<stdio.h>
  7 using namespace std;
  8 int main()
  9 {
 10     int fk[2]={0};
 11     pipe(fk);
 12     pid_t pd=fork();
 13     if(pd<0)
 14     {
 15         cerr<<("fork error");
 16         exit(1);
 17     }
 18 
 19     if(pd==0)
 20     {
 21         close(fk[0]);
 22         const char*str="hello my father ,i am your son\\n";
 23         int count=0;
 24         while(1)
 25         {
 26 
 27         write(fk[1],str,strlen(str));
 28         cout<<"CHILD :"<<count++<<endl;
 29         }
 30                                                                                        
 31     exit(2);
 32     }
 33     else if( pd>0 )
 34     {
 35         int count=0;
 36         close(fk[1]);
 37         char buf[64]={0};
 38         while(1)
 39         {
 39         {
 40 
 41           size_t ret=read(fk[0],buf,64);
 42           if(ret>0)
 43           {
 44               cout<<buf<<endl;
 45               sleep(1);
 46           }
 47           if(count++==3)
 48           {
 49               close(fk[0]);
 50           }
 51         }
 52 
 53     //    char buf[64]={0};
 54     //    while(1)
 55     //    {
 56 
 57     //    }
 58     }
 59     return 0;
 60 }                 

sheel脚本: while : ; do ps axj | grep mypipe | grep -v grep;echo “#########”;sleep 1; done
可以得出子进程已经被干掉–即写端被干掉
在这里插入图片描述
获取退出原因

1 #include<iostream>                                                                                                                                                                    
  2 #include<sys/wait.h>
  3 #include<unistd.h>
  4 #include<fcntl.h>
  5 #include<string.h>
  6 #include<stdlib.h>
  7 #include<stdio.h>
  8 using namespace std;
  9 int main()
 10 {
 11     int fk[2]={0};
 12     pipe(fk);
 13     pid_t pd=fork();
 14     if(pd<0)
 15     {
 16         cerr<<("fork error");
 17         exit(1);
 18 
 19     }
 20 
 21     if(pd==0)
 22     {
 23         close(fk[0]);
 24         const char*str="hello my father ,i am your son\\n";
 25         int count=0;
 26         while(1)
 27         {
 28 
 29             write(fk[1],str,strlen(str));
 30             cout<<"CHILD :"<<count++<<endl;
 31 
 32         }
 33 
 34         exit(2);
 35 
 36     }
 37     else if( pd>0  )
 38     {
 39         int count=0;
 40         close(fk[1]);
 41         char buf[64]={0};
 42         while(1)
 43         {
 44             {
 45 
 46                 size_t ret=read(fk[0],buf,64);
 47                 if(ret>0)
 48                 {
 49                     cout<<buf<<endl;
 50                     buf[ret]='\\0';
 51                     sleep(1);
 52 
 53                 }
 54                 if(count++==3)
 55                 {
 56                     close(fk[0]);
 57                     break;
 58                 }
 59             }
 60 
 61         }
 62         
 63                 int status=0;
 64                 waitpid(pd,&status,0);
 65                 cout<<"退出码为"<<(status&0x7F)<<endl;
 66 
 67         }
 68         return

以上是关于Linux系统编程--进程间通信 ---管道篇的主要内容,如果未能解决你的问题,请参考以下文章

Linux系统编程——进程间通信:共享内存

[Linux用户空间编程-1]:Linux进程间主要的通信方式

Linux系统编程——进程间通信:信号中断处理

Linux网络编程--进程间通信

Linux系统编程之进程间通信之浅谈信号

Linux/Unix 多线程通信