在linux中,进程的通信可以通过信号量,互斥量来进行交流,但这些变量只是一个简单的数据结构,而不是一组数据字符串,所以通信过程变得复杂,而且效率不高。
为了使得进程间的通信变得更加方便,便引入了管道(pipe)这一功能。通常把一个进程的输出通过管道链接到另一个进程的输入。
在shell命令中,命令的连接通过管道字符(‘ | ’)来完成的,比如:
cmd1 | cmd2
cmd1的标准输入来自终端键盘;cmd1的标准输出传递给cmd2,作为它的标准输入;cmd2的标准输出连接到终端屏幕。
在程序之间传递数据的常用方法就是使用popen和pclose两个功能函数。它们的定义如下:
#include<stdio.h>
FILE *popen(const char *command,const char *open_mode);
int pclose(FILE *stream_to_close);
popen函数:
它允许一个程序将另一个程序作为新的进程来启动,并可以传递数据给它或者通过它接收数据。command参数是要运行的程序和相应的参数着一组数据的字符串。open_mode必须是“r”或者是"w"。如果是“r”,被调用程序的输出就可以让调用程序使用,调用程序利用popen函数返回的*FILE文件流指针,就可以通过常用的stdio库函数(如fread)来读取被调用程序的输出。如果是“w”,调用程序就可以用fwrite调用向被调用程序发送数据,而被调用程序可以在自己的标准输入上读取这些数据。
pclose函数:
pclose调用只在popen启动的进程结束后才返回u。如果调用pclose时它仍在运行,pclose调用将等待该进程的结束。如果调用进程在调用pclose之前执行了一个wait语句,被调用进程的退出状态就会丢失,因为被调用进程已结束。此时,pclose将返回-1并设置errno为ECHILD。
下面通过2个例子介绍popen和pclose的具体使用。
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#define BUFSIZ 1024
int main()
{
FILE *read_fp;
char buffer[BUFSIZ+1];
int chars_read;
memset(buffer,'\0',sizeof(buffer)); //初始化数组buffer全为空。
read_fp=popen("uname -a","r"); //"read_fp"指向从"uname -a"读出来的信息的首地址。
if(read_fp!=NULL) { //read_fp不为空则说明读到了信息。
chars_read=fread(buffer,sizeof(char),BUFSIZ,read_fp);//fread返回的值为读到的字节数,所以用int类型的变量接收。
if(chars_read>0) {
pritf("output was:-\n%s\n",buffer);
}
pclose(read_fp);
exit(EXIT_SUCCESS);
}
exit(EXIT_FAILURE);
}
该程序运行的结果是现实计算机以及操作系统的相关信息。这段代码展示了调用程序利用popen将被调用程序的输出作为自己的输入。
例子2:
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#define BUFSIZ 1
int main() {
FILE *write_fp;
char buffer[BUFSIZ+1];
sprintf(buffer,”once upon a time,there was...\n”); //在buffer中写入这一字符串。
write_fp=popen(“od -c”,”w”); //write_fp指向“od -c”读取的信息的首地址。
if(write_fp!=NULL) {
fwrite(buffer,sizeof(char),strlen(buffer),write_fp); //将write_fp指向的信息写入buffer当中。
pclose(write_fp);
exit(EXIT_SUCCESS);
}
exit(EXIT_FAILURE);
}
这段代码显示的那串字符存储的地址。调用程序把自己的输出利用popen函数作为了被调用程序的输入。
请求popen调用运行一个程序时,首先要启动shell,即系统中的sh命令。为了省略掉启动shell,节省空间 ,还有对数据的控制,一般使用pipe函数来让两个程序之间传递数据。
pipe函数的原型如下:
#inclde<unistd.h>
int pipe(int file_descriptor[2]);
pipe函数中的参数是有两个整数类型的文件描述符组成的数组的指针。该函数在数组中填入两个新的文件描述符后返回0,如果失败则返回-1并设置errno来表明失败原因。
两个返回的文件描述符以一种特殊的方式连接起来。写到file_descriptor[1]的所有数据都可从file_descriptor[0]中读回来。其中数据是按照先进先出的原则处理的。
通过例子说明pipe的使用:
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#define BUFSIZ 1024
int main() {
int data_processed;
int file_pipes[2]; //定义了pipe函数需要的参数。
const char some_data[]=”123”;
char buffer[BUFSIZ+1];
memset(buffer,’\0’,sizeof(buffer)); //设置buffer为空。
if(pipe(file_pipes)==0) { //pipe函数返回0,则运行正确。
data_processed=write(file_pipes[1],some_data,strlen(some_data); //将some_data中的数据写入file_pipes[1]中。
printf(“wrote %d bytes\n”,data_processed);
data_processed=read(file_pipes[0],buffer,BUFSIZ); //将file_pipes[0]中的数据读入buffer。
printf(“read %d bytes: %s\n”,data_processed,buffer);
exit(EXIT_SUCCESS);
}
exit(EXIT_FAILURE);
}