Linux进程间通信——使用匿名管道
在前面,介绍了一种进程间的通信方式:使用信号,我们创建通知事件,并通过它引起响应,但传递的信息只是一个信号值。这里将介绍另一种进程间通信的方式——匿名管道,通过它进程间可以交换更多有用的数据。
一、什么是管道
如果你使用过Linux的命令,那么对于管道这个名词你一定不会感觉到陌生,因为我们通常通过符号“|"来使用管道,但是管理的真正定义是什么呢?管道是一个进程连接数据流到另一个进程的通道,它通常是用作把一个进程的输出通过管道连接到另一个进程的输入。
举个例子,在shell中输入命令:ls -l | grep string,我们知道ls命令(其实也是一个进程)会把当前目录中的文件都列出来,但是它不会直接输出,而是把本来要输出到屏幕上的数据通过管道输出到grep这个进程中,作为grep这个进程的输入,然后这个进程对输入的信息进行筛选,把存在string的信息的字符串(以行为单位)打印在屏幕上。
二、使用popen函数
1、popen函数和pclose函数介绍
有静就有动,有开就有关,与此相同,与popen函数相对应的函数是pclose函数,它们的原型如下:
#include <stdio.h>
FILE* popen (const char *command, const char *open_mode);
int pclose(FILE *stream_to_close);
poen函数允许一个程序将另一个程序作为新进程来启动,并可以传递数据给它或者通过它接收数据。command是要运行的程序名和相应的参数。open_mode只能是"r(只读)"和"w(只写)"的其中之一。注意,popen函数的返回值是一个FILE类型的指针,而Linux把一切都视为文件,也就是说我们可以使用stdio I/O库中的文件处理函数来对其进行操作。
如果open_mode是"r",主调用程序就可以使用被调用程序的输出,通过函数返回的FILE指针,就可以能过stdio函数(如fread)来读取程序的输出;如果open_mode是"w",主调用程序就可以向被调用程序发送数据,即通过stdio函数(如fwrite)向被调用程序写数据,而被调用程序就可以在自己的标准输入中读取这些数据。
如果type是“w”,则是文件指针是可写的,向这个流 的 写入被转化为 对 command 命令的标准输入;而 command 命令的 标准输出 则是和 调用 popen(), 函数 的 进程 相同,除非 这个被command命令 自己改变. 相反的, 如果type是“r”,则返回的文件指针是可读的,读取 一个 “被popen了的” 流, 就相当于 读取 command 命令的标准输出, 而 command 的标准输入 则是和 调用 popen, 函数的进程 相同.
注意, popen 函数的 输出流默认是被全缓冲的.
pclose 函数 等待 相关的进程结束并返回 一个 command 命令的 退出状态, 就像 wait4 函数 一样
函数popen先执行fork,然后调用exec以执行cmdstring,并且返回一个标准I/O文件指针。如果type是“r”,则文件指针连接到cmdstring的标准输出(见图15-5)。
fp相当于管道的fd[0], stdout相当于管道的fd[1].
图15-5 执行fp = popen(cmdstring, “r”)函数的结果
如果type是“w”,则文件指针连接到cmdstring的标准输入(见图15-6)。
fp相当于管道的fd[1], stdin相当于管道的fd[0].
图15-6 执行fp = popen(cmdstring, “w”)函数的结果
pclose函数用于关闭由popen创建出的关联文件流。pclose只在popen启动的进程结束后才返回,如果调用pclose时被调用进程仍在运行,pclose调用将等待该进程结束。它返回关闭的文件流所在进程的退出码。
2、例子
很多时候,我们根本就不知道输出数据的长度,为了避免定义一个非常大的数组作为缓冲区,我们可以以块的方式来发送数据,一次读取一个块的数据并发送一个块的数据,直到把所有的数据都发送完。下面的例子就是采用这种方式的数据读取和发送方式。源文件名为popen.c,代码如下:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()
{
FILE *read_fp = NULL;
FILE *write_fp = NULL;
char buffer[BUFSIZ + 1];
int chars_read = 0;
//初始化缓冲区
memset(buffer, '