管道和FIFO

2015/05/12 17:29
阅读数 143
  1. 管道(pipe)

      管道在Unix及Linux进程间通信是最基础的,很容易理解。管道就像一个自来水管,一端注入水,一端放出水,水只能在一个方向上流动,而不能双向流动。管道是典型的单向通信,即计算机网络中所说的“半双工”。管道又名匿名管道,所以只能用在具有公共祖先的进程之间使用,通常使用在父子进程之间通信。通常是父进程创建一个管道,然后fork一个子进程,此后父子进程共享这个管道进行通信。

  管道由pipe函数创建,函数原型如下:

      #include<unistd.h>

      int  pipe(int fd[2]); 成功返回0,否则返回-1;参数fd返回两个文件描述符,fd[0]为读,fd[1]为写,fd[1]的输入是fd[0]的输出。即fd[0]对应读端,fd[1]对应写端。

      举例说明一下管道的用法:模拟client-server通信过程,父进程模拟client,子进程模拟server。server向client发送一个字符串,client接收到输出到屏幕。

pipe1.c

 /*
    单向通信 = “半双工”

*/
#include <stdio.h>
#include <stdlib.h>

#include <string.h>
#include <sys/types.h> //
#include <errno.h>     //errno == EINTR

#include <unistd.h>  // int pipe(int fd[2]),wait

int main(int argc , char** argv)
{
      int fd[2];
      pid_t child;
       
      char buf[100];
      memset(buf,0,100);
       
      //创建管道 
      if(pipe(fd) == -1)
      {
          perror("pipe error.\n");
          exit(1);  
      }
      
      child = fork();
      if(child == -1)
      {
           perror("fork error.\n");
           exit(1);
      }
      //进入子进程
      if(child == 0)
      {
           printf("server input a msg: ");
           scanf("%s",buf);
           
           close(fd[0]);
           write(fd[1],buf,strlen(buf));
           
           exit(0);   //退出子进程
      }
      
       memset(buf, 0, 100);
       close(fd[1]);
       read(fd[0],buf, 100);
       printf("client recv a msg: %s",buf);
       waitpid(child,NULL,0);   //等待子进程运行结束
       
       return 0;
      
}

上面程序的细节问题在于子进程需要关闭读端,父进程需要关闭写端。因为管道最早提出时候是单向,虽然现在有些系统提供全双工的管道。那么如何采用管道实现双向通信呢?很显然我们需要两个管道,控制两个不同的数据流向。现在有模拟一个Client和Server双向通信的过程,Client与Server之间可以相互发送和接收信息。此时需要两个管道进行模拟,管道1模拟Server写Client读数据流向,管道2模拟Client写Server读数据流向。代码如下所示:

pipe2.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <errno.h>
#include <sys/types.h>

#include <unistd.h>


int main(int argc, char** argv)
{
   int fd1[2],fd2[2];
   pid_t child;
   
   char buf[100];
   
   if(pipe(fd1) == -1)
   {
       perror("pipe error.\n");
       exit(1);
   }
   
   if(pipe(fd2) == -1)
   {
       perror("pipe error.\n");
       exit(1);
   }
   
    child = fork();
    if(child == -1)
    {
        perror("fork error");
        exit(1);
    }
    if(child == 0)
    {
        memset(buf,0,100);
        printf("server input msg: ");
        gets(buf);
        close(fd1[0]);
        write(fd1[1],buf,strlen(buf));
        
        close(fd2[1]);
        memset(buf, 0, 100);
        read(fd2[0],buf,100); 
        printf("server recv: %s\n",buf);
          
        exit(0);
    }
     
    memset(buf,0,100);
    close(fd1[1]);
    read(fd1[0],buf,100);
    printf("client recv : %s\n",buf);
    
    memset(buf,0,100);
    printf("client input msg:");
    gets(buf);
    close(fd2[0]);
    write(fd2[1],buf,strlen(buf));
    
    waitpid(child, NULL,0); 
    
    return 0;
}

2 FIFO(first in first out)

  FIFO又名有名管道,相对于上述管道而言。管道没有名字,因此只能在具有共同祖先进程的各个进程之间通信,无法在无亲缘关系的两个进程之间创建一个管道进行通信。为此有了FIFO,类似管道,也是一个单向(半双工)数据流,每个FIFO有一个路径名与之关联,从而允许无亲缘关系的进程访问同一个FIFO。FIFO有mkfifo函数创建。

#include <sys/types.h>

#include <sys/stat.h>

int mkfifo(const char *pathname,mode_t mode); 成功返回0,出错返回-1。pathname是一个普通的路径名,是FIFO的名字,mode指定文件的权位。

在创建FIFO后,必须打开来读或者打开来写,不能打开来既读既写(因为FIFO是半双工)。现在采用FIFO实现上面的第二个例子,代码如下:

fifo.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/types.h>
#include <errno.h>
#include <unistd.h>   //memset

#include <sys/stat.h> //int mkfifo(const char* pathname, mode_t mode)

#include <fcntl.h>  //open, O_RDONLY


#define FILE_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)

#define FIFO1  "/tmp/fifo.1"
#define FIFO2  "/tmp/fifo.2"

int main(int argc, char** argv)
{
     int readfd,writefd;
     pid_t child;
     
     char buf[100];
     
     if((mkfifo(FIFO1,FILE_MODE) < 0) 
         && (errno != EEXIST ))
     {
          perror("mkfifo error.\n");
          exit(1);
     }
     if((mkfifo(FIFO2,FILE_MODE) < 0)
         && (errno != EEXIST))
     {
          unlink(FIFO1);
          perror("mkfifo error.\n");
          exit(1);
     }
     
     child = fork();
     if(child == -1)
     {
         perror("fork error.\n");
         exit(1);
     }
     
     if(child == 0)
     {
         
         printf("in server:\n");
         readfd  = open(FIFO1,O_RDONLY,0);
         writefd = open(FIFO2,O_WRONLY,0);
         
         memset(buf,0,100);
         printf("server input msg:");
         gets(buf); 
         write(writefd,buf,strlen(buf));
         
         memset(buf,0,100);
         read(readfd,buf,100);
         printf("server recv: %s\n",buf);
         
         exit(0);
         
     }
     printf("in client:\n");

//没出现死锁
     writefd = open(FIFO1,O_WRONLY,0);
     readfd  = open(FIFO2,O_RDONLY,0);

//出现死锁
     //readfd  = open(FIFO2,O_RDONLY,0);
     //writefd = open(FIFO1,O_WRONLY,0);

     memset(buf,0,100);
     read(readfd,buf,100);
     printf("client recv : %s\n",buf);
     
     memset(buf,0,100);
     printf("client input msg:");
     gets(buf);
     write(writefd,buf,strlen(buf));
     waitpid(child,NULL,0);
     
     close(readfd);
     close(writefd);
     unlink(FIFO1);
     unlink(FIFO2);
     
     return 0;
}

上面的程序当中父进程打开FIFO的顺序不能颠倒,否则会造成死锁。因为在当前没有任何进程打开某个FIFO来写的时候,打开该FIFO来读的进程将会阻塞交换父进程中两个open的调用顺序后,父子进程都将打开同一个FIFO进行读,而当前没有任何进程来打开该文件进行写,于是父子进程都阻塞,造成死锁

下面采用FIFO实现无亲缘关系的两个进程之间的通信。Client与Server是两个独立的进程。

fifo.h

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

#include <sys/types.h>
#include <sys/stat.h>

#include <fcntl.h>
#include <errno.h>
#include <unistd.h>


#define FILE_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
#define FIFO1  "/tmp/fifo.1"
#define FIFO2  "/tmp/fifo.2"

server.c

#include "fifo.h"

int main(int argc, char** argv)
{

    int readfd, writefd;
    char buf[100];
    
    if((mkfifo(FIFO1,FILE_MODE) < 0) 
    &&(errno != EEXIST) )
    {
        perror("mkfifo error.\n");
        exit(1);
    }
    
    if((mkfifo(FIFO2,FILE_MODE) <0 )  
    &&(errno != EEXIST))
    {
         perror("mkfifo error.\n");
         exit(1);
    }
    
    readfd  = open(FIFO1,O_RDONLY,0);
    writefd = open(FIFO2,O_WRONLY,0);
    
    printf("server input msg:");
    gets(buf);
    write(writefd, buf, strlen(buf));
    read(readfd, buf, 100);
    printf("server recv: %s",buf);
    return 0;
}

client.c

#incldue "fifo.h"

int main(int argc,char** argv)
{
     int readfd,writefd;
     char buf[100];
     
     if((mkfifo(FIFO1,FILE_MODE) < 0)
     && (errno != EEXIST))
     {
           perror("mkfifo error.\n");
           exit(1);
     }
     if((mkfifo(FIFO2,FILE_MODE) < 0) 
     && (errno != EEXIST))
     {
          perror("mkfifo error.\n");
          exit(1);
     }
     
     writefd = open(FIFO1,O_WRONLY,0);
     readfd  = open(FIFO2,O_RDONLY,0);
     
     memset(buf,0,100);
     read(readfd,buf,100);
     printf("client recv: %s\n");
     
     memset(buf,0,100);
     printf("client input msg:");
     gets(buf);
     writefd(writefd,buf,stelen(buf));
     
     close(readfd);
     close(writefd);
     
     unlink(readfd);
     unlink(writefd);
     return 0;
}


展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部