进程间的通信(IPC)——管道(pipe)与命名管道(fifo)

一、管道的概念

管道的实质是一个内核缓冲区,进程以先进先出(FIFO, First In First Out)的方式从缓冲区存取数据:管道一端的进程顺序地将进程数据写入缓冲区,另一端的进程则顺序地读取数据,该缓冲区可以看做一个循环队列,读和写的位置都是自动增加的,一个数据只能被读一次,读出以后再缓冲区都不复存在了。管道也可以看成一个文件,但不是普通文件,也不属于任何文件系统,自立门户,构成一个的文件系统,存在于内存中。
管道的局限性:
1.半双工,数据只能在一个方向流动;
2.管道只能在具有共同的父进程之间的两个进程使用;
管道的创建:
管道可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。管道是通过调用pipe函数创建的。
pipe()函数

#include <unistd.h>
int pipe(int fd[2]); //返回值:若成功,返回0,若出错,返回-1.

参数fd返回的两个文件描述符:fd[0]为读而打开,fd[1]为写而打开,fd[1]的输出是fd[0]的输入。
以下演示管道图示:
在这里插入图片描述
进程先调用pipe,创建管道,接着调用fork,从而创建了父进程和子进程的IPC通道
在这里插入图片描述
如果我们想从父进程传输数据到子进程,父进程关闭读端fd0,子进程断开写端fd1。
在这里插入图片描述

代码演示

下面编写一个程序,是父进程将给子进程发数据:

  #include <stdio.h>
  #include <string.h>
  #include <errno.h>
  #include <unistd.h>
  #include <sys/types.h>
  #include <sys/wait.h>
   
  #define  SENTENCE  "Parent say that:hello, pig child"
  
  int main(int argc, char **argv)
  {
          int         pipe_fd[2];
          int         pid;
          int         rd;
          int         wstatus;
          char        buf[1024];
  
  
          if( pipe(pipe_fd) < 0)
          {
                  printf("create pipe failure: %s\n", strerror(errno));
                  return -1;
          }
  
          if( (pid = fork()) < 0)
          {
                  printf("create child failure: %s\n", strerror(errno));
                  return -2;
          }
          else if(pid == 0)
          {
                  close(pipe_fd[1]);
  
                  memset(buf, 0 ,sizeof(buf));
                  rd = read(pipe_fd[0], buf, sizeof(buf));
                  if( rd < 0)
                  {
                          printf("child read from pipe failure: %s\n", strerror(errno));
                          return -3;
                  }
  
                  printf("child read %d btyes data from pipe: %s\n", rd, buf);
                  return 0;
          }
  
          close(pipe_fd[0]);
  
  
          if(write(pipe_fd[1], SENTENCE, strlen(SENTENCE)) < 0)
          {
                  printf("parent write data to pipe failure: %s\n", strerror(errno));
                  return -4;
          }
  
          printf("parent waiting child to exit...\n");
          wait(&wstatus);
  
          return 0;
  }

首先父进程创建管道之后fork(),这时子进程会继承父进程所有打开的文件描述符(包括管道),这时对于一个管道就有4个读写端(父子进程各有一对管道读写端),如果需要父进程往子进程里写数据,则需要在父进程中关闭读端,在子进程中关闭写端;而如果需要子进程往父进程中写数据,则可以在父进程关闭写端,然后子进程中关闭读端。
程序运行结果:
在这里插入图片描述

二、命名管道(fifo)

前面讲到的未命名的管道只能在两个具有亲缘关系的进程之间通信,通过命名管道(Named PiPe)FIFO,不相关的进程也能交换数据。FIFO不同于管道之处在于它提供一个路径与之关联,以FIFO的文件形式存在于系统中。它在磁盘上有对应的节点,但没有数据块——换言之,只是拥有一个名字和相应的访问权限,通过mknode()系统调用或者mkfifo()函数来建立的。一旦建
立,任何进程都可以通过文件名将其打开和进行读写,而不局限于父子进程,当然前提是进程对FIFO有适当的访问权。当不再被进程使用时,FIFO在内存中释放,但磁盘节点仍然存在。
图示:
在这里插入图片描述
下面这个例程创建了两个掩藏的命名管道文件(.fifo_chat1和.fifo_chat2)在不同的进程间进行双向通信。该程序需要运行两次(即两个进程),其中进程0(mode=0)从标准输入里读入数据后通过命名管道2(.fifo_chat2)写入数据给进程1(mode=1);而进程1(mode=1)则从标准输入里读入数据后通过命名管道1(.fifo_chat1)写给进程0。

代码示例

 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
 #include <errno.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
 #include <sys/types.h>
 #include <sys/time.h>
 #include <sys/select.h>
 #include <stdlib.h>
 #include <fcntl.h>
 #include <libgen.h>
 
 #define   FIFO1     ".fifo_chat1"
 #define   FIFO2     ".fifo_chat2"
 
 int    stop = 0;
 
 void signal_pipe(int signum)
 {
         if(SIGPIPE == signum)
         {
                 printf("progrom exit and pipe had broken\n");
                 stop = 1;
         }
 }
 
 int main(int argc, char **argv)
 {
         int      fdr;
         int      fdw;
         int      rd;
         fd_set   rdset;
         char     buf[1024];
         int      mode = 0;
 
         if(argc != 2)
         {
                 printf("usage: %s [0/1]\n", basename(argv[0]));
                 printf("this chat program need run twice\n");
                 return -1;
         }
 
         mode = atoi(argv[1]);
 
         if( access(FIFO1, F_OK) )
         {
                 printf("\"%s\" not exist and create it\n", FIFO1);
                 mkfifo(FIFO1, 0666);
         }
         if( access(FIFO2, F_OK) )
         {
                 printf("\"%s\" not exist and create it\n", FIFO2);
                 mkfifo(FIFO2, 0666);
         }
 
         signal(SIGPIPE, signal_pipe);
 
         if( mode == 0)
         {
                 if( ( fdr = open(FIFO1, O_RDONLY) ) < 0)
                 {
                         printf("open '%s' for read failure: %s\n", FIFO1, strerror(errno));
                         return -1;
                 }
 
                 if( ( fdw = open(FIFO2, O_WRONLY)) < 0)
                 {
                         printf("open '%s' for write failure: %s\n", FIFO2, strerror(errno));
                         return -2;
                 }
         }
         else
         {
                 if( ( fdw = open(FIFO1, O_WRONLY)) < 0)
                 {
                         printf("open '%s' for write failure: %s\n", FIFO1, strerror(errno));
                         return -1;
                 }
 
                 if( ( fdr = open(FIFO2, O_RDONLY)) < 0)
                 {
                         printf("open '%s' for read failure: %s\n", FIFO2,strerror(errno));
                         return -2;
                 }
         }
 
         printf("start chating wtih another program now , please input message now: \n");
         while( !stop )
         {
                 FD_ZERO(&rdset);
                 FD_SET(STDIN_FILENO, &rdset);
                 FD_SET(fdr, &rdset);
 
                 rd=select(fdr+1, &rdset, NULL, NULL, NULL);
 
                 if( rd <= 0)
                 {
                         printf("time out and create select() failure: %s\n", strerror(errno));
                         continue;
                 }
 
                 if( FD_ISSET(fdr, &rdset))
                 {
                         memset(buf, 0, sizeof(buf));
                         rd = read(fdr, buf, sizeof(buf));
                         if(rd < 0)
                         {
                                 printf("read data from pipe failure: %s\n", strerror(errno));
                                 break;
                         }
                         else if( 0==rd )
                         {
                                 printf("another side of fifo get closed and program will exit\n");
                                 break;
                         }
                         printf("<-- %s", buf);
                 }
 
                 if(FD_ISSET(STDIN_FILENO, &rdset))
                 {
                         memset(buf, 0, sizeof(buf));
                         fgets(buf, sizeof(buf), stdin);
                         write(fdw, buf, strlen(buf));
                 }
         }
 } 

程序运行:
在这里插入图片描述
管道是一种半双工的通知方式,如果要实现两个进程间的双向通信则需要两个管道,即两个管道分别作为两个进程的读端和写端;
运行如下:
在这里插入图片描述
在这里插入图片描述
通信结束后按Ctrl+C结束:
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值