首页 > 代码库 > Linux编程 — IPC之管道

Linux编程 — IPC之管道

管道,一种古老的进程间通信形式。一个管道由一个进程创建,然后该进程调用fork,此后父、子进程就可以用管道通信了。

函数原型:
#include <unistd.h>
int pipe(int filedes[2]);    // 成功返回0,出错返回-1


参数filedes返回两个文件描述符。filedes[0]用来输入,filedes[1]用来输出。注意,经过实验,这里的两个描述符并不对应标准输入和标准输出。下面是一个简单的测试例程:
#include <stdio.h>
#include <unistd.h>
 
#define MAXLINE 1024
 
int main(void)
{
    int n;
    int fd[2];
    pid_t pid;
    char buf[MAXLINE];
 
    if (pipe(fd) < 0)
        return -1;
    if ((pid = fork()) < 0)
        return -1;
    else if (pid > 0)
    {
        // 父进程
        printf("parent ID = %d\n", getpid());
        close(fd[0]);
        write(fd[1], "Hello world\n", 12);
    }
    else
    {
        // 子进程
        printf("child ID = %d\n", getpid());
        close(fd[1]);
        n = read(fd[0], buf, MAXLINE);
        write(STDOUT_FILENO, buf, n);
    }
    return 0;
}

运行结果:


上述程序利用管道,实现了数据从父进程传递到子进程。数据的传递方向决定了各进程应该关闭的描述符。

现在来看看如何在shell命令中使用管道:

上图使用了管道命令"|"。前一个命令的输出作为了后一个命令的输入。注意,这里的输出只能是标准输出,输入也只能是标准输入,也就是说前一个命令必须要有输出到标准输出的能力,后一个命令必须要有接受标准输入的能力。下图来自鸟哥的网站。


下面用一个程序来模拟上述shell命令的行为。一种常见的操作时创建一个管道连接到另一个进程,然后读其输出或向其输入发送数据。这是可以使用下列两个库函数:
#include <stdio.h>
FILE *popen(const char *cmdstring, const char *type);    // 成功返回文件指针,出错返回NULL
int pclose(FILE *fp);    // 获得cmdstring的终止状态,出错返回-1


popen等价于fork、exec("cmdstring")启动一个子进程。type的含义如下:
  • "r"表示文件指针连接到cmdstring的标准输出,父进程读cmdstring的数据。
  • "w"表示文件指针连接到cmdstring的标准输入,父进程向cmdstring写数据。
有几个问题需要注意:
  • popen返回的文件指针同样不对应标准输入和标准输出,这可以通过fileno函数把文件指针转换成文件描述符进行查看。
  • 函数pclose不仅关闭标准I/O流,还要获得子进程终止状态。如果不使用这个函数,当父进程还在运行而子进程运行完毕后会产生僵死进程,等下会通过实验来说明。
测试例程如下:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
 
#define MAXLINE 1024
 
int main(void)
{
    char buf[MAXLINE];
    FILE *fpin;
    int n;
 
    if ((fpin = popen("date", "r")) == NULL)  // 启动data命令
        return -1;
 
    fgets(buf, MAXLINE, fpin);                    // 获得data命令的输出
    write(STDOUT_FILENO, buf, strlen(buf));       // 将数据打印到终端
    pclose(fpin);
     
    return 0;
}

运行结果:


父进程test正确地接收了data命令的输出,管道传输成功。如果去掉pclose(fpin);这条语句,那么结果如下:


啊哦,出现一个僵死进程。这就是因为父进程没有获得子进程的终止状态而引起的。

参考:
《unix环境高级编程》 P397-P404.