popen()/pclose()
管道通信的一般流程是:在一个进程中创建管道,通过fork()创建子进程,关闭管道多余端口,使父子进程与管道形成单向通道,进行数据传输。Linux标准I/O库中封装了两个函数——popen()和pclose(),使用这两个函数即可完成管道通信的流程。
popen()和pclose()函数存在于函数库stdio.h中,它们的函数声明分别如下:
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
popen()函数的功能是:调用pipe()函数创建管道,调用fork()函数创建子进程,之后在子进程中通过execve()函数调用shell命令执行相应功能,若整个流程都成功执行,则返回一个I/O文件指针;若pipe()或fork()函数调用失败,或因无法分配内存等原因造成popen()函数调用失败,该函数将会返回NULL。
popen()函数的参数command用来传入要执行的shell命令;type用于指定命令类型(输入w/输出r),因为管道是单向的,所以type参数只能设定为读取或者写入:若type设定为“r”,文件指针连接到command的标准输出,返回的文件指针是可读的;若type设定为“w”,文件指针连接到command的标准输入,返回的文件指针是可写的。父进程与由popen()创建出的进程之间的关系如图1所示。
图1 popen()调用结果
pclose()函数的功能是关闭由popen()打开的I/O流,并通过调用wait()函数等待子进程命令执行结束,返回shell的终止状态,防止产生僵尸进程。与文件操作函数fopen()类似, popen()调用之后务必要使用pclose()函数关闭打开的文件I/O指针,若pclose()函数调用失败,则返回-1。
下面通过案例来展示popen()函数与pclose()函数的使用方法。
案例3:使用popen()函数与pclose()函数实现管道通信。
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 int main()
5 {
6 FILE *r_fp,*w_fp;
7 char buf[100];
8 r_fp=popen("ls","r"); //读取命令执行结果
9 w_fp=popen("wc -l","w"); //将管道中的数据传递给进程
10 while(fgets(buf,sizeof(buf),r_fp)!=NULL)
11 fputs(buf,w_fp);
12 pclose(r_fp);
13 pclose(w_fp);
14 return 0;
15 }
编译案例,执行程序,执行结果如下所示:
101
其中101为兄弟进程对“ls | wc -l”命令的实现结果,在终端输入该命令,得到的结果与程序相同,可知案例成功实现。popen()函数和pclose()函数实现的代码要更加简洁。