linux进程间通信:匿名管道&&命名管道

Posted mbf330

tags:

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

linux进程间通信:匿名管道&&命名管道

什么是管道
管道是Unix中最古老的进程间通信的形式。
我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”

1.匿名管道

#include <unistd.h>
功能:创建一无名管道
原型
int pipe(int fd[2]);
参数:fd:文件描述符数组,其中fd[0]表示读端, fd[1]表示写端
返回值:成功返回0,失败返回错误代码

(1)匿名管道的代码实现以及分析:

管道的四个重要结论
1.当实际读取时,如果读取条件不满足,读取端会阻塞。(管道为空)
2.当实际写入时,如果写入条件不满足,写入端会阻塞。(管道已写满)
3.如果写端不写且关闭文件描述符,读端会读到文件结尾。
4.如果读端关闭,写端进程可能会被系统直接杀掉。

在这段代码中,写端(子进程)每隔一秒写入一次,读端(父进程)便读取一次,实现一个简单的匿名管道,并以此来分析上述四个结论。

a.当实际读取时,如果读取条件不满足,读取端会阻塞。(管道为空)

写端每隔一秒写入一次,读端不断读取,读取条件不满足时,读取端会阻塞。在运行截图中,每读取一次便会空一行,便是读端阻塞的结果

#include<stdio.h>                                                                    
#include<unistd.h>
#include<string.h>
//当实际读取时,如果读取条件不满足,读取端会阻塞(管道为空)
//当实际写入时,如果写入条件不满足,写入端会阻塞(管道已写满)
//如果写端不写且占用文件描述符,读端不会阻塞
//如果读端关闭,写端进程可能会被系统直接杀掉
int main()

   //文件描述符数组,pipefd[0]为读,pipefd[1]为写
   int pipefd[2]=0;
   pipe(pipefd);
   //创建进程
   pid_t id=fork();
   if(id==0)
   
     //child,write
     //防止读写同时进行,关闭读区
     close(pipefd[0]);
     const char *msg="I am child!\\n";
     int count=0;
     while(1)
     
       //write函数,返回值为:数组下标,字符串,字符串长度
       write(pipefd[1],msg,strlen(msg));
       //写端每隔一秒写入一次
       sleep(1);
       printf("child:%d\\n",count++);
     
   
   else
   
     //father,read
     close(pipefd[1]); 
     char buffer[64];
     while(1)
     
       //关闭写区
       close(pipefd[1]);                                                              
       //read函数,返回值:数组下标(从哪读),字符数组(读到哪),常量(读多少)
       ssize_t s=read(pipefd[0],buffer,sizeof(buffer)-1);
       if(s>0)
       
         //以‘0’结尾
         buffer[s]=0;
         //父进程读取子进程所写的消息
         printf("father get message:%s\\n",buffer);
         //sleep(1);
       
     
   
return 0;
                        

b.当实际写入时,如果写入条件不满足,写入端会阻塞。(管道已写满)

在这段代码中,写端不断写入,读端每隔一秒读取一次,写入条件不满足时,写端阻塞。

if(id==0)
   
     //child,write
     //防止读写同时进行,关闭读区
     close(pipefd[0]);
     const char *msg="I am child!\\n";
     int count=0;
     while(1)
     
       //write函数,返回值为:数组下标,字符串,字符串长度
       write(pipefd[1],msg,strlen(msg));
       //写端每隔一秒写入一次
       //sleep(1);
       printf("child:%d\\n",count++);
     
   
   else
   
     //father,read
     close(pipefd[1]); 
     char buffer[64];
     while(1)
     
       //关闭写区
       close(pipefd[1]);                                                              
       //read函数,返回值:数组下标(从哪读),字符数组(读到哪),常量(读多少)
       ssize_t s=read(pipefd[0],buffer,sizeof(buffer)-1);
       if(s>0)
       
         //以‘0’结尾
         buffer[s]=0;
         //父进程读取子进程所写的消息
         printf("father get message:%s\\n",buffer);
         //每隔一秒读取一次
         sleep(1);
       
     
   


在上述两个实例中,子进程与父进程一个慢时另一个也慢,一个快时另一个也快,叫做进程间同步。

c.如果写端不写且关闭文件描述符,读端会读到文件结尾。

在这段代码中,写端写入五次后,关闭写端,读端仍然不断读取,最终读到文件结尾。

if(id==0)
   
     //child,write
     //防止读写同时进行,关闭读区
     close(pipefd[0]);
     const char *msg="I am child!\\n";
     int count=0;
     while(1)
     
       //write函数,返回值为:数组下标,字符串,字符串长度
       write(pipefd[1],msg,strlen(msg));
       //写端每隔一秒写入一次
       //sleep(1);
       printf("child:%d\\n",count++);
       if(count==5)
       
          //count为5时,关闭写端
      	  close(pipefd[1]);
      	  break;
       
     
     exit(2);
   
   else
   
     //father,read
     close(pipefd[1]); 
     char buffer[64];
     while(1)
     
       //关闭写区
       close(pipefd[1]);                                                              
       //read函数,返回值:数组下标(从哪读),字符数组(读到哪),常量(读多少)
       ssize_t s=read(pipefd[0],buffer,sizeof(buffer)-1);
       if(s>0)
       
         //以‘0’结尾
         buffer[s]=0;
         //父进程读取子进程所写的消息
         printf("father get message:%s\\n",buffer);
         //每隔一秒读取一次
         sleep(1);
       
       printf("father exit return :%d",s);
     
   

d.如果读端关闭,写端进程可能会被系统直接杀掉。

这段代码中,读端读取三秒后关闭,写端(子进程)变成僵尸进程,被系统杀掉

if(id==0)
   
     //child,write
     //防止读写同时进行,关闭读区
     close(pipefd[0]);
     const char *msg="I am child!\\n";
     int count=0;
     while(1)
     
       //write函数,返回值为:数组下标,字符串,字符串长度
       write(pipefd[1],msg,strlen(msg));
       //写端每隔一秒写入一次
       //sleep(1);
       printf("child:%d\\n",count++);
       if(count==5)
       
          //count为5时,关闭写端
      	  close(pipefd[1]);
      	  break;
       
     
     exit(2);
   
   else
   
     //father,read
     close(pipefd[1]); 
     char buffer[64];
     while(1)
     
       //关闭写区
       close(pipefd[1]);   
       int count=0;                                                           
       //read函数,返回值:数组下标(从哪读),字符数组(读到哪),常量(读多少)
       ssize_t s=read(pipefd[0],buffer,sizeof(buffer)-1);
       if(s>0)
       
         //以‘0’结尾
         buffer[s]=0;
         //父进程读取子进程所写的消息
         printf("father get message:%s\\n",buffer);
         //每隔一秒读取一次
         sleep(1);
       
       if(count++==3)
       
       	  close(pipefd[0]);
       
     
   

(2)管道的特点

1.只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;通常,一个管道由一个进程创建,然后该进程调用fork,此后父、子进程之间就可应用该管道。
2.管道提供流式服务。
3.一般而言,进程退出,管道释放,所以管道的生命周期随进程。
4.一般而言,内核会对管道操作进行同步与互斥。
5.管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道。

2.命名管道

1.匿名管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。
2.如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道
3.命名管道是一种特殊类型的文件

(1)匿名管道与命名管道的关系

1.匿名管道由pipe函数创建并打开。
2.命名管道由mkfifo函数创建,打开用open。
3.FIFO(命名管道)与pipe(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一但这些工作完成之后,它们具有相同的语义。

(2)简单命名管道的创建

#include<stdio.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/types.h>
int main()

  //创建一个命名管道,名为fifoTest,如果失败打印mkfifo faild!                       
  if(-1==mkfifo("./fifoTest",2333)) 
  
    perror("mkfifo faild!\\n");
    return 1;
  

(3)命名管道中实现简单的通信

创建两个不相关的进程:server.c和client.c,在client中输入字符串,在server中读取。

//server.c
#include<stdio.h>                                                                  
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#define FIFO_FILE "./fifo"
int main()

  umask(0);
  if(-1==mkfifo(FIFO_FILE,0666))
  
    perror("mkfifo faild!\\n");
    return -1;                                               
  
  //打开命名管道“./fifo”
  int fd=open("FIFO_FILE",O_RDONLY);
  char buf[100];
  if(fd>=0)
  
    while(1)
    
      //buf从client进程读取字符串
      ssize_t s=read(fd,buf,sizeof(buf)-1);
      //读取成功则打印字符串
      if(s>0)
      
        buf[s]=0;                                                                  
        printf("client#%s",buf);
      
      else if(s==0)
                                                            
        printf("client quit,me too!\\n");
        break;
      
     //读取失败打印read faild!
      else
      
        perror("read faild!\\n");
        break;
      
    
  
  return 0;
        
//client.c
int main()

  //打开命名管道
  int fd=open(FIFO_FILE,O_WRONLY);
  if(fd<0)
                                                            
    perror("open error!\\n");
    return 1;
  
  else
  
    while(1)
    
      printf("please enter message#");
      fflush(stdout);
      char buf[100];
      ssize_t s = read(0,buf,sizeof(buf)-1);
      if(s>0)
      
         //'\\0'结尾
         buf[s]=0;
         write(fd,buf,s);                                                            
      
      
  
  return 0;

运行结果:

以上是关于linux进程间通信:匿名管道&&命名管道的主要内容,如果未能解决你的问题,请参考以下文章

Linux进程间通信

Linux进程间通信

Linux进程间通信

Linux-进程间通信

Linux-进程间通信

Linux-进程间通信