【问题标题】:Implementing pipe in C在 C 中实现管道
【发布时间】:2014-03-21 19:10:31
【问题描述】:

我正在尝试在 C 中实现管道。例如 - $ ls | wc | wc

我已经写了以下代码 -

#include<stdio.h>
#include<stdlib.h>
#include <unistd.h>

void run_cmd(char *cmd, int* fd_in, int* fd_out)
{
    int c = fork();

    if (c==0)
    {
        if (fd_in != NULL)
        {
            close(fd_in[1]);
            dup2(fd_in[0], 0);
        }
        if (fd_out != NULL)
        {
            close(fd_out[0]);
            dup2(fd_out[1],1);
        }
        execlp(cmd, cmd, NULL);
    }
}

int main(int argc, char **argv)
{
    int fd_1[2], fd_2[2], i;
    pipe(fd_1);
    pipe(fd_2);

    run_cmd(argv[1], NULL, fd_1);

    for( i=2; i<argc-1; i++)
    {
        if (i%2 == 0)
            run_cmd(argv[i], fd_1, fd_2);
        else
            run_cmd(argv[i], fd_2, fd_1);
    }
    if (i%2 == 0)
        run_cmd(argv[i], fd_1, NULL);
    else
        run_cmd(argv[i], fd_2, NULL);
}

这适用于两个参数,例如 - $./a.out ls wc

但是当我尝试使用两个以上的参数时它不起作用。

谁能告诉我我的代码有什么问题,或者有其他方法可以做到这一点吗?

【问题讨论】:

  • 创建两个管道是不够的。每两个进程之间都需要一个新管道。
  • @IngoLeonhardt 但在任何时候都只使用了两个管道,一个用于读取,一个用于写入。
  • 我必须承认,如果您恰好有三个进程,我目前看不出问题。拥有四个或更多进程,重用 fd_1 作为进程 argv[3]argv[4] 之间的管道肯定会导致问题

标签: c linux pipe


【解决方案1】:

如果您仍然对为什么您的源不起作用感兴趣(无论如何,Sergey 的解决方案更好):

问题是没有在父进程中关闭fd_1 的写入端。因此,argv[1] 和 parent 都是该管道的作者,这导致了混乱。请不要询问更多细节(尤其是如果您只使用一个管道,为什么不会发生问题)但是如果您在第一次调用 run_cmd() 之后添加一个 close( fd_1[1] );,那么您的原始源将使用树进程运行

【讨论】:

    【解决方案2】:

    这几乎没有错误检查,但为什么这么复杂?

    int main (int argc, char ** argv) {
        int i;
    
        for( i=1; i<argc-1; i++)
        {
            int pd[2];
            pipe(pd);
    
            if (!fork()) {
                dup2(pd[1], 1); // remap output back to parent
                execlp(argv[i], argv[i], NULL);
                perror("exec");
                abort();
            }
    
            // remap output from previous child to input
            dup2(pd[0], 0);
            close(pd[1]);
        }
    
        execlp(argv[i], argv[i], NULL);
        perror("exec");
        abort();
    }
    

    【讨论】:

    • 你为什么不在dup2(pd[0], 0)之后打电话给close(pd[0])
    • @J.F.Sebastian 虽然这是一种很好的做法,但实际上并不需要关闭管道的读取端。没有人会知道那个文件描述符并且会监听它。不过,我必须关闭写端,因为在读端阅读的人将等待EOF。我还写道,这是一个可以工作的最小功能,但实际上没有错误检查。
    • 如果读取管道的孩子死了,是否会阻止生成 SIGPIPE/EPIPE?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-25
    • 2012-01-13
    相关资源
    最近更新 更多