概述
管道是单向的、先进先出的,它把一个进程的输出和另一个进程的输入连接在一起。一个进程(写进程)在管道尾部写入数据,另一个进程(读进程)从管道的头部读出数据。
两个程序之间传递数据的一种简单方法是使用popen和pclose。
#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
popen函数允许一个程序将另一个程序作为新进程来启动,并可以传递数据给它或者通过它接收数据。command字符串是要运行的程序名和相应的参数。type必须是"r"或"w"。
如果type是"r",被调程序的输出就可以被调用程序使用,调用程序利用popen函数返回的FILE *文件流指针,可以读取被调程序的输出;如果type是"w",调用程序就可以向被调程序发送数据,而被调程序可以在自己的标准输入上读取这些数据。
pclose函数只在popen启动的进程结束后才返回。如果调用pclose时它仍在运行,pclose将等待该进程的结束。
案例:
int main()
{
FILE *fp = popen("ps -ef", "r");
if (fp == NULL)
{
perror ("popen");
return -1;
}
char buf[SIZE] = {0};
int ret = fread(buf, sizeof(char), SIZE-1, fp);
// printf ("读到的数据:n %sn", buf);
FILE *fp2 = popen("grep a.out", "w");
if (fp2 == NULL)
{
perror ("popen");
return -1;
}
fwrite (buf, sizeof(char), ret, fp2);
printf ("写入完成n");
pclose (fp);
pclose (fp2);
return 0;
}
从案例可以看出通过popen这个函数可以实现与终端语句ps -ef | grep a.out相同的功能。
管道包括无名管道和有名管道两种,前者用于父进程和子进程间的通信,后者可用于运行于同一系统中的任意两个进程间的通信。
无名管道由pipe()函数创建:
int pipe(int filedis[2]);
当一个管道建立时,它会创建两个文件描述符:
filedis[0]用于读管道,filedis[1]用于写管道。
案例:
int main()
{
int fd[2];
int ret = pipe(fd);
if (ret == -1)
{
perror ("pipe");
return -1;
}
ret = write (fd[1], "hello", 5);
printf ("写入 %d 个字节n", ret);
char ch;
while (1)
{
// 如果管道里面没有数据可读,read会阻塞
ret = read (fd[0], &ch, 1);
if (ret == -1)
{
perror ("read");
break;
}
printf ("读到 %d 字节: %cn", ret, ch);
}
close (fd[0]);
close (fd[1]);
return 0;
}
从案例可以看出通过pipe 这个函数,可以创建一个管道,fd[0]用于读,f[1]用于写。
管道用于不同进程间通信。通常先创建一个管道,在通过fork函数创建一个子进程,该子进程会继承父进程所创建的管道描述符。
案例:
void child_do(int *fd)
{
// 将管道的写端关闭
close (fd[1]);
char buf [SIZE];
while (1)
{
// 从父进程读取数据
int ret = read (fd[0], buf, SIZE-1);
if (ret == -1)
{
perror ("read");
break;
}
buf[ret] = '