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 0;
 69 
 70     }                                                                                                                                                                                 
 71 


在这里插入图片描述

在这里插入图片描述

操作系统是怎么知道你保存的文件属性

在这里插入图片描述

匿名管道的使用场景

在这里插入图片描述

在这里插入图片描述

匿名管道的本质

在这里插入图片描述
在这里插入图片描述

修改管道默认属性

在这里插入图片描述

 1 #include<iostream>
  2 #include<unistd.h>
  3 #include<fcntl.h>
  4 #include<stdlib.h>
  5 using namespace std;
  6 int main()
  7 {
  8     int fd[2];
  9 
 10    int ret= pipe(fd);
 11    if(ret<0)
 12    {
 13      cerr<<"pipe()";
 14      exit(1);
 15    }
 16    int fg=fcntl(fd[0],F_GETFL);
 17    cout<<"读端"<<fg<<endl;
 18    int fg1=fcntl(fd[1],F_GETFL);
 19    cout<<"写端"<<fg1<<endl;
 20   fcntl(fd[0],F_SETFL,fg|O_NONBLOCK);
 21     fg=fcntl(fd[0],F_GETFL);
 22    cout<<"更改后读端"<<fg<<endl;
 23    fcntl(fd[1],F_SETFL,fg1|O_NONBLOCK);
 24     fg1=fcntl(fd[1],F_GETFL);                                                                                                                                                         
 25    cout<<"更改后写端"<<fg1<<endl;
 26    return 0;
 27 
 28 }


在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

命名管道

区别于匿名管道来说,命名管道可以被用于非亲缘关系的进程间通信

使用mkfifo创建命名管道

在这里插入图片描述
测试:
在这里插入图片描述

makefile文件

在这里插入图片描述

在这里插入图片描述

使用函数创建

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
server端:

 1 #include<iostream>
  2 #include<unistd.h>
  3 #include<fcntl.h>
  4 #include<sys/stat.h>
  5 #include<cstdlib>
  6 using namespace std;
  7 
  8 int main()
  9 {
 10     umask(0);
 11     if(-1==mkfifo("./pipe",0664))
 12     {
 13         cerr<<"mkfifo()"<<endl;
 14         exit(1);
 15     }
 16     int fd=open("./pipe",O_RDONLY);
 17     if(fd<0)
 18     {
 19         cerr<<"open()"<<endl;
 20         exit(-1);
 21     }
 22     char buf[64];
 23     while(1)
 24     {
 25      ssize_t s=  read(fd,buf,sizeof(buf)-1);
 26      if(s>0)
 27      {
 28          buf[s]=0;
 29          cout<<"server get message #"<<buf;                                            
 30      }
 31      else if (s==0)
 32      {
 33          cout<<"client quit ,i have to quit!!"<<endl;
 34          break;
 35      }
 36      else
 37      {
 38          cout<<"读取错误"<<endl;
 39          return -2;
 40      }
 41 
 42     }
 43    
 44     return 0;
 45 
 46 }          

client端

1 #include<iostream>
  2 #include<unistd.h>
  3 #include<fcntl.h>
  4 #include<stdio.h>
  5 #include<cstdlib>
  6 using namespace std;
  7 int main()
  8 {
  9     int fd=open("./pipe",O_WRONLY);
 10     if(fd<0)
 11     {
 12         cerr<<"open()"<<endl;
 13         exit(1);
 14     }
 15     char buf[64];
 16     while(1)
 17     {
 18       cout<<"please enter your words#";
 19       fflush(stdout);                                                                  
 20        ssize_t s= read(0,buf,sizeof(buf)-1);
 21        if(s>0)
 22        {
 23            buf[s]=0;
 24            write(fd,buf,sizeof(buf)-1);
 25        }
 26     }
 27     return 0;
 28 }

makfile文件

1 all:client server
  2 client:client.cpp
  3     g++ $^ -o $@
  4 server:server.cpp
  5     g++ $^ -o $@
  6 .PNONY:clean
  7 clean:
  8     rm client server pipe 

在这里插入图片描述

  • 23
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 24
    评论
进程间通信是指在不同进程之间进行数据交换和同步的一种技术。Linux提供了多种进程间通信的方式,包括管道、消息队列、共享内存和信号量等。在实验六中,我们将学习如何使用这些方式进行进程间通信。 1. 管道 管道是一种半双工的通信方式,它可以在两个进程之间传递数据。在Linux中,管道分为匿名管道和命名管道。匿名管道只能用于父子进程之间的通信,而命名管道可以用于任意两个进程之间的通信。 使用匿名管道进行进程间通信的步骤如下: - 父进程创建管道,并调用fork函数创建子进程。 - 子进程通过管道接收数据。 - 父进程通过管道发送数据。 - 子进程接收到数据后进行处理。 使用命名管道进行进程间通信的步骤如下: - 创建命名管道。 - 打开命名管道并进行读写操作。 2. 消息队列 消息队列是一种进程间通信机制,它允许不同进程之间通过一个消息传递序列来进行通信。在Linux中,每个消息都有一个类型,接收进程可以选择接收某个特定类型的消息。 使用消息队列进行进程间通信的步骤如下: - 创建消息队列。 - 发送消息到消息队列。 - 接收消息并进行处理。 3. 共享内存 共享内存是一种进程间通信的方式,它允许不同进程之间共享同一个物理内存区域。这种方式比较高效,但需要考虑进程间的同步和互斥问题,否则会出现数据不一致的情况。 使用共享内存进行进程间通信的步骤如下: - 创建共享内存区域。 - 进程通过共享内存区域进行数据交换。 - 进程需要进行同步和互斥操作。 4. 信号量 信号量是一种进程间同步的机制,它可以用来保证不同进程之间的共享资源在同一时刻只能被一个进程访问。在Linux中,每个信号量都有一个计数器,当计数器为0时,进程需要等待;当计数器大于0时,进程可以继续执行。 使用信号量进行进程间通信的步骤如下: - 创建信号量。 - 进程对信号量进行P操作(等待)。 - 进程对信号量进行V操作(释放)。 总体来说,不同的进程间通信方式各有优缺点,应根据实际需求选择适合的方式。在实验六中,我们将通过编写代码来学习如何使用这些方式进行进程间通信

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值