进程间的通信:管道

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/YL970302/article/details/83754434

我们知道在两个进程间发送消息的非常简单的方法:使用信号,我们创建通知事件,通过它引起响应,但传送的信息只限于一个信号值,所以在此我们将介绍管道,通过它进程之间可以交换更有用的数据。

一、什么是管道:通常是把一个进程得输出通过管道连接到另一个进程得输入。对于shell命令来说,命令的连接是通过管道字符来完成的,如下:

cmd1 | cmd2   shell负责安排两个命令的标准输入和标准输出

  • cmd1的标准输入来自终端键盘
  • cmd1的标准输出传递给cmd2,作为它的标准输入
  • cmd2的标准输出连接到终端屏幕上

                               

二、进程管道

最简单的在两个程序之间传递数据的方法是使用popen和pclose函数

头文件 #include<stdio.h>

FILE *popen(const char *command, const char *open_mode);

int pclose(FILE *stream_to_close);

1、popen函数:

(1)允许一个程序将另一个程序作为新进程来启动,并可以传递数据给它或者通过它接收数据。

(2)command字符串是要运行的程序名和相应的参数

(3)open_mode必须是“r” 或者“w”,这意味着我们不能调用另一个程序并同时对它进行读写操作。popen函数在失败是返回一个空指针,如果想通过管道实现双向通信,最普通的结局方法就是使用两个管道,每个管道负责一个方向的数据流

  • 如果是“r”,被调用程序的输出就可以被调用者程序使用,调用程序利用popen函数返回FILE *文件流指针,就可以通过常用的stdio库函数(如fread)来读取调用程序的输出。
  • 如果是“w” 调用程序就可以用fwrite调用向被调用程序发送数据,而被调用程序可以在自己的标准输入上读取这些数据。被调用的程序通常不会意识到自己正在从另一个进程读取数据,它只是在标准输入流上读取数据,然后做出相应的操作

2、pclose函数:
(1)用popen启动的进程结束时,我们可以用pclose函数关闭与之关联的文件流。pclose调用只在popen启动的进程结束后才返回,如果调用pclose时它仍在运行,pclose调用将等待该进程的结束

(2)pclose调用的返回值通常时它所关闭的文件流所在进程的退出码。如果调用进程在调用pclose之前执行一个wait语句,被调用进程的退出状态就会丢失,因为被调用进程已结束。此时,pclose将返回-1,并设置errno为ECHILD。

  • 现在我们写一个简单的读取外部程序的输出的代码:
//捕获uname命令的输出 
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#define BUFSIZE 128
 
  int main()
  {
      FILE *read_fp;   //设置文件流指针
      char buffer[BUFSIZE];
      int chars_read;
      memset(buffer,'\0',sizeof(buffer));  //全部清零
  
      read_fp = popen("uname -a","r");
      if(read_fp != NULL)
      {
          chars_read = fread(buffer,sizeof(char),BUFSIZE,read_fp);
          if(chars_read > 0)
          {
              printf("Outpup was:-\n%s",buffer);
          }
          pclose(read_fp);
          exit(EXIT_SUCCESS);
      }
      exit(EXIT_FAILURE);
  }

结果显示:

uname -a 的作用是打印系统信息,包括计算机型号、操作系统名称、版本和发行号,以及计算机的网络名。

  • 将输出送往popen

代码实现:

  //将输出送往外部程序
  #include<stdio.h>
  #include<string.h>
  #include<unistd.h>
  #include<stdlib.h>
  #define BUFSIZE 128
  int main()
  {
      FILE *write_fp;
      char buffer[BUFSIZE];
  
      sprintf(buffer, "Once upon a time, there was ...\n");
      write_fp = popen("od -c","w");
      if(write_fp != NULL)
      {
          fwrite(buffer,sizeof(char),strlen(buffer),write_fp);
          pclose(write_fp);
          exit(EXIT_SUCCESS);
      }
      exit(EXIT_FAILURE);
  }      

结果展示:

程序使用带参数“w”的popen启动od -c命令,这样就可以向该命令发送数据了,然后它给od -c命令发送一个字符串,该命令接收并处理它,最后把处理结果打印到自己的标准输出上。

3、pipe调用

前面所讲的popen函数属于高级的函数,现在讲述底层的pipe函数,通过pipe函数在两个程序之间传递数据不需要启动一个shell来解释请求的命令,它同时还提供了对读写数据的更多控制。

  • 头文件 <unistd.h>
  • int pipe(int file_descriptor[2]);  //参数是一个由两个整数类型的文件描述符组成的数组的指针。该函数在数组中填上两个新的文件描述符后返回0,如果失败则返回-1并设置errno来表明失败的原因。
  • EMFILE:进程使用的文件描述符过多
  • ENFILE:系统的文件表已满
  • EFAULT:文件描述符无效

两个返回的文件描述符以一种特殊的方式连接起来,写到file_descriptor[1]的所有数据都可以从file_descriptor[0]读回来,数据基于先进先出的原则进行处理,这意味着如果你把字节1,2,3写到file_descriptor[1],从file_descriptor[0]读取到的数据也会是1,2,3。

特别注意的是:这里使用的是文件描述符而不是文件流,所以我们必须用底层的read和write调用来访问数据,而不是文件流库函数fread和fwrite。

:file_pipe数组的用法,它的地址被当作参数传递给pipe函数

代码实现:

 #include<stdio.h>
 #include<string.h>
 #include<stdlib.h>
 #include<unistd.h>
 #define BUFSIZE 128
 
 int main()
 {
     int data_processed;
     int file_pipes[2];
     const char some_data[] = "123";
     char buffer[BUFSIZE];
     memset(buffer,'\0',sizeof(buffer));
 
     if(pipe(file_pipes) == 0)
     {
         data_processed = write(file_pipes[1],some_data,strlen(some_data));
         printf("Wrote %d bytes\n",data_processed);
         data_processed = read(file_pipes[0], buffer,BUFSIZE);
         printf("Read %d bytes :%s\n",data_processed,buffer);
         exit(EXIT_SUCCESS);
     }
         exit(EXIT_SUCCESS);
 }

结果展示:

这个程序用数组file_pipes[]中的两个文件描述符创建一个管道,然后它用文件描述符file_pipes[1]向管道中写数据,再从file_pipes[0]读回数据,注:管道有一些内置的缓存区,它在write和read调用之间保存数据。

猜你喜欢

转载自blog.csdn.net/YL970302/article/details/83754434
今日推荐